View Javadoc
1   /*
2    * The contents of this file are subject to the terms of the Common Development and
3    * Distribution License (the License). You may not use this file except in compliance with the
4    * License.
5    *
6    * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
7    * specific language governing permission and limitations under the License.
8    *
9    * When distributing Covered Software, include this CDDL Header Notice in each file and include
10   * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
11   * Header, with the fields enclosed by brackets [] replaced by your own identifying
12   * information: "Portions copyright [year] [name of copyright owner]".
13   *
14   * Copyright 2013-2016 ForgeRock AS.
15   */
16  
17  package org.forgerock.json.jose.jwe;
18  
19  import java.security.Key;
20  
21  import org.forgerock.json.jose.jwe.handlers.compression.CompressionHandler;
22  import org.forgerock.json.jose.jwe.handlers.encryption.EncryptionHandler;
23  import org.forgerock.json.jose.jws.SignedJwt;
24  import org.forgerock.json.jose.jwt.Jwt;
25  import org.forgerock.json.jose.jwt.JwtClaimsSet;
26  import org.forgerock.json.jose.jwt.JwtHeader;
27  import org.forgerock.json.jose.jwt.Payload;
28  import org.forgerock.json.jose.utils.Utils;
29  import org.forgerock.util.encode.Base64url;
30  
31  /**
32   * A JWE implementation of the <tt>Jwt</tt> interface.
33   * <p>
34   * JSON Web Encryption (JWE) is a representing encrypted content using JSON based data structures.
35   * <p>
36   * @see <a href="http://tools.ietf.org/html/draft-ietf-jose-json-web-encryption-11">
37   *     JSON Web Encryption Specification</a>
38   *
39   * @since 2.0.0
40   */
41  public class EncryptedJwt implements Jwt, Payload {
42  
43      private final EncryptionManager encryptionManager = new EncryptionManager();
44      private final CompressionManager compressionManager = new CompressionManager();
45  
46      private final JweHeader header;
47  
48      private Payload payload;
49      private final Key publicKey;
50  
51      private final String encodedHeader;
52      private final byte[] encryptedContentEncryptionKey;
53      private final byte[] initialisationVector;
54      private final byte[] ciphertext;
55      private final byte[] authenticationTag;
56  
57      /**
58       * Constructs a fresh, new EncryptedJwt from the given JweHeader and JwtClaimsSet.
59       * <p>
60       * The specified public key will be used to perform the encryption of the JWT.
61       *
62       * @param header The JweHeader containing the header parameters of the JWE.
63       * @param payload The claimset of the JWE.
64       * @param publicKey The public key to use to perform the encryption.
65       */
66      public EncryptedJwt(JweHeader header, JwtClaimsSet payload, Key publicKey) {
67          this(header, (Payload) payload, publicKey);
68      }
69  
70      EncryptedJwt(JweHeader header, Payload payload, Key encryptionKey) {
71          this.header = header;
72          this.payload = payload;
73          this.publicKey = encryptionKey;
74  
75          this.encodedHeader = null;
76          this.encryptedContentEncryptionKey = null;
77          this.initialisationVector = null;
78          this.ciphertext = null;
79          this.authenticationTag = null;
80      }
81  
82      /**
83       * Constructs a reconstructed EncryptedJwt from its constituent parts, the JweHeader, encrypted Content Encryption
84       * Key (CEK), initialisation vector, ciphertext and additional authentication data.
85       * <p>
86       * For use when an encrypted JWT has been reconstructed from its base64url encoded string representation and the
87       * JWT needs decrypting.
88       *
89       * @param header The JweHeader containing the header parameters of the JWE.
90       * @param encodedHeader The Base64url encoded JWE header.
91       * @param encryptedContentEncryptionKey The encrypted Content Encryption Key (CEK).
92       * @param initialisationVector The initialisation vector.
93       * @param ciphertext The ciphertext.
94       * @param authenticationTag The authentication tag.
95       */
96      public EncryptedJwt(JweHeader header, String encodedHeader, byte[] encryptedContentEncryptionKey,
97              byte[] initialisationVector, byte[] ciphertext, byte[] authenticationTag) {
98          this.header = header;
99          this.encodedHeader = encodedHeader;
100         this.encryptedContentEncryptionKey = encryptedContentEncryptionKey;
101         this.initialisationVector = initialisationVector;
102         this.ciphertext = ciphertext;
103         this.authenticationTag = authenticationTag;
104 
105         this.publicKey = null;
106     }
107 
108     @Override
109     public JwtHeader getHeader() {
110         return header;
111     }
112 
113     @Override
114     public JwtClaimsSet getClaimsSet() {
115         return (JwtClaimsSet) payload;
116     }
117 
118     /**
119      * The payload of the encrypted JWT. This is either the claims set or a nested signed JWT.
120      *
121      * @return the payload of the encrypted JWT.
122      */
123     Payload getPayload() {
124         return payload;
125     }
126 
127     @Override
128     public String build() {
129 
130         EncryptionHandler encryptionHandler = encryptionManager.getEncryptionHandler(header);
131 
132         Key contentEncryptionKey = encryptionHandler.getContentEncryptionKey();
133         if (contentEncryptionKey == null) {
134             contentEncryptionKey = publicKey;
135         }
136         byte[] encryptedContentEncryptionKey = encryptionHandler.generateJWEEncryptedKey(publicKey,
137                 contentEncryptionKey);
138         String encodedEncryptedKey = Base64url.encode(encryptedContentEncryptionKey);
139 
140 
141         byte[] initialisationVector = encryptionHandler.generateInitialisationVector();
142         String encodedInitialisationVector = Base64url.encode(initialisationVector);
143 
144 
145         String jweHeader = header.build();
146         String encodedJweHeader = Utils.base64urlEncode(jweHeader);
147         byte[] plaintext = compressPlaintext(header.getCompressionAlgorithm(),
148                 payload.build().getBytes(Utils.CHARSET));
149         byte[] additionalAuthenticatedData = encodedJweHeader.getBytes(Utils.CHARSET);
150         JweEncryption cipherTextAndAuthTag = encryptionHandler.encryptPlaintext(contentEncryptionKey,
151                 initialisationVector, plaintext, additionalAuthenticatedData);
152 
153         String encodedCiphertext = Base64url.encode(cipherTextAndAuthTag.getCiphertext());
154         String encodedAuthenticationTag = Base64url.encode(cipherTextAndAuthTag.getAuthenticationTag());
155 
156 
157         return new StringBuilder(encodedJweHeader)
158                 .append(".").append(encodedEncryptedKey)
159                 .append(".").append(encodedInitialisationVector)
160                 .append(".").append(encodedCiphertext)
161                 .append(".").append(encodedAuthenticationTag)
162                 .toString();
163     }
164 
165     /**
166      * Performs the compression of the plaintext, if required.
167      * <p>
168      * Whether or not compression is applied is based from the CompressionAlgorithm specified.
169      *
170      * @param compressionAlgorithm The CompressionAlgorithm describing the algorithm to use to compress the plaintext.
171      * @param plaintext The plaintext.
172      * @return A byte array of the (compressed) plaintext.
173      */
174     private byte[] compressPlaintext(CompressionAlgorithm compressionAlgorithm, byte[] plaintext) {
175         CompressionHandler compressionHandler = compressionManager.getCompressionHandler(compressionAlgorithm);
176         return compressionHandler.compress(plaintext);
177     }
178 
179     /**
180      * Decrypts the JWE ciphertext back into a JwtClaimsSet.
181      * <p>
182      * The same private key must be given here that is the pair to the public key that was used to encrypt the JWT.
183      *
184      * @param privateKey The private key pair to the public key that encrypted the JWT.
185      */
186     public void decrypt(Key privateKey) {
187 
188         EncryptionHandler encryptionHandler = encryptionManager.getEncryptionHandler(header);
189 
190         Key contentEncryptionKey = encryptionHandler.decryptContentEncryptionKey(privateKey,
191                 encryptedContentEncryptionKey);
192 
193         byte[] additionalAuthenticatedData = encodedHeader.getBytes(Utils.CHARSET);
194 
195         byte[] plaintext = encryptionHandler.decryptCiphertext(contentEncryptionKey, initialisationVector, ciphertext,
196                 authenticationTag, additionalAuthenticatedData);
197         plaintext = decompressPlaintext(header.getCompressionAlgorithm(), plaintext);
198 
199         String decryptedPayload = new String(plaintext, Utils.CHARSET);
200 
201         payload = decodePayload(decryptedPayload);
202     }
203 
204     /**
205      * Performs decompression of the given plaintext if required. Whether or not decompression is actually applied
206      * depends on the {@link CompressionAlgorithm}.
207      *
208      * @param compressionAlgorithm the compression algorithm.
209      * @param plaintext the plaintext to decompress.
210      * @return the decompressed plaintext.
211      */
212     private byte[] decompressPlaintext(CompressionAlgorithm compressionAlgorithm, byte[] plaintext) {
213         CompressionHandler compressionHandler = compressionManager.getCompressionHandler(compressionAlgorithm);
214         return compressionHandler.decompress(plaintext);
215     }
216 
217     /**
218      * Decodes the decrypted payload of this JWT.
219      *
220      * @param decryptedPayload the decrypted payload.
221      * @return the decoded payload as either a {@link JwtClaimsSet} or nested {@link SignedJwt}.
222      */
223     Payload decodePayload(String decryptedPayload) {
224         return new JwtClaimsSet(Utils.parseJson(decryptedPayload));
225     }
226 }