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