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. Its algorithm must be "RSA".
056         *                   Must not be {@code null}.
057         */
058        public RSASSASigner(final PrivateKey privateKey) {
059
060                if (! privateKey.getAlgorithm().equalsIgnoreCase("RSA")) {
061                        throw new IllegalArgumentException("The private key algorithm must be RSA");
062                }
063
064                this.privateKey = privateKey;
065        }
066
067
068        /**
069         * Creates a new RSA Signature-Scheme-with-Appendix (RSASSA) signer.
070         *
071         * @param rsaJWK The RSA JSON Web Key (JWK). Must contain a private
072         *               part. Must not be {@code null}.
073         *
074         * @throws JOSEException If the RSA JWK doesn't contain a private part
075         *                       or its extraction failed.
076         */
077        public RSASSASigner(final RSAKey rsaJWK)
078                throws JOSEException {
079
080                if (! rsaJWK.isPrivate()) {
081                        throw new JOSEException("The RSA JWK doesn't contain a private part");
082                }
083
084                privateKey = rsaJWK.toRSAPrivateKey();
085        }
086
087
088        /**
089         * Gets the private RSA key.
090         *
091         * @return The private RSA key. Casting to
092         *         {@link java.security.interfaces.RSAPrivateKey} may not be
093         *         possible if the key is backed by a key store that doesn't
094         *         expose the private key parameters.
095         */
096        public PrivateKey getPrivateKey() {
097
098                return privateKey;
099        }
100
101
102        @Override
103        public Base64URL sign(final JWSHeader header, final byte[] signingInput)
104                throws JOSEException {
105
106                Signature signer = RSASSA.getSignerAndVerifier(header.getAlgorithm(), getJCAContext().getProvider());
107
108                try {
109                        signer.initSign(privateKey);
110                        signer.update(signingInput);
111                        return Base64URL.encode(signer.sign());
112
113                } catch (InvalidKeyException e) {
114                        throw new JOSEException("Invalid private RSA key: " + e.getMessage(), e);
115
116                } catch (SignatureException e) {
117                        throw new JOSEException("RSA signature exception: " + e.getMessage(), e);
118                }
119        }
120}