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.security.interfaces.RSAPublicKey; 022import javax.crypto.SecretKey; 023 024import com.nimbusds.jose.crypto.impl.*; 025import net.jcip.annotations.ThreadSafe; 026 027import com.nimbusds.jose.EncryptionMethod; 028import com.nimbusds.jose.JOSEException; 029import com.nimbusds.jose.JWEAlgorithm; 030import com.nimbusds.jose.JWECryptoParts; 031import com.nimbusds.jose.JWEEncrypter; 032import com.nimbusds.jose.JWEHeader; 033import com.nimbusds.jose.jwk.RSAKey; 034import com.nimbusds.jose.util.Base64URL; 035 036 037/** 038 * RSA encrypter of {@link com.nimbusds.jose.JWEObject JWE objects}. Expects a 039 * public RSA key. 040 * 041 * <p>Encrypts the plain text with a generated AES key (the Content Encryption 042 * Key) according to the specified JOSE encryption method, then encrypts the 043 * CEK with the public RSA key and returns it alongside the IV, cipher text and 044 * authentication tag. See RFC 7518, sections 045 * <a href="https://tools.ietf.org/html/rfc7518#section-4.2">4.2</a> and 046 * <a href="https://tools.ietf.org/html/rfc7518#section-4.3">4.3</a> for more 047 * information. 048 * 049 * <p>This class is thread-safe. 050 * 051 * <p>Supports the following key management algorithms: 052 * 053 * <ul> 054 * <li>{@link com.nimbusds.jose.JWEAlgorithm#RSA_OAEP_256} 055 * <li>{@link com.nimbusds.jose.JWEAlgorithm#RSA_OAEP} (deprecated) 056 * <li>{@link com.nimbusds.jose.JWEAlgorithm#RSA1_5} (deprecated) 057 * </ul> 058 * 059 * <p>Supports the following content encryption algorithms: 060 * 061 * <ul> 062 * <li>{@link com.nimbusds.jose.EncryptionMethod#A128CBC_HS256} 063 * <li>{@link com.nimbusds.jose.EncryptionMethod#A192CBC_HS384} 064 * <li>{@link com.nimbusds.jose.EncryptionMethod#A256CBC_HS512} 065 * <li>{@link com.nimbusds.jose.EncryptionMethod#A128GCM} 066 * <li>{@link com.nimbusds.jose.EncryptionMethod#A192GCM} 067 * <li>{@link com.nimbusds.jose.EncryptionMethod#A256GCM} 068 * <li>{@link com.nimbusds.jose.EncryptionMethod#A128CBC_HS256_DEPRECATED} 069 * <li>{@link com.nimbusds.jose.EncryptionMethod#A256CBC_HS512_DEPRECATED} 070 * <li>{@link com.nimbusds.jose.EncryptionMethod#XC20P} 071 * </ul> 072 * 073 * @author David Ortiz 074 * @author Vladimir Dzhuvinov 075 * @author Jun Yu 076 * @version 2018-07-17 077 */ 078@ThreadSafe 079public class RSAEncrypter extends RSACryptoProvider implements JWEEncrypter { 080 081 082 /** 083 * The public RSA key. 084 */ 085 private final RSAPublicKey publicKey; 086 087 088 /** 089 * The externally supplied AES content encryption key (CEK) to use, 090 * {@code null} to generate a CEK for each JWE. 091 */ 092 private final SecretKey contentEncryptionKey; 093 094 095 /** 096 * Creates a new RSA encrypter. 097 * 098 * @param publicKey The public RSA key. Must not be {@code null}. 099 */ 100 public RSAEncrypter(final RSAPublicKey publicKey) { 101 102 this(publicKey, null); 103 } 104 105 106 /** 107 * Creates a new RSA encrypter. 108 * 109 * @param rsaJWK The RSA JSON Web Key (JWK). Must not be {@code null}. 110 * 111 * @throws JOSEException If the RSA JWK extraction failed. 112 */ 113 public RSAEncrypter(final RSAKey rsaJWK) 114 throws JOSEException { 115 116 this(rsaJWK.toRSAPublicKey()); 117 } 118 119 120 /** 121 * Creates a new RSA encrypter with an optionally specified content 122 * encryption key (CEK). 123 * 124 * @param publicKey The public RSA key. Must not be 125 * {@code null}. 126 * @param contentEncryptionKey The content encryption key (CEK) to use. 127 * If specified its algorithm must be "AES" 128 * and its length must match the expected 129 * for the JWE encryption method ("enc"). 130 * If {@code null} a CEK will be generated 131 * for each JWE. 132 */ 133 public RSAEncrypter(final RSAPublicKey publicKey, final SecretKey contentEncryptionKey) { 134 135 if (publicKey == null) { 136 throw new IllegalArgumentException("The public RSA key must not be null"); 137 } 138 this.publicKey = publicKey; 139 140 if (contentEncryptionKey != null) { 141 if (contentEncryptionKey.getAlgorithm() == null || !contentEncryptionKey.getAlgorithm().equals("AES")) { 142 throw new IllegalArgumentException("The algorithm of the content encryption key (CEK) must be AES"); 143 } else { 144 this.contentEncryptionKey = contentEncryptionKey; 145 } 146 } else { 147 this.contentEncryptionKey = null; 148 } 149 } 150 151 152 /** 153 * Gets the public RSA key. 154 * 155 * @return The public RSA key. 156 */ 157 public RSAPublicKey getPublicKey() { 158 159 return publicKey; 160 } 161 162 163 @Override 164 public JWECryptoParts encrypt(final JWEHeader header, final byte[] clearText) 165 throws JOSEException { 166 167 final JWEAlgorithm alg = header.getAlgorithm(); 168 final EncryptionMethod enc = header.getEncryptionMethod(); 169 170 // Generate and encrypt the CEK according to the enc method 171 final SecretKey cek; 172 if (contentEncryptionKey != null) { 173 // Use externally supplied CEK 174 cek = contentEncryptionKey; 175 } else { 176 // Generate and encrypt the CEK according to the enc method 177 cek = ContentCryptoProvider.generateCEK(enc, getJCAContext().getSecureRandom()); 178 } 179 180 final Base64URL encryptedKey; // The second JWE part 181 182 if (alg.equals(JWEAlgorithm.RSA1_5)) { 183 184 encryptedKey = Base64URL.encode(RSA1_5.encryptCEK(publicKey, cek, getJCAContext().getKeyEncryptionProvider())); 185 186 } else if (alg.equals(JWEAlgorithm.RSA_OAEP)) { 187 188 encryptedKey = Base64URL.encode(RSA_OAEP.encryptCEK(publicKey, cek, getJCAContext().getKeyEncryptionProvider())); 189 190 } else if (alg.equals(JWEAlgorithm.RSA_OAEP_256)) { 191 192 encryptedKey = Base64URL.encode(RSA_OAEP_256.encryptCEK(publicKey, cek, getJCAContext().getKeyEncryptionProvider())); 193 194 } else { 195 196 throw new JOSEException(AlgorithmSupportMessage.unsupportedJWEAlgorithm(alg, SUPPORTED_ALGORITHMS)); 197 } 198 199 return ContentCryptoProvider.encrypt(header, clearText, cek, encryptedKey, getJCAContext()); 200 } 201}