ECDSA using java.security.Signature



Table of Contents


1. Java Classes

The Digital Signature Algorithm is available in JDK class java.security.Signature. Elliptic curve public/private key pairs can be generated using java.security.KeyPairGenerator.

java.security.spec.ECGenParameterSpec is used here to generate precomputed elliptic curve domain parameters using a standard name. This supports the SECG recommended names and parameters, and more.

References:

Java API

Java Security Documentation

secg.org - Standards for Efficient Cryptography Group


2. Generating a Keypair

ECgen.java
    ...
    KeyPairGenerator kpg = KeyPairGenerator.getInstance( "EC");
    ECGenParameterSpec curve = new ECGenParameterSpec( name);
    kpg.initialize( curve);
    KeyPair kp = kpg.genKeyPair();
    ECPrivateKey priv = (ECPrivateKey) kp.getPrivate();
    ECPublicKey pub = (ECPublicKey) kp.getPublic();
    ...
    BigInteger S = priv.getS();
    ECPoint W = pub.getW();
    BigInteger WX = W.getAffineX();
    BigInteger WY = W.getAffineY();
    ...

3. Signing a File

Sign.java
   ...
   // b is the private key in a byte array
    PKCS8EncodedKeySpec pkcs8 = new PKCS8EncodedKeySpec( b);
    KeyFactory kf = KeyFactory.getInstance( "EC");
    PrivateKey priv = kf.generatePrivate( pkcs8);

    Signature s = Signature.getInstance( "SHA256withECDSA");
    s.initSign( priv);
    ...
    while( (nbytes = in.read( msg)) > 0)
      s.update( msg, 0, nbytes);
    ...
    out.write( s.sign());

4. Verifying a Signature

Verify.java
    ...
   // b is the public key in a byte array
    X509EncodedKeySpec x509 = new X509EncodedKeySpec( b);
    KeyFactory kf = KeyFactory.getInstance( "EC");
    PublicKey pub = kf.generatePublic( x509);

    Signature s = Signature.getInstance( "SHA256withECDSA");
    s.initVerify( pub);
    ...
    while( (nbytes = in.read( msg)) > 0)
      s.update( msg, 0, nbytes);
    ...
   // b is the signature in a byte array
    if( s.verify( b))
      System.err.println( "Signature is valid.");
    else
      System.err.println( "INVALID signature.");

5. Example - Generating a Keypair

Generate an EC keypair for Alice:
  % java ECgen
  Usage: ECgen [-c curve_name (default: secp256k1)] UserID
  % java ECgen Alice
  secp256k1
    S = 13efe8901167863088934dc6a53ccabdb7ade25eb4900bfe703b70da6551bbce
  W.X = 7bfb1007a53524466a97130a3d9af755a6f859c4b6e34f97a679069e3813cc31
  W.Y = f7ab3f0c50ed5333f4f46c7041efd6183377bce9ab0291611c4832a020f71649
  %
The keys are saved in binary ASN.1 format. The values may be seen using openssl asn1parse and ec utilities, or simply using hex dump:
  % od -tx1 Alice.private
  0000000 30 3e 02 01 00 30 10 06 07 2a 86 48 ce 3d 02 01
  0000020 06 05 2b 81 04 00 0a 04 27 30 25 02 01 01 04 20
  0000040 13 ef e8 90 11 67 86 30 88 93 4d c6 a5 3c ca bd
  0000060 b7 ad e2 5e b4 90 0b fe 70 3b 70 da 65 51 bb ce
  0000100
  % od -tx1 Alice.public
  0000000 30 56 30 10 06 07 2a 86 48 ce 3d 02 01 06 05 2b
  0000020 81 04 00 0a 03 42 00 04 7b fb 10 07 a5 35 24 46
  0000040 6a 97 13 0a 3d 9a f7 55 a6 f8 59 c4 b6 e3 4f 97
  0000060 a6 79 06 9e 38 13 cc 31 f7 ab 3f 0c 50 ed 53 33
  0000100 f4 f4 6c 70 41 ef d6 18 33 77 bc e9 ab 02 91 61
  0000120 1c 48 32 a0 20 f7 16 49
  0000130
  %

6. Signing a Message

