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