001package com.nimbusds.jose.crypto; 002 003 004import java.security.PrivateKey; 005import java.util.Set; 006import javax.crypto.SecretKey; 007 008import com.nimbusds.jose.*; 009import com.nimbusds.jose.jwk.RSAKey; 010import com.nimbusds.jose.util.Base64URL; 011import net.jcip.annotations.ThreadSafe; 012 013 014/** 015 * RSA decrypter of {@link com.nimbusds.jose.JWEObject JWE objects}. This class 016 * is thread-safe. 017 * 018 * <p>Supports the following key management algorithms: 019 * 020 * <ul> 021 * <li>{@link com.nimbusds.jose.JWEAlgorithm#RSA1_5} 022 * <li>{@link com.nimbusds.jose.JWEAlgorithm#RSA_OAEP} 023 * <li>{@link com.nimbusds.jose.JWEAlgorithm#RSA_OAEP_256} 024 * </ul> 025 * 026 * <p>Supports the following content encryption algorithms: 027 * 028 * <ul> 029 * <li>{@link com.nimbusds.jose.EncryptionMethod#A128CBC_HS256} 030 * <li>{@link com.nimbusds.jose.EncryptionMethod#A192CBC_HS384} 031 * <li>{@link com.nimbusds.jose.EncryptionMethod#A256CBC_HS512} 032 * <li>{@link com.nimbusds.jose.EncryptionMethod#A128GCM} 033 * <li>{@link com.nimbusds.jose.EncryptionMethod#A192GCM} 034 * <li>{@link com.nimbusds.jose.EncryptionMethod#A256GCM} 035 * <li>{@link com.nimbusds.jose.EncryptionMethod#A128CBC_HS256_DEPRECATED} 036 * <li>{@link com.nimbusds.jose.EncryptionMethod#A256CBC_HS512_DEPRECATED} 037 * </ul> 038 * 039 * @author David Ortiz 040 * @author Vladimir Dzhuvinov 041 * @author Dimitar A. Stoikov 042 * @version 2016-06-29 043 */ 044@ThreadSafe 045public class RSADecrypter extends RSACryptoProvider implements JWEDecrypter, CriticalHeaderParamsAware { 046 047 048 /** 049 * The critical header policy. 050 */ 051 private final CriticalHeaderParamsDeferral critPolicy = new CriticalHeaderParamsDeferral(); 052 053 054 /** 055 * The private RSA key. 056 */ 057 private final PrivateKey privateKey; 058 059 060 /** 061 * Creates a new RSA decrypter. 062 * 063 * @param privateKey The private RSA key. Must not be {@code null}. 064 */ 065 public RSADecrypter(final PrivateKey privateKey) { 066 067 this(privateKey, null); 068 } 069 070 071 /** 072 * Creates a new RSA decrypter. 073 * 074 * @param rsaJWK The RSA JSON Web Key (JWK). Must contain a private 075 * part. Must not be {@code null}. 076 * 077 * @throws JOSEException If the RSA JWK doesn't contain a private part 078 * or its extraction failed. 079 */ 080 public RSADecrypter(final RSAKey rsaJWK) 081 throws JOSEException { 082 083 if (! rsaJWK.isPrivate()) { 084 throw new JOSEException("The RSA JWK doesn't contain a private part"); 085 } 086 087 privateKey = rsaJWK.toRSAPrivateKey(); 088 } 089 090 091 /** 092 * Creates a new RSA decrypter. 093 * 094 * @param privateKey The private RSA key. Its algorithm must be 095 * "RSA". Must not be {@code null}. 096 * @param defCritHeaders The names of the critical header parameters 097 * that are deferred to the application for 098 * processing, empty set or {@code null} if none. 099 */ 100 public RSADecrypter(final PrivateKey privateKey, 101 final Set<String> defCritHeaders) { 102 103 if (privateKey == null) { 104 throw new IllegalArgumentException("The private RSA key must not be null"); 105 } 106 107 if (! privateKey.getAlgorithm().equalsIgnoreCase("RSA")) { 108 throw new IllegalArgumentException("The private key algorithm must be RSA"); 109 } 110 111 this.privateKey = privateKey; 112 113 critPolicy.setDeferredCriticalHeaderParams(defCritHeaders); 114 } 115 116 117 /** 118 * Gets the private RSA key. 119 * 120 * @return The private RSA key. Casting to 121 * {@link java.security.interfaces.RSAPrivateKey} may not be 122 * possible if the key is backed by a key store that doesn't 123 * expose the private key parameters. 124 */ 125 public PrivateKey getPrivateKey() { 126 127 return privateKey; 128 } 129 130 131 @Override 132 public Set<String> getProcessedCriticalHeaderParams() { 133 134 return critPolicy.getProcessedCriticalHeaderParams(); 135 } 136 137 138 @Override 139 public Set<String> getDeferredCriticalHeaderParams() { 140 141 return critPolicy.getProcessedCriticalHeaderParams(); 142 } 143 144 145 @Override 146 public byte[] decrypt(final JWEHeader header, 147 final Base64URL encryptedKey, 148 final Base64URL iv, 149 final Base64URL cipherText, 150 final Base64URL authTag) 151 throws JOSEException { 152 153 // Validate required JWE parts 154 if (encryptedKey == null) { 155 throw new JOSEException("Missing JWE encrypted key"); 156 } 157 158 if (iv == null) { 159 throw new JOSEException("Missing JWE initialization vector (IV)"); 160 } 161 162 if (authTag == null) { 163 throw new JOSEException("Missing JWE authentication tag"); 164 } 165 166 critPolicy.ensureHeaderPasses(header); 167 168 169 // Derive the content encryption key 170 JWEAlgorithm alg = header.getAlgorithm(); 171 172 SecretKey cek; 173 174 if (alg.equals(JWEAlgorithm.RSA1_5)) { 175 176 int keyLength = header.getEncryptionMethod().cekBitLength(); 177 178 // Protect against MMA attack by generating random CEK on failure, 179 // see http://www.ietf.org/mail-archive/web/jose/current/msg01832.html 180 final SecretKey randomCEK = ContentCryptoProvider.generateCEK(header.getEncryptionMethod(), getJCAContext().getSecureRandom()); 181 182 try { 183 cek = RSA1_5.decryptCEK(privateKey, encryptedKey.decode(), keyLength, getJCAContext().getKeyEncryptionProvider()); 184 185 if (cek == null) { 186 // CEK length mismatch, signalled by null instead of 187 // exception to prevent MMA attack 188 cek = randomCEK; 189 } 190 191 } catch (Exception e) { 192 // continue 193 cek = randomCEK; 194 } 195 196 } else if (alg.equals(JWEAlgorithm.RSA_OAEP)) { 197 198 cek = RSA_OAEP.decryptCEK(privateKey, encryptedKey.decode(), getJCAContext().getKeyEncryptionProvider()); 199 200 } else if (alg.equals(JWEAlgorithm.RSA_OAEP_256)) { 201 202 cek = RSA_OAEP_256.decryptCEK(privateKey, encryptedKey.decode(), getJCAContext().getKeyEncryptionProvider()); 203 204 } else { 205 206 throw new JOSEException(AlgorithmSupportMessage.unsupportedJWEAlgorithm(alg, SUPPORTED_ALGORITHMS)); 207 } 208 209 return ContentCryptoProvider.decrypt(header, encryptedKey, iv, cipherText, authTag, cek, getJCAContext()); 210 } 211} 212