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 }