An elliptic curve can have the relationship of \(y^2 = x^3 + ax +b\) and for a defined prime number (\(p\)). With P-256 we use: \(p = 2^{256} - 2^{224} + 2^{192} + 2^{96} - 1 \), \(a= -3\), \(b=41058363725152142129326129780047268409114441015993725554835256314039467401291\). RFC 9500 [here] was published in Dec 2023 and defines RSA, DLP (Discrete Logarithm Problem) and ECDLP (Elliptic Curve DLP) keys for testing. With DLP, we have discrete log methods such as the Diffie-Hellman method and ElGamal, and with ECDLP, we have elliptic curve methods, such as for ECDH and ECDSA. In this page, we will use the ECC keys for P256, P384 and P512.
ECC Test Keys |
Theory
In ECC, we have a private key of \(a\) and which is a scalar value. The public key is then:
\(Q=a.G\)
and where \(G\) is the base point on the curve, and \(Q\) is a public key point.
Coding
For this, we have the \(d\) array as the private key, and qx and qx defining the (x,y) coordinates for the public key point. We also have a PEM format for the private key. First, let’s code this into a program and see the outputs for the private key and the public key:
import sys from Crypto.PublicKey import ECC from Crypto.Util.number import * from ecdsa import ECDH, NIST256p import binascii msg="hello" test=1 curve_name='P-256' if (len(sys.argv)>1): test=int(sys.argv[1]) try: M= bytes_to_long(msg.encode('utf-8')) if (test==1): d=0xE6CB5BDD80AA45AE9C95E8C15476679FFEC953C16851E711E743939589C64FC1 curve_name='P-256' if (test==2): d=0xE2563328DFABF68188606B91324281C1D58A4456431B09D510B35FECC9F307CA1822846FA2671371A9A81BAC0E35749D curve_name='P-384' if (test==3): d=0x01D924DCCA0A887F8D99767A37D874E637A12CCB477D6E08665356694D68B7655E5069638FDE7B45C854013DC77A35B18655B84C966A60220D40F91ED9F5145802EA curve_name='P-521' key=ECC.construct(curve=curve_name,d=d) print("=== ECC Private key ===") print (f"d={hex(d)}\n") print (f"Public Key (x) {hex(key.pointQ.x)}") print (f"Public Key (y) {hex(key.pointQ.y)}") print("\n=== Private Key PEM format ===") eccKey = ECC.construct( curve=curve_name,d=d ) pubKeyPEM = eccKey.export_key(format='PEM') print(pubKeyPEM) print("\n=== Private Key DER format ===") pubKeyDER = eccKey.export_key(format='DER') print(binascii.hexlify(pubKeyDER).decode()) pub= eccKey.public_key() print("\n=== Public Key PEM format ===") pubKeyPEM = pub.export_key(format='PEM') print(pubKeyPEM) print("\n=== Public Key DER format ===") pubKeyDER = pub.export_key(format='DER') print(binascii.hexlify(pubKeyDER).decode()) print("\n=== Public Key OpenSSH format ===") pubKeyOpen = pub.export_key(format='OpenSSH') print(pubKeyOpen) except Exception as e: print(e)
and a sample run for P256 shows the correct public key:
=== ECC Private key === d=0xe6cb5bdd80aa45ae9c95e8c15476679ffec953c16851e711e743939589c64fc1 Public Key (x) 0x422548f88fb782ffb5eca3744452c72a1e558fbd6f73be5e48e93232cc45c5b1 Public Key (y) 0x6c4cd10c4cb8d5b8a17139e94882c8992572993425f41419ab7e90a42a494272 === Private Key PEM format === -----BEGIN PRIVATE KEY----- MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQg5stb3YCqRa6clejB VHZnn/7JU8FoUecR50OTlYnGT8GhRANCAARCJUj4j7eC/7Xso3REUscqHlWPvW9z vl5I6TIyzEXFsWxM0QxMuNW4oXE56UiCyJklcpk0JfQUGat+kKQqSUJy -----END PRIVATE KEY----- === Private Key DER format === 308187020100301306072a8648ce3d020106082a8648ce3d030107046d306b0201010420e6cb5bdd80aa45ae9c95e8c15476679ffec953c16851e711e743939589c64fc1a14403420004422548f88fb782ffb5eca3744452c72a1e558fbd6f73be5e48e93232cc45c5b16c4cd10c4cb8d5b8a17139e94882c8992572993425f41419ab7e90a42a494272 === Public Key PEM format === -----BEGIN PUBLIC KEY----- MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEQiVI+I+3gv+17KN0RFLHKh5Vj71v c75eSOkyMsxFxbFsTNEMTLjVuKFxOelIgsiZJXKZNCX0FBmrfpCkKklCcg== -----END PUBLIC KEY----- === Public Key DER format === 3059301306072a8648ce3d020106082a8648ce3d03010703420004422548f88fb782ffb5eca3744452c72a1e558fbd6f73be5e48e93232cc45c5b16c4cd10c4cb8d5b8a17139e94882c8992572993425f41419ab7e90a42a494272 === Public Key OpenSSH format === ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBEIlSPiPt4L/teyjdERSxyoeVY+9b3O+XkjpMjLMRcWxbEzRDEy41bihcTnpSILImSVymTQl9BQZq36QpCpJQnI=
The key is constructed by just using the curve name and the private key (d):
key=ECC.construct(curve=curve_name,d=d) print("=== ECC Private key ===") print (f"d={hex(d)}\n") print (f"Public Key (x) {hex(key.pointQ.x)}") print (f"Public Key (y) {hex(key.pointQ.y)}")
We can see we have three forms of the private key: raw (d and (x,y)), PEM and DER. The DER format is a binary format for the key pair, while PEM defines the key in a Base 64 format. With DER, we have the 0x30 value at the start. This identifies the ANS1 tag of “SEQUENCE”. Using OpenSSL, we can parse with [here]:
echo 308187020100301306072a8648ce3d020106082a8648ce3d030107046d306b020101042 0e6cb5bdd80aa45ae9c95e8c15476679ffec953c16851e711e743939589c64fc1a1440342000 4422548f88fb782ffb5eca3744452c72a1e558fbd6f73be5e48e93232cc45c5b16c4cd10c4cb 8d5b8a17139e94882c8992572993425f41419ab7e90a42a494272 | xxd -r -p | openssl asn1parse -inform der 0:d=0 hl=3 l= 135 cons: SEQUENCE 3:d=1 hl=2 l= 1 prim: INTEGER :00 6:d=1 hl=2 l= 19 cons: SEQUENCE 8:d=2 hl=2 l= 7 prim: OBJECT :id-ecPublicKey 17:d=2 hl=2 l= 8 prim: OBJECT :prime256v1 27:d=1 hl=2 l= 109 prim: OCTET STRING 0000 - 30 6b 02 01 01 04 20 e6-cb 5b dd 80 aa 45 ae 9c 0k.... ..[...E.. 0010 - 95 e8 c1 54 76 67 9f fe-c9 53 c1 68 51 e7 11 e7 ...Tvg...S.hQ... 0020 - 43 93 95 89 c6 4f c1 a1-44 03 42 00 04 42 25 48 C....O..D.B..B%H 0030 - f8 8f b7 82 ff b5 ec a3-74 44 52 c7 2a 1e 55 8f ........tDR.*.U. 0040 - bd 6f 73 be 5e 48 e9 32-32 cc 45 c5 b1 6c 4c d1 .os.^H.22.E..lL. 0050 - 0c 4c b8 d5 b8 a1 71 39-e9 48 82 c8 99 25 72 99 .L....q9.H...%r. 0060 - 34 25 f4 14 19 ab 7e 90-a4 2a 49 42 72 4%....~..*IBr
We can see that the key is also defined in a DER format, so we can parse it with [here]:
echo 306b0201010420e6cb5bdd80aa45ae9c95e8c15476679ffec953c16851e711e74393958 9c64fc1a14403420004422548f88fb782ffb5eca3744452c72a1e558fbd6f73be5e48e93232c c45c5b16c4cd10c4cb8d5b8a17139e94882c8992572993425f41419ab7e90a42a494272 | xxd -r -p | openssl asn1parse -inform der 0:d=0 hl=2 l= 107 cons: SEQUENCE 2:d=1 hl=2 l= 1 prim: INTEGER :01 5:d=1 hl=2 l= 32 prim: OCTET STRING 0000 - e6 cb 5b dd 80 aa 45 ae-9c 95 e8 c1 54 76 67 9f ..[...E.....Tvg. 0010 - fe c9 53 c1 68 51 e7 11-e7 43 93 95 89 c6 4f c1 ..S.hQ...C....O. 39:d=1 hl=2 l= 68 cons: cont [ 1 ] 41:d=2 hl=2 l= 66 prim: BIT STRING 0000 - 00 04 42 25 48 f8 8f b7-82 ff b5 ec a3 74 44 52 ..B%H........tDR 0010 - c7 2a 1e 55 8f bd 6f 73-be 5e 48 e9 32 32 cc 45 .*.U..os.^H.22.E 0020 - c5 b1 6c 4c d1 0c 4c b8-d5 b8 a1 71 39 e9 48 82 ..lL..L....q9.H. 0030 - c8 99 25 72 99 34 25 f4-14 19 ab 7e 90 a4 2a 49 ..%r.4%....~..*I 0040 - 42 72 Br
As we can see this reveals the correct private key (0xe6cb …) and the public key has a “00 04" in front of it. Overall, the “00” identifies a public key and “04” identifies that it is an uncompressed public key, and thus contains an (x,y) point.
With PEM, we can easily transport the keys over a text-based message. We output the PEM and DER formats for the private key with:
pubKeyPEM = eccKey.export_key(format='PEM') print(pubKeyPEM) print("\n=== Private Key DER format ===") pubKeyDER = eccKey.export_key(format='DER') print(binascii.hexlify(pubKeyDER).decode())
Note that the private key contains both the private key (sk) and the public key (sk.G), and will thus be larger than the exported public key. For the public key, we export the public key and then display it in PEM, DER and OpenSSH format:
pub= eccKey.public_key() print("\n=== Public Key PEM format ===") pubKeyPEM = pub.export_key(format='PEM') print(pubKeyPEM) print("\n=== Public Key DER format ===") pubKeyDER = pub.export_key(format='DER') print(binascii.hexlify(pubKeyDER).decode()) print("\n=== Public Key OpenSSH format ===") pubKeyOpen = pub.export_key(format='OpenSSH') print(pubKeyOpen)
With OpenSSH, we have the form of:
=== Public Key OpenSSH format === ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBEIlS PiPt4L/teyjdERSxyoeVY+9b3O+XkjpMjLMRcWxbEzRDEy41bihcTnpSILImSVymTQl9BQZq36QpC pJQnI=
This form is typically used to remotely log into a system with SSH. With P-384, the get:
=== ECC Private key === d=0xe2563328dfabf68188606b91324281c1d58a4456431b09d510b35fecc9f307ca1822846fa2671371a9a81bac0e35749d Public Key (x) 0x5b0901b88523296eb919d50ffa1a9cb374bc4d409586282bfeca11b1d95adbb54734af570bf82b7228cf226bcf4c25dd Public Key (y) 0xbcfe3b1a3ad39430eff763e1d68d2e151d91720b7795b58da6b34639613a8fb9b5a8da48c6747117f9919e8424f37ec8 === Private Key PEM format === -----BEGIN PRIVATE KEY----- MIG2AgEAMBAGByqGSM49AgEGBSuBBAAiBIGeMIGbAgEBBDDiVjMo36v2gYhga5Ey QoHB1YpEVkMbCdUQs1/syfMHyhgihG+iZxNxqagbrA41dJ2hZANiAARbCQG4hSMp brkZ1Q/6GpyzdLxNQJWGKCv+yhGx2VrbtUc0r1cL+CtyKM8ia89MJd28/jsaOtOU MO/3Y+HWjS4VHZFyC3eVtY2ms0Y5YTqPubWo2kjGdHEX+ZGehCTzfsg= -----END PRIVATE KEY----- === Private Key DER format === 3081b6020100301006072a8648ce3d020106052b8104002204819e30819b0201010430e2563328dfabf68188606b91324281c1d58a4456431b09d510b35fecc9f307ca1822846fa2671371a9a81bac0e35749da164036200045b0901b88523296eb919d50ffa1a9cb374bc4d409586282bfeca11b1d95adbb54734af570bf82b7228cf226bcf4c25ddbcfe3b1a3ad39430eff763e1d68d2e151d91720b7795b58da6b34639613a8fb9b5a8da48c6747117f9919e8424f37ec8 === Public Key PEM format === -----BEGIN PUBLIC KEY----- MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEWwkBuIUjKW65GdUP+hqcs3S8TUCVhigr /soRsdla27VHNK9XC/grcijPImvPTCXdvP47GjrTlDDv92Ph1o0uFR2Rcgt3lbWN prNGOWE6j7m1qNpIxnRxF/mRnoQk837I -----END PUBLIC KEY----- === Public Key DER format === 3076301006072a8648ce3d020106052b81040022036200045b0901b88523296eb919d50ffa1a9cb374bc4d409586282bfeca11b1d95adbb54734af570bf82b7228cf226bcf4c25ddbcfe3b1a3ad39430eff763e1d68d2e151d91720b7795b58da6b34639613a8fb9b5a8da48c6747117f9919e8424f37ec8 === Public Key OpenSSH format === ecdsa-sha2-nistp384 AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBFsJAbiFIyluuRnVD/oanLN0vE1AlYYoK/7KEbHZWtu1RzSvVwv4K3IozyJrz0wl3bz+Oxo605Qw7/dj4daNLhUdkXILd5W1jaazRjlhOo+5tajaSMZ0cRf5kZ6EJPN+yA==
And P-512:
=== ECC Private key === d=0x1d924dcca0a887f8d99767a37d874e637a12ccb477d6e08665356694d68b7655e5069638fde7b45c854013dc77a35b18655b84c966a60220d40f91ed9f5145802ea Public Key (x) 0x1d0fd7257a84c747f562575c07385dbebf2f52bea58083db82fdd1531d8aae3cc875ff02ff7fa2da260d8eb62d6d2f5d649278e321736a0628cbbb30308b6e618db Public Key (y) 0xf62ad204c6460359bc818ab8961bf0f0fc0ec5aae8a428173ce56f00de9b157c1e5c82c64f562fcadefc4a4c28f6d342cf3ef616fc82d33b7285c921f2bf36fdd8 === Private Key PEM format === -----BEGIN PRIVATE KEY----- MIHuAgEAMBAGByqGSM49AgEGBSuBBAAjBIHWMIHTAgEBBEIB2STcygqIf42Zdno3 2HTmN6Esy0d9bghmU1ZpTWi3ZV5QaWOP3ntFyFQBPcd6NbGGVbhMlmpgIg1A+R7Z 9RRYAuqhgYkDgYYABAHQ/XJXqEx0f1YldcBzhdvr8vUr6lgIPbgv3RUx2KrjzIdf 8C/3+i2iYNjrYtbS9dZJJ44yFzagYoy7swMItuYY2wD2KtIExkYDWbyBiriWG/Dw /A7FquikKBc85W8A3psVfB5cgsZPVi/K3vxKTCj200LPPvYW/ILTO3KFySHyvzb9 2A== -----END PRIVATE KEY----- === Private Key DER format === 3081ee020100301006072a8648ce3d020106052b810400230481d63081d3020101044201d924dcca0a887f8d99767a37d874e637a12ccb477d6e08665356694d68b7655e5069638fde7b45c854013dc77a35b18655b84c966a60220d40f91ed9f5145802eaa18189038186000401d0fd7257a84c747f562575c07385dbebf2f52bea58083db82fdd1531d8aae3cc875ff02ff7fa2da260d8eb62d6d2f5d649278e321736a0628cbbb30308b6e618db00f62ad204c6460359bc818ab8961bf0f0fc0ec5aae8a428173ce56f00de9b157c1e5c82c64f562fcadefc4a4c28f6d342cf3ef616fc82d33b7285c921f2bf36fdd8 === Public Key PEM format === -----BEGIN PUBLIC KEY----- MIGbMBAGByqGSM49AgEGBSuBBAAjA4GGAAQB0P1yV6hMdH9WJXXAc4Xb6/L1K+pY CD24L90VMdiq48yHX/Av9/otomDY62LW0vXWSSeOMhc2oGKMu7MDCLbmGNsA9irS BMZGA1m8gYq4lhvw8PwOxaropCgXPOVvAN6bFXweXILGT1Yvyt78Skwo9tNCzz72 FvyC0ztyhckh8r82/dg= -----END PUBLIC KEY----- === Public Key DER format === 30819b301006072a8648ce3d020106052b81040023038186000401d0fd7257a84c747f562575c07385dbebf2f52bea58083db82fdd1531d8aae3cc875ff02ff7fa2da260d8eb62d6d2f5d649278e321736a0628cbbb30308b6e618db00f62ad204c6460359bc818ab8961bf0f0fc0ec5aae8a428173ce56f00de9b157c1e5c82c64f562fcadefc4a4c28f6d342cf3ef616fc82d33b7285c921f2bf36fdd8 === Public Key OpenSSH format === ecdsa-sha2-nistp521 AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBAHQ/XJXqEx0f1YldcBzhdvr8vUr6lgIPbgv3RUx2KrjzIdf8C/3+i2iYNjrYtbS9dZJJ44yFzagYoy7swMItuYY2wD2KtIExkYDWbyBiriWG/Dw/A7FquikKBc85W8A3psVfB5cgsZPVi/K3vxKTCj200LPPvYW/ILTO3KFySHyvzb92A==