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-04-04
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         *
079         * @param privateKey The private RSA key. Its algorithm must be "RSA".
080         *                   Must not be {@code null}.
081         */
082        public RSASSASigner(final PrivateKey privateKey) {
083
084                if (! privateKey.getAlgorithm().equalsIgnoreCase("RSA")) {
085                        throw new IllegalArgumentException("The private key algorithm must be RSA");
086                }
087
088                this.privateKey = privateKey;
089        }
090
091
092        /**
093         * Creates a new RSA Signature-Scheme-with-Appendix (RSASSA) signer.
094         *
095         * @param rsaJWK The RSA JSON Web Key (JWK). Must contain a private
096         *               part. Must not be {@code null}.
097         *
098         * @throws JOSEException If the RSA JWK doesn't contain a private part
099         *                       or its extraction failed.
100         */
101        public RSASSASigner(final RSAKey rsaJWK)
102                throws JOSEException {
103
104                if (! rsaJWK.isPrivate()) {
105                        throw new JOSEException("The RSA JWK doesn't contain a private part");
106                }
107
108                privateKey = rsaJWK.toRSAPrivateKey();
109        }
110
111
112        /**
113         * Gets the private RSA key.
114         *
115         * @return The private RSA key. Casting to
116         *         {@link java.security.interfaces.RSAPrivateKey} may not be
117         *         possible if the key is backed by a key store that doesn't
118         *         expose the private key parameters.
119         */
120        public PrivateKey getPrivateKey() {
121
122                return privateKey;
123        }
124
125
126        @Override
127        public Base64URL sign(final JWSHeader header, final byte[] signingInput)
128                throws JOSEException {
129
130                Signature signer = RSASSA.getSignerAndVerifier(header.getAlgorithm(), getJCAContext().getProvider());
131
132                try {
133                        signer.initSign(privateKey);
134                        signer.update(signingInput);
135                        return Base64URL.encode(signer.sign());
136
137                } catch (InvalidKeyException e) {
138                        throw new JOSEException("Invalid private RSA key: " + e.getMessage(), e);
139
140                } catch (SignatureException e) {
141                        throw new JOSEException("RSA signature exception: " + e.getMessage(), e);
142                }
143        }
144}