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