001    package com.nimbusds.jose.crypto;
002    
003    
004    import java.math.BigInteger;
005    
006    import net.jcip.annotations.ThreadSafe;
007    
008    import org.bouncycastle.asn1.x9.X9ECParameters;
009    import org.bouncycastle.crypto.Digest;
010    import org.bouncycastle.crypto.params.ECDomainParameters;
011    import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
012    
013    import com.nimbusds.jose.JOSEException;
014    import com.nimbusds.jose.JWSSigner;
015    import com.nimbusds.jose.ReadOnlyJWSHeader;
016    import com.nimbusds.jose.util.Base64URL;
017    import 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$ (2013-03-27)
035     */
036    @ThreadSafe
037    public 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 ReadOnlyJWSHeader header, final byte[] signableContent)
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(signableContent, 0, signableContent.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    }