Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python : Create ECC Keys from private and public key represented in raw bytes

I have following ECC private and public key pairs:

Private key : 0x63bd3b01c5ce749d87f5f7481232a93540acdb0f7b5c014ecd9cd32b041d6f33

Public Key : 0x04017655e42a892cc71bccedcb1cd421d03530e1d7edb52cef143c5562c4c6f0129fa5a37738013e64a1ff0e6cb7068815a13000eb162cb7a0214dfcf3c8fa101c

Curve : SECP256R1

I want to load these keys in Python to perform signing operations. Can you please suggest possible steps?

(I am comfortable to use "openssl ec" tools if necessary.)

like image 876
Shrikanth K Avatar asked Dec 13 '22 09:12

Shrikanth K


2 Answers

Here is a simple example (using python 3 + cryptography module) loading your key to sign/verify:

from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import ec
from cryptography.exceptions import InvalidSignature


private_value = 0x63bd3b01c5ce749d87f5f7481232a93540acdb0f7b5c014ecd9cd32b041d6f33
curve = ec.SECP256R1()
signature_algorithm = ec.ECDSA(hashes.SHA256())

# Make private and public keys from the private value + curve
priv_key = ec.derive_private_key(private_value, curve, default_backend())
pub_key = priv_key.public_key()
print('Private key: 0x%x' % priv_key.private_numbers().private_value)
print('Public point (Uncompressed): 0x%s' % pub_key.public_bytes(serialization.Encoding.X962, serialization.PublicFormat.UncompressedPoint).hex())

# Sign some data
data = b"this is some data to sign"
signature = priv_key.sign(data, signature_algorithm)
print('Signature: 0x%s' % signature.hex())

# Verify
try:
    pub_key.verify(signature, data, signature_algorithm)
    print('Verification OK')
except InvalidSignature:
    print('Verification failed')

This will display:

Private key: 0x63bd3b01c5ce749d87f5f7481232a93540acdb0f7b5c014ecd9cd32b041d6f33
Public point (Uncompressed): 0x04017655e42a892cc71bccedcb1cd421d03530e1d7edb52cef143c5562c4c6f0129fa5a37738013e64a1ff0e6cb7068815a13000eb162cb7a0214dfcf3c8fa101c
Signature: 0x304402200308ac7b7a56e7227d665d8f652d849935b4876c5ecef252ed9713c975b0a6280220696c134bb6e115b9ac18790c27009938f081bfaf063e547ce75bad3c9682890b
Verification OK
like image 200
nicolas Avatar answered Dec 15 '22 21:12

nicolas


You can use the ECC.construct(**kwargs) call to construct keys from the respective integers.

I've shown below how to do this for an uncompressed point in hex and then bytes rather than as a number though. An uncompressed point is not a number in itself. So I haven't included the 0x in your question for these byte arrays.

The private key vector (usually denoted s or d, I prefer s for secret) is a number, but generally it will be transmitted using bytes as well (if it is ever transmitted, usually it is kept in place).

from Crypto.PublicKey import ECC

# --- ECC public key from "flat" uncompressed EC point representation ---

# lets assume that the input is binary, rather than an integer

uncompressedPointHex = "04017655e42a892cc71bccedcb1cd421d03530e1d7edb52cef143c5562c4c6f0129fa5a37738013e64a1ff0e6cb7068815a13000eb162cb7a0214dfcf3c8fa101c"
uncompressedPoint = bytes.fromhex(uncompressedPointHex)

# check if the point is uncompressed rather than compressed
# for compressed points ask a separate *question*

off = 0
if (uncompressedPoint[off] != 0x04):
    raise Exception("Not an uncompressed point")
off += 1

sizeBytes = (len(uncompressedPoint) - 1) // 2

xBin = uncompressedPoint[off:off + sizeBytes]
x = int.from_bytes(xBin, 'big', signed=False)
off += sizeBytes

yBin = uncompressedPoint[off:off + sizeBytes]
y = int.from_bytes(yBin, 'big', signed=False)
off += sizeBytes

if (off != len(uncompressedPoint)):
    raise Exception("Invalid format of uncompressed point")

# if you already have integers, this is all you need

publicKey = ECC.construct(curve="secp256r1", point_x=x, point_y=y)

# obviously just for testing the result

print(publicKey)

# --- ECC private key from "flat" uncompressed EC point representation ---

# lets assume that the input is binary, rather than an integer

sHex = "63bd3b01c5ce749d87f5f7481232a93540acdb0f7b5c014ecd9cd32b041d6f33"
sBin = bytes.fromhex(sHex)

# very straightforward conversion, as S is just there

s = int.from_bytes(sBin, 'big', signed=False)

# if you already have integers, this is all you need

privateKey = ECC.construct(curve="secp256r1", d=s)

# obviously just for testing the result

print(privateKey)

outputs

EccKey(curve='NIST P-256', 
    point_x=661393602013979783798470650260404653019684003375182707210783968552030760978,
    point_y=72210400889213969389982861398963807410315877398616325431902307461337204789276)
EccKey(curve='NIST P-256',
    point_x=661393602013979783798470650260404653019684003375182707210783968552030760978,
    point_y=72210400889213969389982861398963807410315877398616325431902307461337204789276,
    d=45113313355812346734724097146216873116458888764597604491161664272788312911667)

...slightly formatted with whitespace to show that the second key is indeed the private key containing d.

The x and y values can be calculated from d (point multiplication with the base point: d*G), which is why the private key can contain them without having them specified during construction.

Note that I've used Python 3, maybe some Python dev is able to convert it to Python 2 and include the result in this answer. The idea / calls should be similar after all.

like image 35
Maarten Bodewes Avatar answered Dec 15 '22 22:12

Maarten Bodewes