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 }