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-2015 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.jwt.Jwt;
024import org.forgerock.json.jose.jwt.JwtClaimsSet;
025import org.forgerock.json.jose.jwt.JwtHeader;
026import org.forgerock.json.jose.jwt.Payload;
027import org.forgerock.json.jose.utils.Utils;
028import org.forgerock.util.encode.Base64url;
029
030/**
031 * A JWE implementation of the <tt>Jwt</tt> interface.
032 * <p>
033 * JSON Web Encryption (JWE) is a representing encrypted content using JSON based data structures.
034 * <p>
035 * @see <a href="http://tools.ietf.org/html/draft-ietf-jose-json-web-encryption-11">
036 *     JSON Web Encryption Specification</a>
037 *
038 * @since 2.0.0
039 */
040public class EncryptedJwt implements Jwt, Payload {
041
042    private final EncryptionManager encryptionManager = new EncryptionManager();
043    private final CompressionManager compressionManager = new CompressionManager();
044
045    private final JweHeader header;
046
047    private JwtClaimsSet claimsSet;
048    private final Key publicKey;
049
050    private final String encodedHeader;
051    private final byte[] encryptedContentEncryptionKey;
052    private final byte[] initialisationVector;
053    private final byte[] ciphertext;
054    private final byte[] authenticationTag;
055
056    /**
057     * Constructs a fresh, new EncryptedJwt from the given JweHeader and JwtClaimsSet.
058     * <p>
059     * The specified public key will be used to perform the encryption of the JWT.
060     *
061     * @param header The JweHeader containing the header parameters of the JWE.
062     * @param claimsSet The JwtClaimsSet containing the claims of the JWE.
063     * @param publicKey The public key to use to perform the encryption.
064     */
065    public EncryptedJwt(JweHeader header, JwtClaimsSet claimsSet, Key publicKey) {
066        this.header = header;
067        this.claimsSet = claimsSet;
068        this.publicKey = publicKey;
069
070        this.encodedHeader = null;
071        this.encryptedContentEncryptionKey = null;
072        this.initialisationVector = null;
073        this.ciphertext = null;
074        this.authenticationTag = null;
075    }
076
077    /**
078     * Constructs a reconstructed EncryptedJwt from its constituent parts, the JweHeader, encrypted Content Encryption
079     * Key (CEK), initialisation vector, ciphertext and additional authentication data.
080     * <p>
081     * For use when an encrypted JWT has been reconstructed from its base64url encoded string representation and the
082     * JWT needs decrypting.
083     *
084     * @param header The JweHeader containing the header parameters of the JWE.
085     * @param encodedHeader The Base64url encoded JWE header.
086     * @param encryptedContentEncryptionKey The encrypted Content Encryption Key (CEK).
087     * @param initialisationVector The initialisation vector.
088     * @param ciphertext The ciphertext.
089     * @param authenticationTag The authentication tag.
090     */
091    public EncryptedJwt(JweHeader header, String encodedHeader, byte[] encryptedContentEncryptionKey,
092            byte[] initialisationVector, byte[] ciphertext, byte[] authenticationTag) {
093        this.header = header;
094        this.encodedHeader = encodedHeader;
095        this.encryptedContentEncryptionKey = encryptedContentEncryptionKey;
096        this.initialisationVector = initialisationVector;
097        this.ciphertext = ciphertext;
098        this.authenticationTag = authenticationTag;
099
100        this.publicKey = null;
101    }
102
103    /**
104     * {@inheritDoc}
105     */
106    @Override
107    public JwtHeader getHeader() {
108        return header;
109    }
110
111    /**
112     * {@inheritDoc}
113     */
114    @Override
115    public JwtClaimsSet getClaimsSet() {
116        return claimsSet;
117    }
118
119    /**
120     * {@inheritDoc}
121     */
122    @Override
123    public String build() {
124
125        EncryptionHandler encryptionHandler = encryptionManager.getEncryptionHandler(header);
126
127        Key contentEncryptionKey = encryptionHandler.getContentEncryptionKey();
128        byte[] encryptedContentEncryptionKey = encryptionHandler.generateJWEEncryptedKey(publicKey,
129                contentEncryptionKey);
130        String encodedEncryptedKey = Base64url.encode(encryptedContentEncryptionKey);
131
132
133        byte[] initialisationVector = encryptionHandler.generateInitialisationVector();
134        String encodedInitialisationVector = Base64url.encode(initialisationVector);
135
136
137        String jweHeader = header.build();
138        String encodedJweHeader = Utils.base64urlEncode(jweHeader);
139        byte[] plaintext = compressPlaintext(header.getCompressionAlgorithm(),
140                claimsSet.build().getBytes(Utils.CHARSET));
141        byte[] additionalAuthenticatedData = encodedJweHeader.getBytes(Utils.CHARSET);
142        JweEncryption cipherTextAndAuthTag = encryptionHandler.encryptPlaintext(contentEncryptionKey,
143                initialisationVector, plaintext, additionalAuthenticatedData);
144
145        String encodedCiphertext = Base64url.encode(cipherTextAndAuthTag.getCiphertext());
146        String encodedAuthenticationTag = Base64url.encode(cipherTextAndAuthTag.getAuthenticationTag());
147
148
149        return new StringBuilder(encodedJweHeader)
150                .append(".").append(encodedEncryptedKey)
151                .append(".").append(encodedInitialisationVector)
152                .append(".").append(encodedCiphertext)
153                .append(".").append(encodedAuthenticationTag)
154                .toString();
155    }
156
157    /**
158     * Performs the compression of the plaintext, if required.
159     * <p>
160     * Whether or not compression is applied is based from the CompressionAlgorithm specified.
161     *
162     * @param compressionAlgorithm The CompressionAlgorithm describing the algorithm to use to compress the plaintext.
163     * @param plaintext The plaintext.
164     * @return A byte array of the (compressed) plaintext.
165     */
166    private byte[] compressPlaintext(CompressionAlgorithm compressionAlgorithm, byte[] plaintext) {
167        CompressionHandler compressionHandler = compressionManager.getCompressionHandler(compressionAlgorithm);
168        return compressionHandler.compress(plaintext);
169    }
170
171    /**
172     * Decrypts the JWE ciphertext back into a JwtClaimsSet.
173     * <p>
174     * The same private key must be given here that is the pair to the public key that was used to encrypt the JWT.
175     *
176     * @param privateKey The private key pair to the public key that encrypted the JWT.
177     */
178    public void decrypt(Key privateKey) {
179
180        EncryptionHandler encryptionHandler = encryptionManager.getEncryptionHandler(header);
181
182        Key contentEncryptionKey = encryptionHandler.decryptContentEncryptionKey(privateKey,
183                encryptedContentEncryptionKey);
184
185        byte[] additionalAuthenticatedData = encodedHeader.getBytes(Utils.CHARSET);
186
187        byte[] plaintext = encryptionHandler.decryptCiphertext(contentEncryptionKey, initialisationVector, ciphertext,
188                authenticationTag, additionalAuthenticatedData);
189
190        String claimsSetString = new String(plaintext, Utils.CHARSET);
191
192        claimsSet = new JwtClaimsSet(Utils.parseJson(claimsSetString));
193    }
194}