001/* 002 * nimbus-jose-jwt 003 * 004 * Copyright 2012-2016, Connect2id Ltd. 005 * 006 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use 007 * this file except in compliance with the License. You may obtain a copy of the 008 * License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software distributed 013 * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 014 * CONDITIONS OF ANY KIND, either express or implied. See the License for the 015 * specific language governing permissions and limitations under the License. 016 */ 017 018package com.nimbusds.jose.crypto; 019 020 021import java.net.URI; 022import java.util.List; 023import java.util.Map; 024import java.util.Set; 025 026import com.nimbusds.jose.*; 027import com.nimbusds.jose.crypto.impl.AAD; 028import com.nimbusds.jose.crypto.impl.CriticalHeaderParamsDeferral; 029import com.nimbusds.jose.crypto.impl.JWEHeaderValidation; 030import com.nimbusds.jose.crypto.impl.MultiCryptoProvider; 031import com.nimbusds.jose.jwk.JWK; 032import com.nimbusds.jose.jwk.KeyType; 033import com.nimbusds.jose.util.Base64; 034import com.nimbusds.jose.util.Base64URL; 035import com.nimbusds.jose.util.JSONObjectUtils; 036import net.jcip.annotations.ThreadSafe; 037 038 039/** 040 * Multi-recipient decrypter of {@link com.nimbusds.jose.JWEObjectJSON JWE objects}. 041 * 042 * <p>This class is thread-safe. 043 * 044 * <p>Supports the following key management algorithms: 045 * 046 * <ul> 047 * <li>{@link com.nimbusds.jose.JWEAlgorithm#A128KW} 048 * <li>{@link com.nimbusds.jose.JWEAlgorithm#A192KW} 049 * <li>{@link com.nimbusds.jose.JWEAlgorithm#A256KW} 050 * <li>{@link com.nimbusds.jose.JWEAlgorithm#A128GCMKW} 051 * <li>{@link com.nimbusds.jose.JWEAlgorithm#A192GCMKW} 052 * <li>{@link com.nimbusds.jose.JWEAlgorithm#A256GCMKW} 053 * <li>{@link com.nimbusds.jose.JWEAlgorithm#DIR} 054 * <li>{@link com.nimbusds.jose.JWEAlgorithm#ECDH_ES_A128KW} 055 * <li>{@link com.nimbusds.jose.JWEAlgorithm#ECDH_ES_A192KW} 056 * <li>{@link com.nimbusds.jose.JWEAlgorithm#ECDH_ES_A256KW} 057 * <li>{@link com.nimbusds.jose.JWEAlgorithm#RSA_OAEP_256} 058 * <li>{@link com.nimbusds.jose.JWEAlgorithm#RSA_OAEP_384} 059 * <li>{@link com.nimbusds.jose.JWEAlgorithm#RSA_OAEP_512} 060 * <li>{@link com.nimbusds.jose.JWEAlgorithm#RSA_OAEP} (deprecated) 061 * <li>{@link com.nimbusds.jose.JWEAlgorithm#RSA1_5} (deprecated) 062 * </ul> 063 * 064 * <p>Supports the following elliptic curves: 065 * 066 * <ul> 067 * <li>{@link com.nimbusds.jose.jwk.Curve#P_256} 068 * <li>{@link com.nimbusds.jose.jwk.Curve#P_384} 069 * <li>{@link com.nimbusds.jose.jwk.Curve#P_521} 070 * <li>{@link com.nimbusds.jose.jwk.Curve#X25519} (Curve25519) 071 * </ul> 072 * 073 * <p>Supports the following content encryption algorithms: 074 * 075 * <ul> 076 * <li>{@link com.nimbusds.jose.EncryptionMethod#A128CBC_HS256} (requires 256 bit key) 077 * <li>{@link com.nimbusds.jose.EncryptionMethod#A192CBC_HS384} (requires 384 bit key) 078 * <li>{@link com.nimbusds.jose.EncryptionMethod#A256CBC_HS512} (requires 512 bit key) 079 * <li>{@link com.nimbusds.jose.EncryptionMethod#A128GCM} (requires 128 bit key) 080 * <li>{@link com.nimbusds.jose.EncryptionMethod#A192GCM} (requires 192 bit key) 081 * <li>{@link com.nimbusds.jose.EncryptionMethod#A256GCM} (requires 256 bit key) 082 * <li>{@link com.nimbusds.jose.EncryptionMethod#A128CBC_HS256_DEPRECATED} (requires 256 bit key) 083 * <li>{@link com.nimbusds.jose.EncryptionMethod#A256CBC_HS512_DEPRECATED} (requires 512 bit key) 084 * <li>{@link com.nimbusds.jose.EncryptionMethod#XC20P} (requires 256 bit key) 085 * </ul> 086 * 087 * @author Egor Puzanov 088 * @version 2023-09-10 089 */ 090@ThreadSafe 091public class MultiDecrypter extends MultiCryptoProvider implements JWEDecrypter, CriticalHeaderParamsAware { 092 093 094 /** 095 * The private JWK key. 096 */ 097 private final JWK jwk; 098 099 100 /** 101 * The key id of the private JWK key. 102 */ 103 private final String kid; 104 105 106 /** 107 * The Cerificate URL of the private JWK key. 108 */ 109 private final URI x5u; 110 111 112 /** 113 * The Certificate thumbprint of the private JWK key. 114 */ 115 private final Base64URL x5t; 116 117 118 /** 119 * The Certificate SHA256 thumbprint of the private JWK key. 120 */ 121 private final Base64URL x5t256; 122 123 124 /** 125 * The Certificate chain of the private JWK key. 126 */ 127 private final List<Base64> x5c; 128 129 130 /** 131 * The Thumbprint of the private JWK key. 132 */ 133 private final Base64URL thumbprint; 134 135 136 /** 137 * The critical header policy. 138 */ 139 private final CriticalHeaderParamsDeferral critPolicy = new CriticalHeaderParamsDeferral(); 140 141 142 /** 143 * Creates a new multi-recipient decrypter. 144 * 145 * @param jwk The JSON Web Key (JWK). Must contain a private part. Must 146 * not be {@code null}. 147 * 148 * @throws KeyLengthException If the symmetric key length is not 149 * compatible. 150 * @throws JOSEException If an internal exception is encountered. 151 */ 152 public MultiDecrypter(final JWK jwk) 153 throws JOSEException, KeyLengthException { 154 155 this(jwk, null); 156 } 157 158 159 /** 160 * Creates a new multi-recipient decrypter. 161 * 162 * @param jwk The JSON Web Key (JWK). Must contain a private 163 * part. Must not be {@code null}. 164 * @param defCritHeaders The names of the critical header parameters 165 * that are deferred to the application for 166 * processing, empty set or {@code null} if none. 167 * 168 * @throws KeyLengthException If the symmetric key length is not 169 * compatible. 170 * @throws JOSEException If an internal exception is encountered. 171 */ 172 public MultiDecrypter(final JWK jwk, final Set<String> defCritHeaders) 173 throws JOSEException, KeyLengthException { 174 175 super(null); 176 177 if (jwk == null) { 178 throw new IllegalArgumentException("The private key (JWK) must not be null"); 179 } 180 this.jwk = jwk; 181 this.kid = jwk.getKeyID(); 182 this.x5c = jwk.getX509CertChain(); 183 this.x5u = jwk.getX509CertURL(); 184 this.x5t = jwk.getX509CertThumbprint(); 185 this.x5t256 = jwk.getX509CertSHA256Thumbprint(); 186 this.thumbprint = jwk.computeThumbprint(); 187 188 critPolicy.setDeferredCriticalHeaderParams(defCritHeaders); 189 } 190 191 192 @Override 193 public Set<String> getProcessedCriticalHeaderParams() { 194 195 return critPolicy.getProcessedCriticalHeaderParams(); 196 } 197 198 199 @Override 200 public Set<String> getDeferredCriticalHeaderParams() { 201 202 return critPolicy.getProcessedCriticalHeaderParams(); 203 } 204 205 206 private boolean jwkMatched(final JWEHeader recipientHeader) 207 throws JOSEException { 208 209 if (thumbprint.toString().equals(recipientHeader.getKeyID())) { 210 return true; 211 } 212 JWK rjwk = recipientHeader.getJWK(); 213 if (rjwk != null && thumbprint.equals(rjwk.computeThumbprint())) { 214 return true; 215 } 216 if (x5u != null && x5u.equals(recipientHeader.getX509CertURL())) { 217 return true; 218 } 219 if (x5t != null && x5t.equals(recipientHeader.getX509CertThumbprint())) { 220 return true; 221 } 222 if (x5t256 != null && x5t256.equals(recipientHeader.getX509CertSHA256Thumbprint())) { 223 return true; 224 } 225 List<Base64> rx5c = recipientHeader.getX509CertChain(); 226 if (x5c != null && rx5c != null && x5c.containsAll(rx5c) && rx5c.containsAll(x5c)) { 227 return true; 228 } 229 if (kid != null && kid.equals(recipientHeader.getKeyID())) { 230 return true; 231 } 232 return false; 233 } 234 235 236 /** 237 * Decrypts the specified cipher text of a {@link JWEObject JWE Object}. 238 * 239 * @param header The JSON Web Encryption (JWE) header. Must 240 * specify a supported JWE algorithm and method. 241 * Must not be {@code null}. 242 * @param encryptedKey The encrypted key, {@code null} if not required 243 * by the JWE algorithm. 244 * @param iv The initialisation vector, {@code null} if not 245 * required by the JWE algorithm. 246 * @param cipherText The cipher text to decrypt. Must not be 247 * {@code null}. 248 * @param authTag The authentication tag, {@code null} if not 249 * required. 250 * 251 * @return The clear text. 252 * 253 * @throws JOSEException If the JWE algorithm or method is not 254 * supported, if a critical header parameter is 255 * not supported or marked for deferral to the 256 * application, or if decryption failed for some 257 * other reason. 258 */ 259 @Deprecated 260 public byte[] decrypt(final JWEHeader header, 261 final Base64URL encryptedKey, 262 final Base64URL iv, 263 final Base64URL cipherText, 264 final Base64URL authTag) 265 throws JOSEException { 266 267 return decrypt(header, encryptedKey, iv, cipherText, authTag, AAD.compute(header)); 268 } 269 270 271 @Override 272 public byte[] decrypt(final JWEHeader header, 273 final Base64URL encryptedKey, 274 final Base64URL iv, 275 final Base64URL cipherText, 276 final Base64URL authTag, 277 final byte[] aad) 278 throws JOSEException { 279 280 if (iv == null) { 281 throw new JOSEException("Unexpected present JWE initialization vector (IV)"); 282 } 283 284 if (authTag == null) { 285 throw new JOSEException("Missing JWE authentication tag"); 286 } 287 288 if (aad == null) { 289 throw new JOSEException("Missing JWE additional authenticated data (AAD)"); 290 } 291 292 final JWEDecrypter decrypter; 293 final KeyType kty = jwk.getKeyType(); 294 final Set<String> defCritHeaders = critPolicy.getDeferredCriticalHeaderParams(); 295 JWEObjectJSON.Recipient recipient = null; 296 JWEHeader recipientHeader = null; 297 try { 298 // The encryptedKey value contains the Base64URL encoded JSON string 299 // {"recipients":[{recipient1},{recipient2}]} if multiple recipients are used. 300 for (Object recipientMap : JSONObjectUtils.getJSONArray((JSONObjectUtils.parse(encryptedKey.decodeToString())), "recipients")) { 301 try { 302 recipient = JWEObjectJSON.Recipient.parse((Map<String, Object>) recipientMap); 303 recipientHeader = (JWEHeader) header.join(recipient.getUnprotectedHeader()); 304 } catch (Exception e) { 305 throw new JOSEException(e.getMessage()); 306 } 307 if (jwkMatched(recipientHeader)) { 308 break; 309 } 310 recipientHeader = null; 311 } 312 } catch (Exception e) { 313 // If encryptedKey can not be parsed as a JSON Object, it means the encryptedKey contains the RAW encrypted key value. 314 recipientHeader = header; 315 recipient = new JWEObjectJSON.Recipient(null, encryptedKey); 316 } 317 318 if (recipientHeader == null) { 319 throw new JOSEException("No recipient found"); 320 } 321 322 final JWEAlgorithm alg = JWEHeaderValidation.getAlgorithmAndEnsureNotNull(recipientHeader); 323 critPolicy.ensureHeaderPasses(recipientHeader); 324 325 if (KeyType.RSA.equals(kty) && RSADecrypter.SUPPORTED_ALGORITHMS.contains(alg)) { 326 decrypter = new RSADecrypter(jwk.toRSAKey().toRSAPrivateKey(), defCritHeaders); 327 } else if (KeyType.EC.equals(kty) && ECDHDecrypter.SUPPORTED_ALGORITHMS.contains(alg)) { 328 decrypter = new ECDHDecrypter(jwk.toECKey().toECPrivateKey(), defCritHeaders); 329 } else if (KeyType.OCT.equals(kty) && AESDecrypter.SUPPORTED_ALGORITHMS.contains(alg)) { 330 decrypter = new AESDecrypter(jwk.toOctetSequenceKey().toSecretKey("AES"), defCritHeaders); 331 } else if (KeyType.OCT.equals(kty) && DirectDecrypter.SUPPORTED_ALGORITHMS.contains(alg)) { 332 decrypter = new DirectDecrypter(jwk.toOctetSequenceKey().toSecretKey("AES"), defCritHeaders); 333 } else if (KeyType.OKP.equals(kty) && X25519Decrypter.SUPPORTED_ALGORITHMS.contains(alg)) { 334 decrypter = new X25519Decrypter(jwk.toOctetKeyPair(), defCritHeaders); 335 } else { 336 throw new JOSEException("Unsupported algorithm"); 337 } 338 339 return decrypter.decrypt(recipientHeader, recipient.getEncryptedKey(), iv, cipherText, authTag, aad); 340 } 341}