001/*
002 * The contents of this file are subject to the terms of the Common Development and
003 * Distribution License (the License). You may not use this file except in compliance with the
004 * License.
005 *
006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
007 * specific language governing permission and limitations under the License.
008 *
009 * When distributing Covered Software, include this CDDL Header Notice in each file and include
010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
011 * Header, with the fields enclosed by brackets [] replaced by your own identifying
012 * information: "Portions copyright [year] [name of copyright owner]".
013 *
014 * Copyright 2013-2016 ForgeRock AS.
015 */
016
017package org.forgerock.json.jose.jwe;
018
019import java.security.Key;
020
021import org.forgerock.json.jose.jwe.handlers.compression.CompressionHandler;
022import org.forgerock.json.jose.jwe.handlers.encryption.EncryptionHandler;
023import org.forgerock.json.jose.jws.SignedJwt;
024import org.forgerock.json.jose.jwt.Jwt;
025import org.forgerock.json.jose.jwt.JwtClaimsSet;
026import org.forgerock.json.jose.jwt.JwtHeader;
027import org.forgerock.json.jose.jwt.Payload;
028import org.forgerock.json.jose.utils.Utils;
029import org.forgerock.util.encode.Base64url;
030
031/**
032 * A JWE implementation of the <tt>Jwt</tt> interface.
033 * <p>
034 * JSON Web Encryption (JWE) is a representing encrypted content using JSON based data structures.
035 * <p>
036 * @see <a href="http://tools.ietf.org/html/draft-ietf-jose-json-web-encryption-11">
037 *     JSON Web Encryption Specification</a>
038 *
039 * @since 2.0.0
040 */
041public class EncryptedJwt implements Jwt, Payload {
042
043    private final EncryptionManager encryptionManager = new EncryptionManager();
044    private final CompressionManager compressionManager = new CompressionManager();
045
046    private final JweHeader header;
047
048    private Payload payload;
049    private final Key publicKey;
050
051    private final String encodedHeader;
052    private final byte[] encryptedContentEncryptionKey;
053    private final byte[] initialisationVector;
054    private final byte[] ciphertext;
055    private final byte[] authenticationTag;
056
057    /**
058     * Constructs a fresh, new EncryptedJwt from the given JweHeader and JwtClaimsSet.
059     * <p>
060     * The specified public key will be used to perform the encryption of the JWT.
061     *
062     * @param header The JweHeader containing the header parameters of the JWE.
063     * @param payload The claimset of the JWE.
064     * @param publicKey The public key to use to perform the encryption.
065     */
066    public EncryptedJwt(JweHeader header, JwtClaimsSet payload, Key publicKey) {
067        this(header, (Payload) payload, publicKey);
068    }
069
070    EncryptedJwt(JweHeader header, Payload payload, Key encryptionKey) {
071        this.header = header;
072        this.payload = payload;
073        this.publicKey = encryptionKey;
074
075        this.encodedHeader = null;
076        this.encryptedContentEncryptionKey = null;
077        this.initialisationVector = null;
078        this.ciphertext = null;
079        this.authenticationTag = null;
080    }
081
082    /**
083     * Constructs a reconstructed EncryptedJwt from its constituent parts, the JweHeader, encrypted Content Encryption
084     * Key (CEK), initialisation vector, ciphertext and additional authentication data.
085     * <p>
086     * For use when an encrypted JWT has been reconstructed from its base64url encoded string representation and the
087     * JWT needs decrypting.
088     *
089     * @param header The JweHeader containing the header parameters of the JWE.
090     * @param encodedHeader The Base64url encoded JWE header.
091     * @param encryptedContentEncryptionKey The encrypted Content Encryption Key (CEK).
092     * @param initialisationVector The initialisation vector.
093     * @param ciphertext The ciphertext.
094     * @param authenticationTag The authentication tag.
095     */
096    public EncryptedJwt(JweHeader header, String encodedHeader, byte[] encryptedContentEncryptionKey,
097            byte[] initialisationVector, byte[] ciphertext, byte[] authenticationTag) {
098        this.header = header;
099        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}