001package com.nimbusds.jose.crypto; 002 003 004import java.util.HashSet; 005import java.util.Set; 006 007import javax.crypto.SecretKey; 008import javax.crypto.spec.SecretKeySpec; 009 010import net.jcip.annotations.ThreadSafe; 011 012import com.nimbusds.jose.EncryptionMethod; 013import com.nimbusds.jose.JOSEException; 014import com.nimbusds.jose.JWEAlgorithm; 015import com.nimbusds.jose.JWEDecrypter; 016import com.nimbusds.jose.JWEHeader; 017import com.nimbusds.jose.util.Base64URL; 018import com.nimbusds.jose.util.StringUtils; 019 020 021/** 022 * AES decrypter of {@link com.nimbusds.jose.JWEObject JWE objects}. This class 023 * is thread-safe. 024 * 025 * <p>Supports the following JWE algorithms: 026 * 027 * <ul> 028 * <li>{@link com.nimbusds.jose.JWEAlgorithm#A128KW} 029 * <li>{@link com.nimbusds.jose.JWEAlgorithm#A192KW} 030 * <li>{@link com.nimbusds.jose.JWEAlgorithm#A256KW} 031 * <li>{@link com.nimbusds.jose.JWEAlgorithm#A128GCMKW} 032 * <li>{@link com.nimbusds.jose.JWEAlgorithm#A192GCMKW} 033 * <li>{@link com.nimbusds.jose.JWEAlgorithm#A256GCMKW} 034 * </ul> 035 * 036 * <p>Supports the following encryption methods: 037 * 038 * <ul> 039 * <li>{@link com.nimbusds.jose.EncryptionMethod#A128CBC_HS256} 040 * <li>{@link com.nimbusds.jose.EncryptionMethod#A192CBC_HS384} 041 * <li>{@link com.nimbusds.jose.EncryptionMethod#A256CBC_HS512} 042 * <li>{@link com.nimbusds.jose.EncryptionMethod#A128GCM} 043 * <li>{@link com.nimbusds.jose.EncryptionMethod#A192GCM} 044 * <li>{@link com.nimbusds.jose.EncryptionMethod#A256GCM} 045 * <li>{@link com.nimbusds.jose.EncryptionMethod#A128CBC_HS256_DEPRECATED} 046 * <li>{@link com.nimbusds.jose.EncryptionMethod#A256CBC_HS512_DEPRECATED} 047 * </ul> 048 * 049 * <p>Accepts all {@link com.nimbusds.jose.JWEHeader#getRegisteredParameterNames 050 * registered JWE header parameters}. Use {@link #setAcceptedAlgorithms} and 051 * {@link #setAcceptedEncryptionMethods} to restrict the acceptable JWE 052 * algorithms and encryption methods. 053 * 054 * @author Melisa Halsband 055 * @version $version$ (2014-08-20) 056 */ 057@ThreadSafe 058public class AESDecrypter extends AESCryptoProvider implements JWEDecrypter { 059 060 061 /** 062 * The accepted JWE algorithms. 063 */ 064 private Set<JWEAlgorithm> acceptedAlgs; 065 066 067 /** 068 * The accepted encryption methods. 069 */ 070 private Set<EncryptionMethod> acceptedEncs = 071 new HashSet<>(supportedEncryptionMethods()); 072 073 074 /** 075 * The critical header parameter checker. 076 */ 077 private final CriticalHeaderParameterChecker critParamChecker = 078 new CriticalHeaderParameterChecker(); 079 080 081 /** 082 * The key encrypting key. 083 */ 084 private final SecretKey kek; 085 086 087 /** 088 * Creates a new AES decrypter. 089 * 090 * @param kek The Key Encrypting Key. Must be 128 bits (16 bytes), 192 091 * bits (24 bytes) or 256 bits (32 bytes). Must not be 092 * {@code null}. 093 * 094 * @throws IllegalArgumentException If called with a null parameter or 095 * unsupported key length 096 */ 097 public AESDecrypter(final SecretKey kek) { 098 099 if (kek == null) { 100 101 throw new IllegalArgumentException("The Key Encrypting Key must not be null"); 102 } 103 104 this.kek = kek; 105 106 acceptedAlgs = compatibleAlgorithms(); 107 108 if (acceptedAlgs == null){ 109 throw new IllegalArgumentException("The Key Encrypting Key must be 128, 192 or 256 bits long"); 110 } 111 } 112 113 114 /** 115 * Creates a new AES decrypter. 116 * 117 * @param keyBytes The Key Encrypting Key, as a byte array. Must be 128 118 * bits (16 bytes), 192 bits (24 bytes) or 256 bits (32 119 * bytes). Must not be {@code null}. 120 * 121 * @throws IllegalArgumentException If called with a null parameter or 122 * unsupported key length 123 */ 124 public AESDecrypter(final byte[] keyBytes) 125 throws IllegalArgumentException { 126 127 this(new SecretKeySpec(keyBytes, "AES")); 128 } 129 130 131 /** 132 * Returns the JWK algorithms compatible with the key size. 133 * 134 * @return The set of compatible algorithms. 135 */ 136 public Set<JWEAlgorithm> compatibleAlgorithms() { 137 138 return COMPATIBLE_ALGORITHMS.get(kek.getEncoded().length); 139 } 140 141 142 /** 143 * Gets the Key Encrypting Key. 144 * 145 * @return The Key Encrypting Key. 146 */ 147 public SecretKey getKey() { 148 149 return kek; 150 } 151 152 153 @Override 154 public Set<JWEAlgorithm> getAcceptedAlgorithms() { 155 156 return acceptedAlgs; 157 } 158 159 160 @Override 161 public void setAcceptedAlgorithms(final Set<JWEAlgorithm> acceptedAlgs) { 162 163 if (acceptedAlgs == null) { 164 throw new IllegalArgumentException("The accepted JWE algorithms must not be null"); 165 } 166 167 if (!supportedAlgorithms().containsAll(acceptedAlgs)) { 168 throw new IllegalArgumentException("Unsupported JWE algorithm(s)"); 169 } 170 171 if (!compatibleAlgorithms().containsAll(acceptedAlgs)) { 172 throw new IllegalArgumentException("JWE algorithm(s) not compatible with key size"); 173 } 174 175 this.acceptedAlgs = acceptedAlgs; 176 } 177 178 179 @Override 180 public Set<EncryptionMethod> getAcceptedEncryptionMethods() { 181 182 return acceptedEncs; 183 } 184 185 186 @Override 187 public void setAcceptedEncryptionMethods(final Set<EncryptionMethod> acceptedEncs) { 188 189 if (acceptedEncs == null) 190 throw new IllegalArgumentException("The accepted encryption methods must not be null"); 191 192 if (!supportedEncryptionMethods().containsAll(acceptedEncs)) { 193 throw new IllegalArgumentException("Unsupported encryption method(s)"); 194 } 195 196 this.acceptedEncs = acceptedEncs; 197 } 198 199 200 @Override 201 public Set<String> getIgnoredCriticalHeaderParameters() { 202 203 return critParamChecker.getIgnoredCriticalHeaders(); 204 } 205 206 207 @Override 208 public void setIgnoredCriticalHeaderParameters(final Set<String> headers) { 209 210 critParamChecker.setIgnoredCriticalHeaders(headers); 211 } 212 213 214 @Override 215 public byte[] decrypt(final JWEHeader header, 216 final Base64URL encryptedKey, 217 final Base64URL iv, 218 final Base64URL cipherText, 219 final Base64URL authTag) 220 throws JOSEException { 221 222 // Validate required JWE parts 223 if (encryptedKey == null) { 224 225 throw new JOSEException("The encrypted key must not be null"); 226 } 227 228 if (iv == null) { 229 230 throw new JOSEException("The initialization vector (IV) must not be null"); 231 } 232 233 if (authTag == null) { 234 235 throw new JOSEException("The authentication tag must not be null"); 236 } 237 238 if (!critParamChecker.headerPasses(header)) { 239 240 throw new JOSEException("Unsupported critical header parameter"); 241 } 242 243 244 // Derive the content encryption key 245 JWEAlgorithm alg = header.getAlgorithm(); 246 int keyLength = header.getEncryptionMethod().cekBitLength(); 247 248 SecretKey cek; 249 250 if (alg.equals(JWEAlgorithm.A128KW) || 251 alg.equals(JWEAlgorithm.A192KW) || 252 alg.equals(JWEAlgorithm.A256KW)) { 253 254 cek = AESKW.decryptCEK(kek, encryptedKey.decode()); 255 256 } else if (alg.equals(JWEAlgorithm.A128GCMKW) || 257 alg.equals(JWEAlgorithm.A192GCMKW) || 258 alg.equals(JWEAlgorithm.A256GCMKW)) { 259 260 byte[] keyIV = header.getIV().decode(); 261 byte[] keyTag = header.getAuthTag().decode(); 262 AuthenticatedCipherText authEncrCEK = new AuthenticatedCipherText(encryptedKey.decode(), keyTag); 263 cek = AESGCMKW.decryptCEK(kek, keyIV, authEncrCEK, keyLength, keyEncryptionProvider); 264 265 } else { 266 267 throw new JOSEException("Unsupported JWE algorithm, must be A128KW, A192KW, A256KW, A128GCMKW, A192GCMKW orA256GCMKW"); 268 } 269 270 // Compose the AAD 271 byte[] aad = StringUtils.toByteArray(header.toBase64URL().toString()); 272 273 // Decrypt the cipher text according to the JWE enc 274 EncryptionMethod enc = header.getEncryptionMethod(); 275 276 byte[] plainText; 277 278 if (enc.equals(EncryptionMethod.A128CBC_HS256) || 279 enc.equals(EncryptionMethod.A192CBC_HS384) || 280 enc.equals(EncryptionMethod.A256CBC_HS512)) { 281 282 plainText = AESCBC.decryptAuthenticated( 283 cek, 284 iv.decode(), 285 cipherText.decode(), 286 aad, 287 authTag.decode(), 288 contentEncryptionProvider, 289 macProvider); 290 291 } else if (enc.equals(EncryptionMethod.A128GCM) || 292 enc.equals(EncryptionMethod.A192GCM) || 293 enc.equals(EncryptionMethod.A256GCM)) { 294 295 plainText = AESGCM.decrypt( 296 cek, 297 iv.decode(), 298 cipherText.decode(), 299 aad, 300 authTag.decode(), 301 contentEncryptionProvider); 302 303 } else if (enc.equals(EncryptionMethod.A128CBC_HS256_DEPRECATED) || 304 enc.equals(EncryptionMethod.A256CBC_HS512_DEPRECATED)) { 305 306 plainText = AESCBC.decryptWithConcatKDF( 307 header, 308 cek, 309 encryptedKey, 310 iv, 311 cipherText, 312 authTag, 313 contentEncryptionProvider, 314 macProvider); 315 316 } else { 317 318 throw new JOSEException("Unsupported encryption method, must be A128CBC_HS256, A192CBC_HS384, A256CBC_HS512, A128GCM, A192GCM or A256GCM"); 319 } 320 321 322 // Apply decompression if requested 323 return DeflateHelper.applyDecompression(header, plainText); 324 } 325}