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