001/* 002 * nimbus-jose-jwt 003 * 004 * Copyright 2012-2016, Connect2id Ltd and contributors. 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.impl; 019 020 021import java.security.AlgorithmParameters; 022import java.security.PrivateKey; 023import java.security.Provider; 024import java.security.interfaces.RSAPublicKey; 025import java.security.spec.AlgorithmParameterSpec; 026import java.security.spec.MGF1ParameterSpec; 027import javax.crypto.Cipher; 028import javax.crypto.IllegalBlockSizeException; 029import javax.crypto.SecretKey; 030import javax.crypto.spec.OAEPParameterSpec; 031import javax.crypto.spec.PSource; 032import javax.crypto.spec.SecretKeySpec; 033 034import net.jcip.annotations.ThreadSafe; 035 036import com.nimbusds.jose.JOSEException; 037 038 039/** 040 * RSAES OAEP with SHA-256, SHA-384 and SHA-512 methods for Content Encryption 041 * Key (CEK) encryption and decryption. This class is thread-safe. 042 * 043 * @author Vladimir Dzhuvinov 044 * @author Justin Richer 045 * @author Peter Laurina 046 * @version 2021-09-24 047 */ 048@ThreadSafe 049public class RSA_OAEP_SHA2 { 050 051 052 /** 053 * The JCA algorithm name for RSA-OAEP-256. 054 */ 055 private static final String RSA_OEAP_256_JCA_ALG = "RSA/ECB/OAEPWithSHA-256AndMGF1Padding"; 056 057 058 /** 059 * The JCA algorithm name for RSA-OAEP-384. 060 */ 061 private static final String RSA_OEAP_384_JCA_ALG = "RSA/ECB/OAEPWithSHA-384AndMGF1Padding"; 062 063 064 /** 065 * The JCA algorithm name for RSA-OAEP-512. 066 */ 067 private static final String RSA_OEAP_512_JCA_ALG = "RSA/ECB/OAEPWithSHA-512AndMGF1Padding"; 068 069 070 /** 071 * The JCA algorithm name for SHA-256. 072 */ 073 private static final String SHA_256_JCA_ALG = "SHA-256"; 074 075 076 /** 077 * The JCA algorithm name for SHA-384. 078 */ 079 private static final String SHA_384_JCA_ALG = "SHA-384"; 080 081 /** 082 * The JCA algorithm name for SHA-512. 083 */ 084 private static final String SHA_512_JCA_ALG = "SHA-512"; 085 086 087 /** 088 * Encrypts the specified Content Encryption Key (CEK). 089 * 090 * @param pub The public RSA key. Must not be {@code null}. 091 * @param cek The Content Encryption Key (CEK) to encrypt. Must 092 * not be {@code null}. 093 * @param shaBitSize The SHA-2 bit size. Must be 256, 384 or 512. 094 * @param provider The JCA provider, or {@code null} to use the 095 * default one. 096 * 097 * @return The encrypted Content Encryption Key (CEK). 098 * 099 * @throws JOSEException If encryption failed. 100 */ 101 public static byte[] encryptCEK(final RSAPublicKey pub, 102 final SecretKey cek, 103 final int shaBitSize, 104 final Provider provider) 105 throws JOSEException { 106 107 final String jcaAlgName; 108 final String jcaShaAlgName; 109 final MGF1ParameterSpec mgf1ParameterSpec; 110 if (256 == shaBitSize) { 111 jcaAlgName = RSA_OEAP_256_JCA_ALG; 112 jcaShaAlgName = SHA_256_JCA_ALG; 113 mgf1ParameterSpec = MGF1ParameterSpec.SHA256; 114 } else if (384 == shaBitSize) { 115 jcaAlgName = RSA_OEAP_384_JCA_ALG; 116 jcaShaAlgName = SHA_384_JCA_ALG; 117 mgf1ParameterSpec = MGF1ParameterSpec.SHA384; 118 } else if (512 == shaBitSize) { 119 jcaAlgName = RSA_OEAP_512_JCA_ALG; 120 jcaShaAlgName = SHA_512_JCA_ALG; 121 mgf1ParameterSpec = MGF1ParameterSpec.SHA512; 122 } else { 123 throw new JOSEException("Unsupported SHA-2 bit size: " + shaBitSize); 124 } 125 126 try { 127 AlgorithmParameters algp = AlgorithmParametersHelper.getInstance("OAEP", provider); 128 AlgorithmParameterSpec paramSpec = new OAEPParameterSpec(jcaShaAlgName, "MGF1", mgf1ParameterSpec, PSource.PSpecified.DEFAULT); 129 algp.init(paramSpec); 130 Cipher cipher = CipherHelper.getInstance(jcaAlgName, provider); 131 cipher.init(Cipher.ENCRYPT_MODE, pub, algp); 132 return cipher.doFinal(cek.getEncoded()); 133 134 } catch (IllegalBlockSizeException e) { 135 throw new JOSEException("RSA block size exception: The RSA key is too short, use a longer one", e); 136 } catch (Exception e) { 137 // java.security.NoSuchAlgorithmException 138 // java.security.NoSuchPaddingException 139 // java.security.InvalidKeyException 140 // javax.crypto.BadPaddingException 141 throw new JOSEException(e.getMessage(), e); 142 } 143 } 144 145 146 /** 147 * Decrypts the specified encrypted Content Encryption Key (CEK). 148 * 149 * @param priv The private RSA key. Must not be {@code null}. 150 * @param encryptedCEK The encrypted Content Encryption Key (CEK) to 151 * decrypt. Must not be {@code null}. 152 * @param shaBitSize The SHA-2 bit size. Must be 256 or 512. 153 * @param provider The JCA provider, or {@code null} to use the 154 * default one. 155 * 156 * @return The decrypted Content Encryption Key (CEK). 157 * 158 * @throws JOSEException If decryption failed. 159 */ 160 public static SecretKey decryptCEK(final PrivateKey priv, 161 final byte[] encryptedCEK, 162 final int shaBitSize, 163 final Provider provider) 164 throws JOSEException { 165 166 final String jcaAlgName; 167 final String jcaShaAlgName; 168 final MGF1ParameterSpec mgf1ParameterSpec; 169 if (256 == shaBitSize) { 170 jcaAlgName = RSA_OEAP_256_JCA_ALG; 171 jcaShaAlgName = SHA_256_JCA_ALG; 172 mgf1ParameterSpec = MGF1ParameterSpec.SHA256; 173 } else if (384 == shaBitSize) { 174 jcaAlgName = RSA_OEAP_384_JCA_ALG; 175 jcaShaAlgName = SHA_384_JCA_ALG; 176 mgf1ParameterSpec = MGF1ParameterSpec.SHA384; 177 } else if (512 == shaBitSize) { 178 jcaAlgName = RSA_OEAP_512_JCA_ALG; 179 jcaShaAlgName = SHA_512_JCA_ALG; 180 mgf1ParameterSpec = MGF1ParameterSpec.SHA512; 181 } else { 182 throw new JOSEException("Unsupported SHA-2 bit size: " + shaBitSize); 183 } 184 185 try { 186 AlgorithmParameters algp = AlgorithmParametersHelper.getInstance("OAEP", provider); 187 AlgorithmParameterSpec paramSpec = new OAEPParameterSpec(jcaShaAlgName, "MGF1", mgf1ParameterSpec, PSource.PSpecified.DEFAULT); 188 algp.init(paramSpec); 189 Cipher cipher = CipherHelper.getInstance(jcaAlgName, provider); 190 cipher.init(Cipher.DECRYPT_MODE, priv, algp); 191 return new SecretKeySpec(cipher.doFinal(encryptedCEK), "AES"); 192 193 } catch (Exception e) { 194 // java.security.NoSuchAlgorithmException 195 // java.security.NoSuchPaddingException 196 // java.security.InvalidKeyException 197 // javax.crypto.IllegalBlockSizeException 198 // javax.crypto.BadPaddingException 199 throw new JOSEException(e.getMessage(), e); 200 } 201 } 202 203 204 /** 205 * Prevents public instantiation. 206 */ 207 private RSA_OAEP_SHA2() { } 208}