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:
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: BobbyBob 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. %