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.InvalidKeyException;
022import java.security.PrivateKey;
023import java.security.Signature;
024import java.security.SignatureException;
025import java.security.interfaces.RSAPrivateKey;
026import java.util.Collections;
027import java.util.Set;
028
029import static com.nimbusds.jose.jwk.gen.RSAKeyGenerator.MIN_KEY_SIZE_BITS;
030
031import net.jcip.annotations.ThreadSafe;
032
033import com.nimbusds.jose.*;
034import com.nimbusds.jose.crypto.impl.RSAKeyUtils;
035import com.nimbusds.jose.crypto.impl.RSASSA;
036import com.nimbusds.jose.crypto.impl.RSASSAProvider;
037import com.nimbusds.jose.crypto.opts.AllowWeakRSAKey;
038import com.nimbusds.jose.crypto.opts.OptionUtils;
039import com.nimbusds.jose.crypto.opts.UserAuthenticationRequired;
040import com.nimbusds.jose.jwk.RSAKey;
041import com.nimbusds.jose.util.Base64URL;
042
043
044
045/**
046 * RSA Signature-Scheme-with-Appendix (RSASSA) signer of 
047 * {@link com.nimbusds.jose.JWSObject JWS objects}. Expects a private RSA key.
048 *
049 * <p>See RFC 7518, sections
050 * <a href="https://tools.ietf.org/html/rfc7518#section-3.3">3.3</a> and
051 * <a href="https://tools.ietf.org/html/rfc7518#section-3.5">3.5</a> for more
052 * information.
053 *
054 * <p>This class is thread-safe.
055 *
056 * <p>Supports the following algorithms:
057 *
058 * <ul>
059 *     <li>{@link com.nimbusds.jose.JWSAlgorithm#RS256}
060 *     <li>{@link com.nimbusds.jose.JWSAlgorithm#RS384}
061 *     <li>{@link com.nimbusds.jose.JWSAlgorithm#RS512}
062 *     <li>{@link com.nimbusds.jose.JWSAlgorithm#PS256}
063 *     <li>{@link com.nimbusds.jose.JWSAlgorithm#PS384}
064 *     <li>{@link com.nimbusds.jose.JWSAlgorithm#PS512}
065 * </ul>
066 *
067 * <p>Supports the following {@link JWSSignerOption options}:
068 *
069 * <ul>
070 *     <li>{@link UserAuthenticationRequired} -- to prompt the user to
071 *         authenticate in order to complete the signing operation. Android
072 *         applications can use this option to trigger a biometric prompt that
073 *         is required to unlock a private key created with
074 *         {@code setUserAuthenticationRequired(true)}.
075 *     <li>{@link AllowWeakRSAKey} -- to allow weak RSA keys that are shorter
076 *         than {@link com.nimbusds.jose.jwk.gen.RSAKeyGenerator#MIN_KEY_SIZE_BITS
077 *         2048 bits}
078 * </ul>
079 *
080 * <p>Supports the
081 * {@link com.nimbusds.jose.crypto.bc.BouncyCastleFIPSProviderSingleton
082 * BouncyCastle FIPS provider} for the PSxxx family of JWS algorithms.
083 * 
084 * @author Vladimir Dzhuvinov
085 * @author Omer Levi Hevroni
086 * @version 2023-01-31
087 */
088@ThreadSafe
089public class RSASSASigner extends RSASSAProvider implements JWSSigner {
090
091
092        /**
093         * The private RSA key. Represented by generic private key interface to
094         * support key stores that prevent exposure of the private key
095         * parameters via the {@link java.security.interfaces.RSAPrivateKey}
096         * API.
097         *
098         * See https://bitbucket.org/connect2id/nimbus-jose-jwt/issues/169
099         */
100        private final PrivateKey privateKey;
101        
102        
103        /**
104         * The configured options, empty set if none.
105         */
106        private final Set<JWSSignerOption> opts;
107
108
109        /**
110         * Creates a new RSA Signature-Scheme-with-Appendix (RSASSA) signer.
111         * This constructor can also accept a private RSA key located in a
112         * PKCS#11 store that doesn't expose the private key parameters (such
113         * as a smart card or HSM).
114         *
115         * @param privateKey The private RSA key. Its algorithm must be "RSA"
116         *                   and its length at least 2048 bits. Note that the
117         *                   length of an RSA key in a PKCS#11 store cannot be
118         *                   checked. Must not be {@code null}.
119         */
120        public RSASSASigner(final PrivateKey privateKey) {
121
122                this(privateKey, false);
123        }
124
125
126        /**
127         * Creates a new RSA Signature-Scheme-with-Appendix (RSASSA) signer.
128         * This constructor can also accept a private RSA key located in a
129         * PKCS#11 store that doesn't expose the private key parameters (such
130         * as a smart card or HSM).
131         *
132         * @param privateKey   The private RSA key. Its algorithm must be
133         *                     "RSA" and its length at least 2048 bits. Note
134         *                     that the length of an RSA key in a PKCS#11 store
135         *                     cannot be checked. Must not be {@code null}.
136         * @param allowWeakKey {@code true} to allow an RSA key shorter than
137         *                     2048 bits.
138         */
139        @Deprecated
140        public RSASSASigner(final PrivateKey privateKey, final boolean allowWeakKey) {
141
142                this(privateKey, allowWeakKey ? Collections.singleton((JWSSignerOption) AllowWeakRSAKey.getInstance()) : Collections.<JWSSignerOption>emptySet());
143        }
144
145
146        /**
147         * Creates a new RSA Signature-Scheme-with-Appendix (RSASSA) signer.
148         * This constructor can also accept a private RSA key located in a
149         * PKCS#11 store that doesn't expose the private key parameters (such
150         * as a smart card or HSM).
151         *
152         * @param privateKey The private RSA key. Its algorithm must be "RSA"
153         *                   and its length at least 2048 bits. Note that the
154         *                   length of an RSA key in a PKCS#11 store cannot be
155         *                   checked. Must not be {@code null}.
156         * @param opts       The signing options, empty or {@code null} if
157         *                   none.
158         */
159        public RSASSASigner(final PrivateKey privateKey, final Set<JWSSignerOption> opts) {
160                
161                if (privateKey instanceof RSAPrivateKey || "RSA".equalsIgnoreCase(privateKey.getAlgorithm())) {
162                        // Will also allow "RSASSA-PSS" alg RSAPrivateKey instances with MGF1ParameterSpec
163                        this.privateKey = privateKey;
164                } else {
165                        throw new IllegalArgumentException("The private key algorithm must be RSA");
166                } 
167                
168                this.opts = opts != null ? opts : Collections.<JWSSignerOption>emptySet();
169                
170                if (! OptionUtils.optionIsPresent(this.opts, AllowWeakRSAKey.class)) {
171                        int keyBitLength = RSAKeyUtils.keyBitLength(privateKey);
172                        
173                        if (keyBitLength > 0 && keyBitLength < MIN_KEY_SIZE_BITS) {
174                                throw new IllegalArgumentException("The RSA key size must be at least " + MIN_KEY_SIZE_BITS + " bits");
175                        }
176                }
177        }
178
179
180        /**
181         * Creates a new RSA Signature-Scheme-with-Appendix (RSASSA) signer.
182         *
183         * @param rsaJWK The RSA JSON Web Key (JWK). Must contain or reference
184         *               a private part. Its length must be at least 2048 bits.
185         *               Note that the length of an RSA key in a PKCS#11 store
186         *               cannot be checked. Must not be {@code null}.
187         *
188         * @throws JOSEException If the RSA JWK doesn't contain a private part
189         *                       or its extraction failed.
190         */
191        public RSASSASigner(final RSAKey rsaJWK)
192                throws JOSEException {
193
194                this(rsaJWK, null);
195        }
196
197
198        /**
199         * Creates a new RSA Signature-Scheme-with-Appendix (RSASSA) signer.
200         *
201         * @param rsaJWK       The RSA JSON Web Key (JWK). Must contain or
202         *                     reference a private part. Its length must be at
203         *                     least 2048 bits. Note that the length of an RSA
204         *                     key in a PKCS#11 store cannot be checked. Must
205         *                     not be {@code null}.
206         * @param allowWeakKey {@code true} to allow an RSA key shorter than
207         *                     2048 bits.
208         *
209         * @throws JOSEException If the RSA JWK doesn't contain a private part
210         *                       or its extraction failed.
211         */
212        @Deprecated
213        public RSASSASigner(final RSAKey rsaJWK, final boolean allowWeakKey)
214                throws JOSEException {
215
216                this(RSAKeyUtils.toRSAPrivateKey(rsaJWK), allowWeakKey);
217        }
218        
219        
220        /**
221         * Creates a new RSA Signature-Scheme-with-Appendix (RSASSA) signer.
222         *
223         * @param rsaJWK The RSA JSON Web Key (JWK). Must contain or reference
224         *               a private part. Its length must be at least 2048 bits.
225         *               Note that the length of an RSA key in a PKCS#11 store
226         *               cannot be checked. Must not be {@code null}.
227         * @param opts   The signing options, empty or {@code null} if
228         *               none.
229         *
230         * @throws JOSEException If the RSA JWK doesn't contain a private part
231         *                       or its extraction failed.
232         */
233        public RSASSASigner(final RSAKey rsaJWK, final Set<JWSSignerOption> opts)
234                throws JOSEException {
235                
236                this(RSAKeyUtils.toRSAPrivateKey(rsaJWK), opts);
237        }
238
239
240        /**
241         * Gets the private RSA key.
242         *
243         * @return The private RSA key. Casting to
244         *         {@link java.security.interfaces.RSAPrivateKey} may not be
245         *         possible if the key is located in a PKCS#11 store that
246         *         doesn't expose the private key parameters.
247         */
248        public PrivateKey getPrivateKey() {
249
250                return privateKey;
251        }
252
253
254        @Override
255        public Base64URL sign(final JWSHeader header, final byte[] signingInput)
256                throws JOSEException {
257
258                final Signature signer = getInitiatedSignature(header);
259                
260                if (OptionUtils.optionIsPresent(opts, UserAuthenticationRequired.class)) {
261                        
262                        throw new ActionRequiredForJWSCompletionException(
263                                "Authenticate user to complete signing",
264                                UserAuthenticationRequired.getInstance(),
265                                new CompletableJWSObjectSigning() {
266
267                                        @Override
268                                        public Signature getInitializedSignature() {
269                                                return signer;
270                                        }
271
272                                        @Override
273                                        public Base64URL complete() throws JOSEException {
274                                                return sign(signingInput, signer);
275                                        }
276                                }
277                        );
278                }
279                
280                return sign(signingInput, signer);
281        }
282        
283        
284        private Signature getInitiatedSignature(final JWSHeader header)
285                throws JOSEException {
286                
287                Signature signer = RSASSA.getSignerAndVerifier(header.getAlgorithm(), getJCAContext().getProvider());
288                try {
289                        signer.initSign(privateKey);
290                } catch (InvalidKeyException e) {
291                        throw new JOSEException("Invalid private RSA key: " + e.getMessage(), e);
292                }
293                
294                return signer;
295        }
296        
297        
298        private Base64URL sign(final byte[] signingInput, final Signature signer)
299                throws JOSEException {
300                
301                try {
302                        signer.update(signingInput);
303                        return Base64URL.encode(signer.sign());
304                } catch (SignatureException e) {
305                        throw new JOSEException("RSA signature exception: " + e.getMessage(), e);
306                }
307        }
308}