001package com.nimbusds.jose.crypto;
002
003
004import java.security.InvalidKeyException;
005import java.security.PrivateKey;
006import java.security.Signature;
007import java.security.SignatureException;
008
009import com.nimbusds.jose.JOSEException;
010import com.nimbusds.jose.JWSHeader;
011import com.nimbusds.jose.JWSSigner;
012import com.nimbusds.jose.jwk.RSAKey;
013import com.nimbusds.jose.util.Base64URL;
014import net.jcip.annotations.ThreadSafe;
015
016
017
018/**
019 * RSA Signature-Scheme-with-Appendix (RSASSA) signer of 
020 * {@link com.nimbusds.jose.JWSObject JWS objects}. Expects a private RSA key.
021 *
022 * <p>See RFC 7518, sections
023 * <a href="https://tools.ietf.org/html/rfc7518#section-3.3">3.3</a> and
024 * <a href="https://tools.ietf.org/html/rfc7518#section-3.5">3.5</a> for more
025 * information.
026 *
027 * <p>This class is thread-safe.
028 *
029 * <p>Supports the following algorithms:
030 *
031 * <ul>
032 *     <li>{@link com.nimbusds.jose.JWSAlgorithm#RS256}
033 *     <li>{@link com.nimbusds.jose.JWSAlgorithm#RS384}
034 *     <li>{@link com.nimbusds.jose.JWSAlgorithm#RS512}
035 *     <li>{@link com.nimbusds.jose.JWSAlgorithm#PS256}
036 *     <li>{@link com.nimbusds.jose.JWSAlgorithm#PS384}
037 *     <li>{@link com.nimbusds.jose.JWSAlgorithm#PS512}
038 * </ul>
039 * 
040 * @author Vladimir Dzhuvinov
041 * @author Omer Levi Hevroni
042 * @version 2016-04-04
043 */
044@ThreadSafe
045public class RSASSASigner extends RSASSAProvider implements JWSSigner {
046
047
048        /**
049         * The private RSA key. Represented by generic private key interface to
050         * support key stores that prevent exposure of the private key
051         * parameters via the {@link java.security.interfaces.RSAPrivateKey}
052         * API.
053         *
054         * See https://bitbucket.org/connect2id/nimbus-jose-jwt/issues/169
055         */
056        private final PrivateKey privateKey;
057
058
059        /**
060         * Creates a new RSA Signature-Scheme-with-Appendix (RSASSA) signer.
061         *
062         * @param privateKey The private RSA key. Its algorithm must be "RSA".
063         *                   Must not be {@code null}.
064         */
065        public RSASSASigner(final PrivateKey privateKey) {
066
067                if (! privateKey.getAlgorithm().equalsIgnoreCase("RSA")) {
068                        throw new IllegalArgumentException("The private key algorithm must be RSA");
069                }
070
071                this.privateKey = privateKey;
072        }
073
074
075        /**
076         * Creates a new RSA Signature-Scheme-with-Appendix (RSASSA) signer.
077         *
078         * @param rsaJWK The RSA JSON Web Key (JWK). Must contain a private
079         *               part. Must not be {@code null}.
080         *
081         * @throws JOSEException If the RSA JWK doesn't contain a private part
082         *                       or its extraction failed.
083         */
084        public RSASSASigner(final RSAKey rsaJWK)
085                throws JOSEException {
086
087                if (! rsaJWK.isPrivate()) {
088                        throw new JOSEException("The RSA JWK doesn't contain a private part");
089                }
090
091                privateKey = rsaJWK.toRSAPrivateKey();
092        }
093
094
095        /**
096         * Gets the private RSA key.
097         *
098         * @return The private RSA key. Casting to
099         *         {@link java.security.interfaces.RSAPrivateKey} may not be
100         *         possible if the key is backed by a key store that doesn't
101         *         expose the private key parameters.
102         */
103        public PrivateKey getPrivateKey() {
104
105                return privateKey;
106        }
107
108
109        @Override
110        public Base64URL sign(final JWSHeader header, final byte[] signingInput)
111                throws JOSEException {
112
113                Signature signer = RSASSA.getSignerAndVerifier(header.getAlgorithm(), getJCAContext().getProvider());
114
115                try {
116                        signer.initSign(privateKey);
117                        signer.update(signingInput);
118                        return Base64URL.encode(signer.sign());
119
120                } catch (InvalidKeyException e) {
121                        throw new JOSEException("Invalid private RSA key: " + e.getMessage(), e);
122
123                } catch (SignatureException e) {
124                        throw new JOSEException("RSA signature exception: " + e.getMessage(), e);
125                }
126        }
127}