001    package com.nimbusds.jose.crypto;
002    
003    
004    import java.math.BigInteger;
005    
006    import org.bouncycastle.asn1.x9.X9ECParameters;
007    
008    import org.bouncycastle.crypto.Digest;
009    
010    import org.bouncycastle.crypto.params.ECDomainParameters;
011    import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
012    
013    import net.jcip.annotations.ThreadSafe;
014    
015    import com.nimbusds.jose.JOSEException;
016    import com.nimbusds.jose.JWSSigner;
017    import com.nimbusds.jose.ReadOnlyJWSHeader;
018    
019    import com.nimbusds.jose.util.Base64URL;
020    
021    
022    /**
023     * Elliptic Curve Digital Signature Algorithm (ECDSA) signer of 
024     * {@link com.nimbusds.jose.JWSObject JWS objects}. This class is thread-safe.
025     *
026     * <p>Supports the following JSON Web Algorithms (JWAs):
027     *
028     * <ul>
029     *     <li>{@link com.nimbusds.jose.JWSAlgorithm#ES256}
030     *     <li>{@link com.nimbusds.jose.JWSAlgorithm#ES384}
031     *     <li>{@link com.nimbusds.jose.JWSAlgorithm#ES512}
032     * </ul>
033     * 
034     * @author Axel Nennker
035     * @author Vladimir Dzhuvinov
036     * @version $version$ (2012-10-23)
037     */
038    @ThreadSafe
039    public class ECDSASigner extends ECDSAProvider implements JWSSigner {
040    
041    
042            /**
043             * The private key.
044             */
045            private final BigInteger privateKey;
046            
047            
048            /**
049             * Creates a new Elliptic Curve Digital Signature Algorithm (ECDSA) 
050             * signer.
051             *
052             * @param privateKey The private key ('d' parameter). Must not be 
053             *                   {@code null}.
054             */
055            public ECDSASigner(final BigInteger privateKey) {
056    
057                    if (privateKey == null)
058                            throw new IllegalArgumentException("The private key must not be null");
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 not
079             *                               be {@code null}.
080             * @param bytes                  The byte array to sign. Must not be 
081             *                               {@code null}.
082             *
083             * @return The ECDSA signture.
084             */
085            private static byte[] 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                    BigInteger[] res = signer.generateSignature(bytes);
093                    BigInteger r = res[0];
094                    BigInteger s = res[1];
095    
096                    return formatSignature(r, s);
097            }
098            
099            
100            /**
101             * Converts the specified big integers to byte arrays and returns their
102             * 64-byte array concatenation.
103             *
104             * @param r The R parameter. Must not be {@code null}.
105             * @param s The S parameter. Must not be {@code null}.
106             *
107             * @return The resulting 64-byte array.
108             */
109            private static byte[] formatSignature(final BigInteger r, final BigInteger s) {
110                    
111                    byte[] rBytes = r.toByteArray();
112                    byte[] sBytes = s.toByteArray();
113                    
114                    byte[] rsBytes = new byte[64];
115                    
116                    for (int i=0; i<rsBytes.length; i++)
117                            rsBytes[i] = 0;
118                    
119                    if (rBytes.length >= 32)
120                            System.arraycopy(rBytes, rBytes.length - 32, rsBytes, 0, 32);
121                    
122                    else
123                            System.arraycopy(rBytes, 0, rsBytes, 32 - rBytes.length, rBytes.length);
124                    
125                    
126                    if (sBytes.length >= 32)
127                            System.arraycopy(sBytes, sBytes.length - 32, rsBytes, 32, 32);
128                    
129                    else
130                            System.arraycopy(sBytes, 0, rsBytes, 64 - sBytes.length, sBytes.length);
131                    
132                    
133                    return rsBytes;
134            }
135    
136    
137            @Override
138            public Base64URL sign(final ReadOnlyJWSHeader header, final byte[] signableContent)
139                    throws JOSEException {
140                    
141                    ECDSAParameters initParams = getECDSAParameters(header.getAlgorithm());
142                    X9ECParameters x9ECParameters = initParams.getX9ECParameters();
143                    Digest digest = initParams.getDigest();
144                    
145                    ECDomainParameters ecParameterSpec = new ECDomainParameters(
146                                                            x9ECParameters.getCurve(), 
147                                                            x9ECParameters.getG(), 
148                                                            x9ECParameters.getN(), 
149                                                            x9ECParameters.getH(), 
150                                                            x9ECParameters.getSeed());
151                    
152                    ECPrivateKeyParameters ecPrivateKeyParameters = 
153                            new ECPrivateKeyParameters(privateKey, ecParameterSpec);
154    
155                    digest.update(signableContent, 0, signableContent.length);
156                    byte[] out = new byte[digest.getDigestSize()];
157                    digest.doFinal(out, 0);
158    
159                    byte[] sig = doECDSA(ecPrivateKeyParameters, out);
160    
161                    return Base64URL.encode(sig);
162            }
163    }