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;
025
026import static com.nimbusds.jose.jwk.gen.RSAKeyGenerator.MIN_KEY_SIZE_BITS;
027
028import com.nimbusds.jose.JOSEException;
029import com.nimbusds.jose.JWSHeader;
030import com.nimbusds.jose.JWSSigner;
031import com.nimbusds.jose.crypto.impl.RSAKeyUtils;
032import com.nimbusds.jose.crypto.impl.RSASSA;
033import com.nimbusds.jose.crypto.impl.RSASSAProvider;
034import com.nimbusds.jose.jwk.RSAKey;
035import com.nimbusds.jose.util.Base64URL;
036import net.jcip.annotations.ThreadSafe;
037
038
039
040/**
041 * RSA Signature-Scheme-with-Appendix (RSASSA) signer of 
042 * {@link com.nimbusds.jose.JWSObject JWS objects}. Expects a private RSA key.
043 *
044 * <p>See RFC 7518, sections
045 * <a href="https://tools.ietf.org/html/rfc7518#section-3.3">3.3</a> and
046 * <a href="https://tools.ietf.org/html/rfc7518#section-3.5">3.5</a> for more
047 * information.
048 *
049 * <p>This class is thread-safe.
050 *
051 * <p>Supports the following algorithms:
052 *
053 * <ul>
054 *     <li>{@link com.nimbusds.jose.JWSAlgorithm#RS256}
055 *     <li>{@link com.nimbusds.jose.JWSAlgorithm#RS384}
056 *     <li>{@link com.nimbusds.jose.JWSAlgorithm#RS512}
057 *     <li>{@link com.nimbusds.jose.JWSAlgorithm#PS256}
058 *     <li>{@link com.nimbusds.jose.JWSAlgorithm#PS384}
059 *     <li>{@link com.nimbusds.jose.JWSAlgorithm#PS512}
060 * </ul>
061 *
062 * <p>Supports the
063 * {@link com.nimbusds.jose.crypto.bc.BouncyCastleFIPSProviderSingleton
064 * BouncyCastle FIPS provider} for the PSxxx family of JWS algorithms.
065 * 
066 * @author Vladimir Dzhuvinov
067 * @author Omer Levi Hevroni
068 * @version 2018-10-11
069 */
070@ThreadSafe
071public class RSASSASigner extends RSASSAProvider implements JWSSigner {
072
073
074        /**
075         * The private RSA key. Represented by generic private key interface to
076         * support key stores that prevent exposure of the private key
077         * parameters via the {@link java.security.interfaces.RSAPrivateKey}
078         * API.
079         *
080         * See https://bitbucket.org/connect2id/nimbus-jose-jwt/issues/169
081         */
082        private final PrivateKey privateKey;
083
084
085        /**
086         * Creates a new RSA Signature-Scheme-with-Appendix (RSASSA) signer.
087         * This constructor can also accept a private RSA key located in a
088         * PKCS#11 store that doesn't expose the private key parameters (such
089         * as a smart card or HSM).
090         *
091         * @param privateKey The private RSA key. Its algorithm must be "RSA"
092         *                   and its length at least 2048 bits. Note that the
093         *                   length of an RSA key in a PKCS#11 store cannot be
094         *                   checked. Must not be {@code null}.
095         */
096        public RSASSASigner(final PrivateKey privateKey) {
097
098                this(privateKey, false);
099        }
100
101
102        /**
103         * Creates a new RSA Signature-Scheme-with-Appendix (RSASSA) signer.
104         * This constructor can also accept a private RSA key located in a
105         * PKCS#11 store that doesn't expose the private key parameters (such
106         * as a smart card or HSM).
107         *
108         * @param privateKey   The private RSA key. Its algorithm must be
109         *                     "RSA" and its length at least 2048 bits. Note
110         *                     that the length of an RSA key in a PKCS#11 store
111         *                     cannot be checked. Must not be {@code null}.
112         * @param allowWeakKey {@code true} to allow an RSA key shorter than
113         *                     2048 bits.
114         */
115        public RSASSASigner(final PrivateKey privateKey, final boolean allowWeakKey) {
116
117                if (! "RSA".equalsIgnoreCase(privateKey.getAlgorithm())) {
118                        throw new IllegalArgumentException("The private key algorithm must be RSA");
119                }
120                
121                if (! allowWeakKey) {
122                        
123                        int keyBitLength = RSAKeyUtils.keyBitLength(privateKey);
124                        
125                        if (keyBitLength > 0 && keyBitLength < MIN_KEY_SIZE_BITS) {
126                                throw new IllegalArgumentException("The RSA key size must be at least " + MIN_KEY_SIZE_BITS + " bits");
127                        }
128                }
129
130                this.privateKey = privateKey;
131        }
132
133
134        /**
135         * Creates a new RSA Signature-Scheme-with-Appendix (RSASSA) signer.
136         *
137         * @param rsaJWK The RSA JSON Web Key (JWK). Must contain or reference
138         *               a private part. Its length must be at least 2048 bits.
139         *               Note that the length of an RSA key in a PKCS#11 store
140         *               cannot be checked. Must not be {@code null}.
141         *
142         * @throws JOSEException If the RSA JWK doesn't contain a private part
143         *                       or its extraction failed.
144         */
145        public RSASSASigner(final RSAKey rsaJWK)
146                throws JOSEException {
147
148                this(rsaJWK, false);
149        }
150
151
152        /**
153         * Creates a new RSA Signature-Scheme-with-Appendix (RSASSA) signer.
154         *
155         * @param rsaJWK       The RSA JSON Web Key (JWK). Must contain or
156         *                     reference a private part. Its length must be at
157         *                     least 2048 bits. Note that the length of an RSA
158         *                     key in a PKCS#11 store cannot be checked. Must
159         *                     not be {@code null}.
160         * @param allowWeakKey {@code true} to allow an RSA key shorter than
161         *                     2048 bits.
162         *
163         * @throws JOSEException If the RSA JWK doesn't contain a private part
164         *                       or its extraction failed.
165         */
166        public RSASSASigner(final RSAKey rsaJWK, final boolean allowWeakKey)
167                throws JOSEException {
168
169                this(RSAKeyUtils.toRSAPrivateKey(rsaJWK), allowWeakKey);
170        }
171
172
173        /**
174         * Gets the private RSA key.
175         *
176         * @return The private RSA key. Casting to
177         *         {@link java.security.interfaces.RSAPrivateKey} may not be
178         *         possible if the key is located in a PKCS#11 store that
179         *         doesn't expose the private key parameters.
180         */
181        public PrivateKey getPrivateKey() {
182
183                return privateKey;
184        }
185
186
187        @Override
188        public Base64URL sign(final JWSHeader header, final byte[] signingInput)
189                throws JOSEException {
190
191                Signature signer = RSASSA.getSignerAndVerifier(header.getAlgorithm(), getJCAContext().getProvider());
192
193                try {
194                        signer.initSign(privateKey);
195                        signer.update(signingInput);
196                        return Base64URL.encode(signer.sign());
197
198                } catch (InvalidKeyException e) {
199                        throw new JOSEException("Invalid private RSA key: " + e.getMessage(), e);
200
201                } catch (SignatureException e) {
202                        throw new JOSEException("RSA signature exception: " + e.getMessage(), e);
203                }
204        }
205}