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 com.nimbusds.jose.JOSEException; 022import net.jcip.annotations.ThreadSafe; 023 024import javax.crypto.Cipher; 025import javax.crypto.SecretKey; 026import javax.crypto.spec.OAEPParameterSpec; 027import javax.crypto.spec.PSource; 028import java.security.AlgorithmParameters; 029import java.security.InvalidKeyException; 030import java.security.PrivateKey; 031import java.security.Provider; 032import java.security.interfaces.RSAPublicKey; 033import java.security.spec.AlgorithmParameterSpec; 034import java.security.spec.MGF1ParameterSpec; 035 036 037/** 038 * RSAES OAEP with SHA-256, SHA-384 and SHA-512 methods for Content Encryption 039 * Key (CEK) encryption and decryption. This class is thread-safe. 040 * 041 * @author Vladimir Dzhuvinov 042 * @author Justin Richer 043 * @author Peter Laurina 044 * @author Pankaj Yadav 045 * @version 2024-09-10 046 */ 047@ThreadSafe 048public class RSA_OAEP_SHA2 { 049 050 051 /** 052 * The JCA algorithm name for RSA-OAEP-256. 053 */ 054 private static final String RSA_OEAP_256_JCA_ALG = "RSA/ECB/OAEPWithSHA-256AndMGF1Padding"; 055 056 057 /** 058 * The JCA algorithm name for RSA-OAEP-384. 059 */ 060 private static final String RSA_OEAP_384_JCA_ALG = "RSA/ECB/OAEPWithSHA-384AndMGF1Padding"; 061 062 063 /** 064 * The JCA algorithm name for RSA-OAEP-512. 065 */ 066 private static final String RSA_OEAP_512_JCA_ALG = "RSA/ECB/OAEPWithSHA-512AndMGF1Padding"; 067 068 069 /** 070 * The JCA algorithm name for SHA-256. 071 */ 072 private static final String SHA_256_JCA_ALG = "SHA-256"; 073 074 075 /** 076 * The JCA algorithm name for SHA-384. 077 */ 078 private static final String SHA_384_JCA_ALG = "SHA-384"; 079 080 /** 081 * The JCA algorithm name for SHA-512. 082 */ 083 private static final String SHA_512_JCA_ALG = "SHA-512"; 084 085 086 /** 087 * Encrypts the specified Content Encryption Key (CEK). 088 * 089 * @param pub The public RSA key. Must not be {@code null}. 090 * @param cek The Content Encryption Key (CEK) to encrypt. Must 091 * not be {@code null}. 092 * @param shaBitSize The SHA-2 bit size. Must be 256, 384 or 512. 093 * @param provider The JCA provider, {@code null} to use the 094 * default. 095 * 096 * @return The encrypted Content Encryption Key (CEK). 097 * 098 * @throws JOSEException If encryption failed. 099 */ 100 public static byte[] encryptCEK(final RSAPublicKey pub, 101 final SecretKey cek, 102 final int shaBitSize, 103 final Provider provider) 104 throws JOSEException { 105 106 final String jcaAlgName; 107 final String jcaShaAlgName; 108 final MGF1ParameterSpec mgf1ParameterSpec; 109 if (256 == shaBitSize) { 110 jcaAlgName = RSA_OEAP_256_JCA_ALG; 111 jcaShaAlgName = SHA_256_JCA_ALG; 112 mgf1ParameterSpec = MGF1ParameterSpec.SHA256; 113 } else if (384 == shaBitSize) { 114 jcaAlgName = RSA_OEAP_384_JCA_ALG; 115 jcaShaAlgName = SHA_384_JCA_ALG; 116 mgf1ParameterSpec = MGF1ParameterSpec.SHA384; 117 } else if (512 == shaBitSize) { 118 jcaAlgName = RSA_OEAP_512_JCA_ALG; 119 jcaShaAlgName = SHA_512_JCA_ALG; 120 mgf1ParameterSpec = MGF1ParameterSpec.SHA512; 121 } else { 122 throw new JOSEException("Unsupported SHA-2 bit size: " + shaBitSize); 123 } 124 125 try { 126 AlgorithmParameters algp = AlgorithmParametersHelper.getInstance("OAEP", provider); 127 AlgorithmParameterSpec paramSpec = new OAEPParameterSpec(jcaShaAlgName, "MGF1", mgf1ParameterSpec, PSource.PSpecified.DEFAULT); 128 algp.init(paramSpec); 129 Cipher cipher = CipherHelper.getInstance(jcaAlgName, provider); 130 cipher.init(Cipher.WRAP_MODE, pub, algp); 131 return cipher.wrap(cek); 132 133 } catch (InvalidKeyException e) { 134 throw new JOSEException("Encryption failed due to invalid RSA key for SHA-" + shaBitSize + ": " 135 + "The RSA key may be too short, use a longer key", e); 136 } catch (Exception e) { 137 // java.security.NoSuchAlgorithmException 138 // java.security.NoSuchPaddingException 139 // javax.crypto.IllegalBlockSizeException 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, {@code null} to use the 154 * default. 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.UNWRAP_MODE, priv, algp); 191 return (SecretKey) cipher.unwrap(encryptedCEK, "AES", Cipher.SECRET_KEY); 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}