001package com.nimbusds.jose.crypto;
002
003
004import java.security.InvalidKeyException;
005import java.security.Signature;
006import java.security.SignatureException;
007import java.security.interfaces.ECPrivateKey;
008
009import net.jcip.annotations.ThreadSafe;
010
011import com.nimbusds.jose.JOSEException;
012import com.nimbusds.jose.JWSAlgorithm;
013import com.nimbusds.jose.JWSHeader;
014import com.nimbusds.jose.JWSSigner;
015import com.nimbusds.jose.jwk.ECKey;
016import com.nimbusds.jose.util.Base64URL;
017
018
019/**
020 * Elliptic Curve Digital Signature Algorithm (ECDSA) signer of 
021 * {@link com.nimbusds.jose.JWSObject JWS objects}. This class is thread-safe.
022 *
023 * <p>Supports the following algorithms:
024 *
025 * <ul>
026 *     <li>{@link com.nimbusds.jose.JWSAlgorithm#ES256}
027 *     <li>{@link com.nimbusds.jose.JWSAlgorithm#ES384}
028 *     <li>{@link com.nimbusds.jose.JWSAlgorithm#ES512}
029 * </ul>
030 * 
031 * @author Axel Nennker
032 * @author Vladimir Dzhuvinov
033 * @version 2015-06-07
034 */
035@ThreadSafe
036public class ECDSASigner extends ECDSAProvider implements JWSSigner {
037
038
039        /**
040         * The private EC key.
041         */
042        private final ECPrivateKey privateKey;
043
044
045        /**
046         * Creates a new Elliptic Curve Digital Signature Algorithm (ECDSA) 
047         * signer.
048         *
049         * @param privateKey The private EC key. Must not be {@code null}.
050         *
051         * @throws JOSEException If the elliptic curve of key is not supported.
052         */
053        public ECDSASigner(final ECPrivateKey privateKey)
054                throws JOSEException {
055
056                super(ECDSA.resolveAlgorithm(privateKey));
057
058                this.privateKey = privateKey;
059        }
060
061
062        /**
063         * Creates a new Elliptic Curve Digital Signature Algorithm (ECDSA)
064         * signer.
065         *
066         * @param ecJWK The EC JSON Web Key (JWK). Must contain a private part.
067         *              Must not be {@code null}.
068         *
069         * @throws JOSEException If the EC JWK doesn't contain a private part,
070         *                       its extraction failed, or the elliptic curve
071         *                       is not supported.
072         */
073        public ECDSASigner(final ECKey ecJWK)
074                throws JOSEException {
075
076                super(ECDSA.resolveAlgorithm(ecJWK.getCurve()));
077
078                if (! ecJWK.isPrivate()) {
079                        throw new JOSEException("The EC JWK doesn't contain a private part");
080                }
081
082                privateKey = ecJWK.toECPrivateKey();
083        }
084
085
086        /**
087         * Returns the private EC key.
088         *
089         * @return The private EC key.
090         */
091        public ECPrivateKey getPrivateKey() {
092
093                return privateKey;
094        }
095
096
097        @Override
098        public Base64URL sign(final JWSHeader header, final byte[] signingInput)
099                throws JOSEException {
100
101                final JWSAlgorithm alg = header.getAlgorithm();
102
103                if (! supportedJWSAlgorithms().contains(alg)) {
104                        throw new JOSEException(AlgorithmSupportMessage.unsupportedJWSAlgorithm(alg, supportedJWSAlgorithms()));
105                }
106
107                // DER-encoded signature, according to JCA spec
108                // (sequence of two integers - R + S)
109                final byte[] jcaSignature;
110
111                try {
112                        Signature dsa = ECDSA.getSignerAndVerifier(alg, getJCAContext().getProvider());
113                        dsa.initSign(privateKey, getJCAContext().getSecureRandom());
114                        dsa.update(signingInput);
115                        jcaSignature = dsa.sign();
116
117                } catch (InvalidKeyException | SignatureException e) {
118
119                        throw new JOSEException(e.getMessage(), e);
120                }
121
122                final int rsByteArrayLength = ECDSA.getSignatureByteArrayLength(header.getAlgorithm());
123                final byte[] jwsSignature = ECDSA.transcodeSignatureToConcat(jcaSignature, rsByteArrayLength);
124                return Base64URL.encode(jwsSignature);
125        }
126}