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}