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 BouncyCastle FIPS provider for the PSxxx family of JWS algorithms.
081 * 
082 * @author Vladimir Dzhuvinov
083 * @author Omer Levi Hevroni
084 * @version 2023-01-31
085 */
086@ThreadSafe
087public class RSASSASigner extends RSASSAProvider implements JWSSigner {
088
089
090        /**
091         * The private RSA key. Represented by generic private key interface to
092         * support key stores that prevent exposure of the private key
093         * parameters via the {@link java.security.interfaces.RSAPrivateKey}
094         * API.
095         *
096         * See https://bitbucket.org/connect2id/nimbus-jose-jwt/issues/169
097         */
098        private final PrivateKey privateKey;
099        
100        
101        /**
102         * The configured options, empty set if none.
103         */
104        private final Set<JWSSignerOption> opts;
105
106
107        /**
108         * Creates a new RSA Signature-Scheme-with-Appendix (RSASSA) signer.
109         * This constructor can also accept a private RSA key located in a
110         * PKCS#11 store that doesn't expose the private key parameters (such
111         * as a smart card or HSM).
112         *
113         * @param privateKey The private RSA key. Its algorithm must be "RSA"
114         *                   and its length at least 2048 bits. Note that the
115         *                   length of an RSA key in a PKCS#11 store cannot be
116         *                   checked. Must not be {@code null}.
117         */
118        public RSASSASigner(final PrivateKey privateKey) {
119
120                this(privateKey, false);
121        }
122
123
124        /**
125         * Creates a new RSA Signature-Scheme-with-Appendix (RSASSA) signer.
126         * This constructor can also accept a private RSA key located in a
127         * PKCS#11 store that doesn't expose the private key parameters (such
128         * as a smart card or HSM).
129         *
130         * @param privateKey   The private RSA key. Its algorithm must be
131         *                     "RSA" and its length at least 2048 bits. Note
132         *                     that the length of an RSA key in a PKCS#11 store
133         *                     cannot be checked. Must not be {@code null}.
134         * @param allowWeakKey {@code true} to allow an RSA key shorter than
135         *                     2048 bits.
136         */
137        @Deprecated
138        public RSASSASigner(final PrivateKey privateKey, final boolean allowWeakKey) {
139
140                this(privateKey, allowWeakKey ? Collections.singleton((JWSSignerOption) AllowWeakRSAKey.getInstance()) : Collections.<JWSSignerOption>emptySet());
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                
168                if (! OptionUtils.optionIsPresent(this.opts, AllowWeakRSAKey.class)) {
169                        int keyBitLength = RSAKeyUtils.keyBitLength(privateKey);
170                        
171                        if (keyBitLength > 0 && keyBitLength < MIN_KEY_SIZE_BITS) {
172                                throw new IllegalArgumentException("The RSA key size must be at least " + MIN_KEY_SIZE_BITS + " bits");
173                        }
174                }
175        }
176
177
178        /**
179         * Creates a new RSA Signature-Scheme-with-Appendix (RSASSA) signer.
180         *
181         * @param rsaJWK The RSA JSON Web Key (JWK). Must contain or reference
182         *               a private part. Its length must be at least 2048 bits.
183         *               Note that the length of an RSA key in a PKCS#11 store
184         *               cannot be checked. Must not be {@code null}.
185         *
186         * @throws JOSEException If the RSA JWK doesn't contain a private part
187         *                       or its extraction failed.
188         */
189        public RSASSASigner(final RSAKey rsaJWK)
190                throws JOSEException {
191
192                this(rsaJWK, null);
193        }
194
195
196        /**
197         * Creates a new RSA Signature-Scheme-with-Appendix (RSASSA) signer.
198         *
199         * @param rsaJWK       The RSA JSON Web Key (JWK). Must contain or
200         *                     reference a private part. Its length must be at
201         *                     least 2048 bits. Note that the length of an RSA
202         *                     key in a PKCS#11 store cannot be checked. Must
203         *                     not be {@code null}.
204         * @param allowWeakKey {@code true} to allow an RSA key shorter than
205         *                     2048 bits.
206         *
207         * @throws JOSEException If the RSA JWK doesn't contain a private part
208         *                       or its extraction failed.
209         */
210        @Deprecated
211        public RSASSASigner(final RSAKey rsaJWK, final boolean allowWeakKey)
212                throws JOSEException {
213
214                this(RSAKeyUtils.toRSAPrivateKey(rsaJWK), allowWeakKey);
215        }
216        
217        
218        /**
219         * Creates a new RSA Signature-Scheme-with-Appendix (RSASSA) signer.
220         *
221         * @param rsaJWK The RSA JSON Web Key (JWK). Must contain or reference
222         *               a private part. Its length must be at least 2048 bits.
223         *               Note that the length of an RSA key in a PKCS#11 store
224         *               cannot be checked. Must not be {@code null}.
225         * @param opts   The signing options, empty or {@code null} if
226         *               none.
227         *
228         * @throws JOSEException If the RSA JWK doesn't contain a private part
229         *                       or its extraction failed.
230         */
231        public RSASSASigner(final RSAKey rsaJWK, final Set<JWSSignerOption> opts)
232                throws JOSEException {
233                
234                this(RSAKeyUtils.toRSAPrivateKey(rsaJWK), opts);
235        }
236
237
238        /**
239         * Gets the private RSA key.
240         *
241         * @return The private RSA key. Casting to
242         *         {@link java.security.interfaces.RSAPrivateKey} may not be
243         *         possible if the key is located in a PKCS#11 store that
244         *         doesn't expose the private key parameters.
245         */
246        public PrivateKey getPrivateKey() {
247
248                return privateKey;
249        }
250
251
252        @Override
253        public Base64URL sign(final JWSHeader header, final byte[] signingInput)
254                throws JOSEException {
255
256                final Signature signer = getInitiatedSignature(header);
257                
258                if (OptionUtils.optionIsPresent(opts, UserAuthenticationRequired.class)) {
259                        
260                        throw new ActionRequiredForJWSCompletionException(
261                                "Authenticate user to complete signing",
262                                UserAuthenticationRequired.getInstance(),
263                                new CompletableJWSObjectSigning() {
264
265                                        @Override
266                                        public Signature getInitializedSignature() {
267                                                return signer;
268                                        }
269
270                                        @Override
271                                        public Base64URL complete() throws JOSEException {
272                                                return sign(signingInput, signer);
273                                        }
274                                }
275                        );
276                }
277                
278                return sign(signingInput, signer);
279        }
280        
281        
282        private Signature getInitiatedSignature(final JWSHeader header)
283                throws JOSEException {
284                
285                Signature signer = RSASSA.getSignerAndVerifier(header.getAlgorithm(), getJCAContext().getProvider());
286                try {
287                        signer.initSign(privateKey);
288                } catch (InvalidKeyException e) {
289                        throw new JOSEException("Invalid private RSA key: " + e.getMessage(), e);
290                }
291                
292                return signer;
293        }
294        
295        
296        private Base64URL sign(final byte[] signingInput, final Signature signer)
297                throws JOSEException {
298                
299                try {
300                        signer.update(signingInput);
301                        return Base64URL.encode(signer.sign());
302                } catch (SignatureException e) {
303                        throw new JOSEException("RSA signature exception: " + e.getMessage(), e);
304                }
305        }
306}