Hazmat EC Signatures (ECDSA)With ECDSA (Elliptic Curve Digital Signature Algorithm) we use a private key to sign data, and then the public key can prove it. |
Theory
At the core of digital signing is normally a method known as ECDSA (Elliptic Curve Digital Signature Algorithm), as it is more efficient and produces smaller encryption keys than its RSA equivalant. With this Alice has a public key and a private key. She signs a message with her private key, and then Bob proves the signature with her public key. Under the hood there is r and an s value, and which are used to verify the signature. The mechanics of the (r,s) checking is defined here.
On this page, we will just use the sign() and verify() methods to sign a message and verify it. To generate a key pair we simply use the following and with the curve type defined (in this case it is Secp384r1):
private_key = ec.generate_private_key(ec.SECP384R1())
We now need to extract the public key, so that someone can check the signature:
public_key = private_key.public_key()
With a message, we will generate a hash value with a given hash type and then create the signature. We sign using the private key, the message, and the sign() method. We can then use the verify() method to prove the signature. An exception on the verification will identity an incorrect signature:
print ("Message: ",data.decode()) try: signature = private_key.sign(data,ec.ECDSA(hashes.SHA256())) print ("Good Signature: ",binascii.b2a_hex(signature).decode()) public_key.verify(signature, data, ec.ECDSA(hashes.SHA256())) except exceptions.InvalidSignature: print("A bad signature failed") else: print("Good signature verified")
A run with a good signature is:
Name of curve: secp192r1 Message: My test data Good Signature: 3034021804eb81c512579eafccfc4f54d97cbaac45d6b00c426f0d02021862efa33022bc532b651fda863ce4ca52409154a0432cf267 Good signature verified
The signature is in a DER format with an \(r\) and an \(s\) value, and can be read as [here]:
DER: 3034021804eb81c512579eafccfc4f54d97cbaac45d6b00c426f0d02021862efa33022bc532b651fda863ce4ca52409154a0432cf267 [U] SEQUENCE (30) [U] INTEGER (02): 120636795522494221738083360226736081266793749028489071874 [U] INTEGER (02): 2425905716110104551919552660080279451279883668482964648551
Coding
The following is the code:
from cryptography.hazmat.primitives.asymmetric import ec from cryptography.hazmat.primitives import serialization from cryptography.hazmat.primitives import hashes from cryptography import exceptions import binascii import sys private_key = ec.generate_private_key(ec.SECP384R1()) data = b"test" if (len(sys.argv)>2): type=int(sys.argv[2]) if (len(sys.argv)>1): data=str(sys.argv[1]).encode() if (type==1): private_key = ec.generate_private_key(ec.SECP192R1()) elif (type==2): private_key = ec.generate_private_key(ec.SECP224R1()) elif (type==3): private_key = ec.generate_private_key(ec.SECP256K1()) elif (type==4): private_key = ec.generate_private_key(ec.SECP256R1()) elif (type==5): private_key = ec.generate_private_key(ec.SECP384R1()) elif (type==6): private_key = ec.generate_private_key(ec.SECP521R1()) elif (type==7): private_key = ec.generate_private_key(ec.BrainpoolP256R1()) elif (type==8): private_key = ec.generate_private_key(ec.BrainpoolP384R1()) elif (type==9): private_key = ec.generate_private_key(ec.BrainpoolP512R1()) elif (type==10): private_key = ec.generate_private_key(ec.SECT163K1()) elif (type==11): private_key = ec.generate_private_key(ec.SECT163R2()) elif (type==12): private_key = ec.generate_private_key(ec.SECT233K1()) elif (type==13): private_key = ec.generate_private_key(ec.SECT233R1()) elif (type==14): private_key = ec.generate_private_key(ec.SECT283K1()) elif (type==15): private_key = ec.generate_private_key(ec.SECT233R1()) private_vals = private_key.private_numbers() no_bits=private_vals.private_value.bit_length() print (f"Private key value: {private_vals.private_value}. Number of bits {no_bits}") public_key = private_key.public_key() pub=public_key.public_numbers() print ("Name of curve: ",pub.curve.name) print ("Message: ",data.decode()) try: signature = private_key.sign(data,ec.ECDSA(hashes.SHA256())) print ("Good Signature: ",binascii.b2a_hex(signature).decode()) public_key.verify(signature, data, ec.ECDSA(hashes.SHA256())) except exceptions.InvalidSignature: print("A bad signature failed") else: print("Good signature verified") try: signature = private_key.sign(b"bad message",ec.ECDSA(hashes.SHA256())) print ("Bad Signature: ",binascii.b2a_hex(signature).decode()) public_key.verify(signature, data, ec.ECDSA(hashes.SHA256())) except exceptions.InvalidSignature: print("A bad signature failed") else: print("Good signature verified") pem = private_key.private_bytes(encoding=serialization.Encoding.PEM,format=serialization.PrivateFormat.PKCS8,encryption_algorithm=serialization.NoEncryption()) der = private_key.private_bytes(encoding=serialization.Encoding.DER,format=serialization.PrivateFormat.PKCS8,encryption_algorithm=serialization.NoEncryption()) print ("\nPrivate key (PEM):\n",pem.decode()) print ("Private key (DER):\n",binascii.b2a_hex(der)) pem = public_key.public_bytes(encoding=serialization.Encoding.PEM,format=serialization.PublicFormat.SubjectPublicKeyInfo) der = public_key.public_bytes(encoding=serialization.Encoding.DER,format=serialization.PublicFormat.SubjectPublicKeyInfo) print ("\nPublic key (PEM):\n",pem.decode()) print ("Public key (DER):\n",binascii.b2a_hex(der))
The following is a sample run:
Private key value: 2764182454245540237772308167620229600480588897947498851651. Number of bits 191 Name of curve: secp192r1 Message: My test data Good Signature: 3034021804eb81c512579eafccfc4f54d97cbaac45d6b00c426f0d02021862efa33022bc532b651fda863ce4ca52409154a0432cf267 Good signature verified Bad Signature: 30350218660f6b27c13feee5524f4e0a643466f45ecbf513c5e6660c021900f3ed00930639457fb58093e954d316082dc167e64f475b51 A bad signature failed Private key (PEM): -----BEGIN PRIVATE KEY----- MG8CAQAwEwYHKoZIzj0CAQYIKoZIzj0DAQEEVTBTAgEBBBhwu2lQ4vwkji2+qiQ6 vMot3eF41nEgLUOhNAMyAASXqyOm1I0u6zLvf+wXuonwcLBlj7CQSorIA/WNCZfc 3dfRhZ/6HYETkVeZy2lgI/Y= -----END PRIVATE KEY----- Private key (DER): b'306f020100301306072a8648ce3d020106082a8648ce3d03010104553053020101041870bb6950e2fc248e2dbeaa243abcca2ddde178d671202d43a1340332000497ab23a6d48d2eeb32ef7fec17ba89f070b0658fb0904a8ac803f58d0997dcddd7d1859ffa1d8113915799cb696023f6' Public key (PEM): -----BEGIN PUBLIC KEY----- MEkwEwYHKoZIzj0CAQYIKoZIzj0DAQEDMgAEl6sjptSNLusy73/sF7qJ8HCwZY+w kEqKyAP1jQmX3N3X0YWf+h2BE5FXmctpYCP2 -----END PUBLIC KEY----- Public key (DER): b'3049301306072a8648ce3d020106082a8648ce3d0301010332000497ab23a6d48d2eeb32ef7fec17ba89f070b0658fb0904a8ac803f58d0997dcddd7d1859ffa1d8113915799cb696023f6'