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 * </ul> 071 * 072 * @author David Ortiz 073 * @author Vladimir Dzhuvinov 074 * @author Jun Yu 075 * @version 2018-07-17 076 */ 077@ThreadSafe 078public class RSAEncrypter extends RSACryptoProvider implements JWEEncrypter { 079 080 081 /** 082 * The public RSA key. 083 */ 084 private final RSAPublicKey publicKey; 085 086 087 /** 088 * The externally supplied AES content encryption key (CEK) to use, 089 * {@code null} to generate a CEK for each JWE. 090 */ 091 private final SecretKey contentEncryptionKey; 092 093 094 /** 095 * Creates a new RSA encrypter. 096 * 097 * @param publicKey The public RSA key. Must not be {@code null}. 098 */ 099 public RSAEncrypter(final RSAPublicKey publicKey) { 100 101 this(publicKey, null); 102 } 103 104 105 /** 106 * Creates a new RSA encrypter. 107 * 108 * @param rsaJWK The RSA JSON Web Key (JWK). Must not be {@code null}. 109 * 110 * @throws JOSEException If the RSA JWK extraction failed. 111 */ 112 public RSAEncrypter(final RSAKey rsaJWK) 113 throws JOSEException { 114 115 this(rsaJWK.toRSAPublicKey()); 116 } 117 118 119 /** 120 * Creates a new RSA encrypter with an optionally specified content 121 * encryption key (CEK). 122 * 123 * @param publicKey The public RSA key. Must not be 124 * {@code null}. 125 * @param contentEncryptionKey The content encryption key (CEK) to use. 126 * If specified its algorithm must be "AES" 127 * and its length must match the expected 128 * for the JWE encryption method ("enc"). 129 * If {@code null} a CEK will be generated 130 * for each JWE. 131 */ 132 public RSAEncrypter(final RSAPublicKey publicKey, final SecretKey contentEncryptionKey) { 133 134 if (publicKey == null) { 135 throw new IllegalArgumentException("The public RSA key must not be null"); 136 } 137 this.publicKey = publicKey; 138 139 if (contentEncryptionKey != null) { 140 if (contentEncryptionKey.getAlgorithm() == null || !contentEncryptionKey.getAlgorithm().equals("AES")) { 141 throw new IllegalArgumentException("The algorithm of the content encryption key (CEK) must be AES"); 142 } else { 143 this.contentEncryptionKey = contentEncryptionKey; 144 } 145 } else { 146 this.contentEncryptionKey = null; 147 } 148 } 149 150 151 /** 152 * Gets the public RSA key. 153 * 154 * @return The public RSA key. 155 */ 156 public RSAPublicKey getPublicKey() { 157 158 return publicKey; 159 } 160 161 162 @Override 163 public JWECryptoParts encrypt(final JWEHeader header, final byte[] clearText) 164 throws JOSEException { 165 166 final JWEAlgorithm alg = header.getAlgorithm(); 167 final EncryptionMethod enc = header.getEncryptionMethod(); 168 169 // Generate and encrypt the CEK according to the enc method 170 final SecretKey cek; 171 if (contentEncryptionKey != null) { 172 // Use externally supplied CEK 173 cek = contentEncryptionKey; 174 } else { 175 // Generate and encrypt the CEK according to the enc method 176 cek = ContentCryptoProvider.generateCEK(enc, getJCAContext().getSecureRandom()); 177 } 178 179 final Base64URL encryptedKey; // The second JWE part 180 181 if (alg.equals(JWEAlgorithm.RSA1_5)) { 182 183 encryptedKey = Base64URL.encode(RSA1_5.encryptCEK(publicKey, cek, getJCAContext().getKeyEncryptionProvider())); 184 185 } else if (alg.equals(JWEAlgorithm.RSA_OAEP)) { 186 187 encryptedKey = Base64URL.encode(RSA_OAEP.encryptCEK(publicKey, cek, getJCAContext().getKeyEncryptionProvider())); 188 189 } else if (alg.equals(JWEAlgorithm.RSA_OAEP_256)) { 190 191 encryptedKey = Base64URL.encode(RSA_OAEP_256.encryptCEK(publicKey, cek, getJCAContext().getKeyEncryptionProvider())); 192 193 } else { 194 195 throw new JOSEException(AlgorithmSupportMessage.unsupportedJWEAlgorithm(alg, SUPPORTED_ALGORITHMS)); 196 } 197 198 return ContentCryptoProvider.encrypt(header, clearText, cek, encryptedKey, getJCAContext()); 199 } 200}