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.Signature;
023import java.security.SignatureException;
024import java.security.interfaces.ECPrivateKey;
025
026import net.jcip.annotations.ThreadSafe;
027
028import com.nimbusds.jose.JOSEException;
029import com.nimbusds.jose.JWSAlgorithm;
030import com.nimbusds.jose.JWSHeader;
031import com.nimbusds.jose.JWSSigner;
032import com.nimbusds.jose.jwk.ECKey;
033import com.nimbusds.jose.util.Base64URL;
034
035
036/**
037 * Elliptic Curve Digital Signature Algorithm (ECDSA) signer of 
038 * {@link com.nimbusds.jose.JWSObject JWS objects}. Expects a private EC key
039 * (with a P-256, P-384 or P-521 curve).
040 *
041 * <p>See RFC 7518
042 * <a href="https://tools.ietf.org/html/rfc7518#section-3.4">section 3.4</a>
043 * for more information.
044 *
045 * <p>This class is thread-safe.
046 *
047 * <p>Supports the following algorithms:
048 *
049 * <ul>
050 *     <li>{@link com.nimbusds.jose.JWSAlgorithm#ES256}
051 *     <li>{@link com.nimbusds.jose.JWSAlgorithm#ES384}
052 *     <li>{@link com.nimbusds.jose.JWSAlgorithm#ES512}
053 * </ul>
054 * 
055 * @author Axel Nennker
056 * @author Vladimir Dzhuvinov
057 * @version 2015-06-07
058 */
059@ThreadSafe
060public class ECDSASigner extends ECDSAProvider implements JWSSigner {
061
062
063        /**
064         * The private EC key.
065         */
066        private final ECPrivateKey privateKey;
067
068
069        /**
070         * Creates a new Elliptic Curve Digital Signature Algorithm (ECDSA) 
071         * signer.
072         *
073         * @param privateKey The private EC key. Must not be {@code null}.
074         *
075         * @throws JOSEException If the elliptic curve of key is not supported.
076         */
077        public ECDSASigner(final ECPrivateKey privateKey)
078                throws JOSEException {
079
080                super(ECDSA.resolveAlgorithm(privateKey));
081
082                this.privateKey = privateKey;
083        }
084
085
086        /**
087         * Creates a new Elliptic Curve Digital Signature Algorithm (ECDSA)
088         * signer.
089         *
090         * @param ecJWK The EC JSON Web Key (JWK). Must contain a private part.
091         *              Must not be {@code null}.
092         *
093         * @throws JOSEException If the EC JWK doesn't contain a private part,
094         *                       its extraction failed, or the elliptic curve
095         *                       is not supported.
096         */
097        public ECDSASigner(final ECKey ecJWK)
098                throws JOSEException {
099
100                super(ECDSA.resolveAlgorithm(ecJWK.getCurve()));
101
102                if (! ecJWK.isPrivate()) {
103                        throw new JOSEException("The EC JWK doesn't contain a private part");
104                }
105
106                privateKey = ecJWK.toECPrivateKey();
107        }
108
109
110        /**
111         * Returns the private EC key.
112         *
113         * @return The private EC key.
114         */
115        public ECPrivateKey getPrivateKey() {
116
117                return privateKey;
118        }
119
120
121        @Override
122        public Base64URL sign(final JWSHeader header, final byte[] signingInput)
123                throws JOSEException {
124
125                final JWSAlgorithm alg = header.getAlgorithm();
126
127                if (! supportedJWSAlgorithms().contains(alg)) {
128                        throw new JOSEException(AlgorithmSupportMessage.unsupportedJWSAlgorithm(alg, supportedJWSAlgorithms()));
129                }
130
131                // DER-encoded signature, according to JCA spec
132                // (sequence of two integers - R + S)
133                final byte[] jcaSignature;
134
135                try {
136                        Signature dsa = ECDSA.getSignerAndVerifier(alg, getJCAContext().getProvider());
137                        dsa.initSign(privateKey, getJCAContext().getSecureRandom());
138                        dsa.update(signingInput);
139                        jcaSignature = dsa.sign();
140
141                } catch (InvalidKeyException | SignatureException e) {
142
143                        throw new JOSEException(e.getMessage(), e);
144                }
145
146                final int rsByteArrayLength = ECDSA.getSignatureByteArrayLength(header.getAlgorithm());
147                final byte[] jwsSignature = ECDSA.transcodeSignatureToConcat(jcaSignature, rsByteArrayLength);
148                return Base64URL.encode(jwsSignature);
149        }
150}