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