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}