Sign a message and verify the signature:
  % fortune > msg
  % cat msg
  It's lucky you're going so slowly, because you're going in the wrong direction.
  % java Sign
  Usage: Sign UserID input [input.sig]
  % java Sign Alice msg
  Signing msg using Alice.private
  % java Verify
  Usage: Verify signer input [input.sig]
  % java Verify Alice msg
  Verifying msg using Alice.public
  Signature is valid.
The signature is saved in binary ASN.1 format. The (r,s) values may be seen using using a hex dump:
  % od -tx1 msg.sig
  0000000 30 45 02 20 42 a8 ad 84 1b 1f 01 14 1a 75 7a 27
  0000020 a2 db fe 61 ee 40 f7 e5 a8 a7 1b a0 1c d4 ed 4c
  0000040 37 d4 9c f8 02 21 00 e1 6b be 4b 9e a7 80 b9 4b
  0000060 09 b9 2e 9e 0d 91 c2 3c 4c c3 28 d0 38 42 d3 2e
  0000100 26 db f1 57 58 39 fd
  0000107
  %
In the ASN.1 DER format, 0x30 means SEQUENCE, followed by the length of the sequence (0x45); 0x02 means INTEGER, followed by the length of the integer. The first integer in the sequence is r and the second is s, for the ECDSA signature.

7. Elliptic Curve Parameters

See classes.list for an overview of the low-level elliptic curve classes.

ECparams.java extracts the parameters from the public key:

  % java ECparams
  Usage: ECparams UserID
  % java ECparams Alice
  ECFieldFp
  G.X = 79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798
  G.Y = 483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8
    A = 0
    B = 7
    n = fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141
    h = 1
    P = fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f
  %
Openssl can also be used to extract the parameters from the private key:
  % openssl pkcs8 -nocrypt -inform der < Alice.private | \
    openssl ec -text -param_out -param_enc explicit -noout
  read EC key
  Private-Key: (256 bit)
  priv:
      13:ef:e8:90:11:67:86:30:88:93:4d:c6:a5:3c:ca:
      bd:b7:ad:e2:5e:b4:90:0b:fe:70:3b:70:da:65:51:
      bb:ce
  pub:
      04:7b:fb:10:07:a5:35:24:46:6a:97:13:0a:3d:9a:
      f7:55:a6:f8:59:c4:b6:e3:4f:97:a6:79:06:9e:38:
      13:cc:31:f7:ab:3f:0c:50:ed:53:33:f4:f4:6c:70:
      41:ef:d6:18:33:77:bc:e9:ab:02:91:61:1c:48:32:
      a0:20:f7:16:49
  Field Type: prime-field
  Prime:
      00:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:
      ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:fe:ff:
      ff:fc:2f
  A:    0
  B:    7 (0x7)
  Generator (uncompressed):
      04:79:be:66:7e:f9:dc:bb:ac:55:a0:62:95:ce:87:
      0b:07:02:9b:fc:db:2d:ce:28:d9:59:f2:81:5b:16:
      f8:17:98:48:3a:da:77:26:a3:c4:65:5d:a4:fb:fc:
      0e:11:08:a8:fd:17:b4:48:a6:85:54:19:9c:47:d0:
      8f:fb:10:d4:b8
  Order:
      00:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:
      ff:fe:ba:ae:dc:e6:af:48:a0:3b:bf:d2:5e:8c:d0:
      36:41:41
  Cofactor:  1 (0x1)
  %

8. Manually Verifying the Signature

% openssl sha256 msg
SHA256(msg)= 2508b06d476c99ec4fe0eb0c962ab8255f728fd4d6be036abe8bad85c54398e9
%
Using the BigInteger Calculator:
0> run ECdata.calc
> 16 BigInteger 2508b06d476c99ec4fe0eb0c962ab8255f728fd4d6be036abe8bad85c54398e9 = H
> # signature:
> 16 BigInteger 42a8ad841b1f01141a757a27a2dbfe61ee40f7e5a8a71ba01cd4ed4c37d49cf8 = r
> 16 BigInteger e16bbe4b9ea780b94b09b92e9e0d91c23c4cc328d03842d32e26dbf1575839fd = s
> # private key:
> 16 BigInteger 13efe8901167863088934dc6a53ccabdb7ade25eb4900bfe703b70da6551bbce = S
> # public key:
> 16 BigInteger 7bfb1007a53524466a97130a3d9af755a6f859c4b6e34f97a679069e3813cc31 = WX
> 16 BigInteger f7ab3f0c50ed5333f4f46c7041efd6183377bce9ab0291611c4832a020f71649 = WY
> # parameters:
> 16 BigInteger 79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798 = GX
> 16 BigInteger 483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 = GY
> 16 BigInteger 0 = A
> 16 BigInteger 7 = B
> 16 BigInteger fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141 = n
> 1 = h
> 16 BigInteger fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f = P
1> run ECverify.calc
> # verify that G is a point on the curve
> GX GX * P mod A + GX * B + P mod GY GY * P mod
---
		32748224938747404814623910738487752935528512903530129802856995983256684603122
		32748224938747404814623910738487752935528512903530129802856995983256684603122
