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