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}