简体   繁体   中英

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.)

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

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.

The ECDSA library can do this.

import ecdsa

skStr = "0x63bd3b01c5ce749d87f5f7481232a93540acdb0f7b5c014ecd9cd32b041d6f33"
skBytes = bytes.fromhex(skStr[2:])  # Skip "0x".
sk = ecdsa.SigningKey.from_string(skBytes, curve=ecdsa.NIST256p)

vkStr = "0x04017655e42a892cc71bccedcb1cd421d03530e1d7edb52cef143c5562c4c6f0129fa5a37738013e64a1ff0e6cb7068815a13000eb162cb7a0214dfcf3c8fa101c"
vkBytes = bytes.fromhex(vkStr[2:])  # Skip "0x".
if False:  # Expected to work, but memoryview[slice] != bytes:
    vk = ecdsa.VerifyingKey.from_string(vkBytes, curve=ecdsa.NIST256p)
else:  # Python 3.8 workaround
    vkPoint = ecdsa.VerifyingKey._from_raw_encoding(vkBytes[1:], curve=ecdsa.NIST256p)  # Skip b"\x04".
    vk = ecdsa.VerifyingKey.from_public_point(vkPoint, curve=ecdsa.NIST256p)
# or vk = sk.get_verifying_key()

Note that the curve of ecdsa.SECP256k1 does not work with the provided key data ("MalformedPointError: Point does not lie on the curve"), but ecdsa.NIST256p works fine.

Here is how you sign and verify a message:

message = b"Hello, world!"
signature = sk.sign(message)
print(f"Signature = 0x{signature.hex()}")
# Signature = 0x35b8d39a6655f8de13ebe9b30bbadd1c9dbf32ccfcb1c7ca106305214740b7dca652d59902eb7152c2e6e8bfc76872b803d1110defdf833bcb969a63beab6364
isSignatureValid = vk.verify(signature, message)
print(f"{isSignatureValid=}")
# isSignatureValid=True

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM