001package com.nimbusds.jose.crypto;
002
003
004import java.math.BigInteger;
005
006import net.jcip.annotations.ThreadSafe;
007
008import org.bouncycastle.asn1.x9.X9ECParameters;
009import org.bouncycastle.crypto.Digest;
010import org.bouncycastle.crypto.params.ECDomainParameters;
011import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
012
013import com.nimbusds.jose.JOSEException;
014import com.nimbusds.jose.JWSSigner;
015import com.nimbusds.jose.JWSHeader;
016import com.nimbusds.jose.util.Base64URL;
017import com.nimbusds.jose.util.BigIntegerUtils;
018
019
020/**
021 * Elliptic Curve Digital Signature Algorithm (ECDSA) signer of 
022 * {@link com.nimbusds.jose.JWSObject JWS objects}. This class is thread-safe.
023 *
024 * <p>Supports the following JSON Web Algorithms (JWAs):
025 *
026 * <ul>
027 *     <li>{@link com.nimbusds.jose.JWSAlgorithm#ES256}
028 *     <li>{@link com.nimbusds.jose.JWSAlgorithm#ES384}
029 *     <li>{@link com.nimbusds.jose.JWSAlgorithm#ES512}
030 * </ul>
031 * 
032 * @author Axel Nennker
033 * @author Vladimir Dzhuvinov
034 * @version $version$ (2014-07-08)
035 */
036@ThreadSafe
037public class ECDSASigner extends ECDSAProvider implements JWSSigner {
038
039
040        /**
041         * The private key.
042         */
043        private final BigInteger privateKey;
044
045
046        /**
047         * Creates a new Elliptic Curve Digital Signature Algorithm (ECDSA) 
048         * signer.
049         *
050         * @param privateKey The private key ('d' parameter). Must not be 
051         *                   {@code null}.
052         */
053        public ECDSASigner(final BigInteger privateKey) {
054
055                if (privateKey == null) {
056
057                        throw new IllegalArgumentException("The private key must not be null");
058                }
059
060                this.privateKey = privateKey;
061        }
062
063
064        /**
065         * Gets the private key ('d' parameter).
066         *
067         * @return The private key.
068         */
069        public BigInteger getPrivateKey() {
070
071                return privateKey;
072        }
073
074
075        /**
076         * Performs the actual ECDSA signing.
077         *
078         * @param ecPrivateKeyParameters The EC private key parameters. Must 
079         *                               not be {@code null}.
080         * @param bytes                  The byte array to sign. Must not be 
081         *                               {@code null}.
082         *
083         * @return The ECDSA signature parts R and S.
084         */
085        private static BigInteger[] doECDSA(final ECPrivateKeyParameters ecPrivateKeyParameters, 
086                                            final byte[] bytes) {
087
088                org.bouncycastle.crypto.signers.ECDSASigner signer = 
089                        new org.bouncycastle.crypto.signers.ECDSASigner();
090
091                signer.init(true, ecPrivateKeyParameters);
092                
093                return signer.generateSignature(bytes);
094        }
095
096
097        /**
098         * Converts the specified big integers to byte arrays and returns their
099         * array concatenation.
100         *
101         * @param r                 The R parameter. Must not be {@code null}.
102         * @param s                 The S parameter. Must not be {@code null}.
103         * @param rsByteArrayLength The expected concatenated array length.
104         *
105         * @return The resulting concatenated array.
106         */
107        private static byte[] formatSignature(final BigInteger r, 
108                                              final BigInteger s,
109                                              final int rsByteArrayLength) {
110
111                byte[] rBytes = BigIntegerUtils.toBytesUnsigned(r);
112                byte[] sBytes = BigIntegerUtils.toBytesUnsigned(s);
113
114                byte[] rsBytes = new byte[rsByteArrayLength];
115
116                int i = 0;
117
118                // Copy R bytes to first array half, zero pad front
119                int offset = (rsByteArrayLength / 2) - rBytes.length;
120
121                i += offset;
122
123                for (byte rB: rBytes) {
124
125                        rsBytes[i++] = rB;
126                }
127
128                // Copy S bytes to second array half, zero pad front
129                i = rsByteArrayLength / 2;
130
131                offset = (rsByteArrayLength / 2) - sBytes.length;
132
133                i += offset;
134
135                for (byte sB: sBytes) {
136
137                        rsBytes[i++] = sB;
138                }
139
140                return rsBytes;
141        }
142
143
144        @Override
145        public Base64URL sign(final JWSHeader header, final byte[] signingInput)
146                throws JOSEException {
147
148                ECDSAParameters initParams = getECDSAParameters(header.getAlgorithm());
149                X9ECParameters x9ECParameters = initParams.getX9ECParameters();
150                Digest digest = initParams.getDigest();
151
152                ECDomainParameters ecParameterSpec = new ECDomainParameters(
153                        x9ECParameters.getCurve(), 
154                        x9ECParameters.getG(), 
155                        x9ECParameters.getN(), 
156                        x9ECParameters.getH(), 
157                        x9ECParameters.getSeed());
158
159                ECPrivateKeyParameters ecPrivateKeyParameters = 
160                        new ECPrivateKeyParameters(privateKey, ecParameterSpec);
161
162                digest.update(signingInput, 0, signingInput.length);
163                byte[] out = new byte[digest.getDigestSize()];
164                digest.doFinal(out, 0);
165
166                BigInteger[] signatureParts = doECDSA(ecPrivateKeyParameters, out);
167
168                int rsByteArrayLength = ECDSAProvider.getSignatureByteArrayLength(header.getAlgorithm());
169
170                return Base64URL.encode(formatSignature(signatureParts[0], 
171                                                        signatureParts[1], 
172                                                        rsByteArrayLength));
173        }
174}