> - # should be 0 if GY*GY = GX*(GX*GX+A)+B (mod P)
---
		0
> clear # display W and r base 10
> WX WY r
---
	WX	56078069623060704908196095968461147682366987869857115502485688283293784722481
	WY	112023839590205722640398089480172000375544454374103061011458378919331661026889
	r	30150675877065491299928200238071960623312133111713312759907769636290880118008
> clear # verify that S*G = W, i.e. private_key*G = public_key
> GX GY S P A ECmul
---
		56078069623060704908196095968461147682366987869857115502485688283293784722481
		112023839590205722640398089480172000375544454374103061011458378919331661026889
> WY - # should be 0 if result = WY
---
		56078069623060704908196095968461147682366987869857115502485688283293784722481
		0
> pop WX - # should be 0 if result = WX
---
		0
> clear
> # verify signature:
> s n modInverse = si
> H si * n mod = u1
> r si * n mod = u2
> GX GY u1 P A ECmul WX WY u2 P A ECmul P A ECadd
---
		30150675877065491299928200238071960623312133111713312759907769636290880118008
		53488613055006182523885369902128813210484146628273428754557320121001597244440
> pop r - # should be 0 if result = r
---
		0

9. Using OpenSSL Keys and Certificates

Bob uses openssl to create an EC keypair and certificate request (details) and obtains a certificate Bob.cert with his public key certified by the RPC CA (Bob.CAcert).

Bob converts his private key to PKCS8 format for use with Java:

  % openssl pkcs8 -topk8 -nocrypt -outform der < Bob.pem > Bob.private
  Enter pass phrase: Bobby
Bob generates a message and signs it:
  % fortune > bobmsg
  % cat bobmsg
  FORTUNE PROVIDES QUESTIONS FOR THE GREAT ANSWERS: #19
  A:	To be or not to be.
  Q:	What is the square root of 4b^2?
  % java Sign Bob bobmsg
  Signing bobmsg using Bob.private
  %
The signed message can then be verified using Bob's public key from his certificate:
  % java CertVerify Bob bobmsg
  Certificate Subject:
  EMAILADDRESS=Bob, CN=Bob, O=Bob, L=Villanova, ST=Pennsylvania, C=US
  Checking certificate validity...
  Certificate is valid.
  Key type is EC
  Checking certificate signature...
  Certificate signature is verified.
  Verifying bobmsg using Bob.cert
  Signature is valid.
  %
CertVerify.java uses the certificate methods from the java.security.cert package:
    ...
    FileInputStream fis = new FileInputStream( certfile);
    CertificateFactory cf = CertificateFactory.getInstance( "X509");
    X509Certificate cert = (X509Certificate) cf.generateCertificate( fis);
    PublicKey pub = cert.getPublicKey();
    String alg = pub.getAlgorithm();
    ...

10. Compatibility with OpenSSL and C

sign.c demonstrates using the openssl library to create a signature which is compatible with Verify.java and CertVerify.java.

First Bob's private key is converted to binary format with the password removed:

% openssl ec -outform der < Bob.pem > Bob.der
read EC key
Enter PEM pass phrase: Bobby
writing EC key
%
Then Bob signs a message using sign.c and verifies it using CertVerify.java:
% ./sign Bob bobmsg
Signing bobmsg using Bob.der
% java CertVerify Bob bobmsg
Certificate Subject:
EMAILADDRESS=Bob, CN=Bob, O=Bob, L=Villanova, ST=Pennsylvania, C=US
Checking certificate validity...
Certificate is valid.
Key type is EC
Checking certificate signature...
Certificate signature is verified.
Verifying bobmsg using Bob.cert
Signature is valid.
%