Python:根据原始字节表示的私钥和公钥创建ECC密钥

时间:2019-12-30 02:36:37

标签: python cryptography ecdsa

我有以下ECC私钥和公钥对:

私钥:0x63bd3b01c5ce749d87f5f7481232a93540acdb0f7b5c014ecd9cd32b041d6f33

公钥:0x04017655e42a892cc71bccedcb1cd421d03530e1d7edb52cef143c5562c4c6f0129fa5a37738013e64a1ff0e6cb7068815a13000eb162cb7a0214dfcf3c8fa101c

曲线:SECP256R1

我想在Python中加载这些密钥以执行签名操作。您能否建议可能的步骤?

(如有必要,我很愿意使用“ openssl ec”工具。)

3 个答案:

答案 0 :(得分:1)

这是一个简单的示例(使用python 3 +加密模块),加载密钥以进行签名/验证:

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

这将显示:

Private key: 0x63bd3b01c5ce749d87f5f7481232a93540acdb0f7b5c014ecd9cd32b041d6f33
Public point (Uncompressed): 0x04017655e42a892cc71bccedcb1cd421d03530e1d7edb52cef143c5562c4c6f0129fa5a37738013e64a1ff0e6cb7068815a13000eb162cb7a0214dfcf3c8fa101c
Signature: 0x304402200308ac7b7a56e7227d665d8f652d849935b4876c5ecef252ed9713c975b0a6280220696c134bb6e115b9ac18790c27009938f081bfaf063e547ce75bad3c9682890b
Verification OK

答案 1 :(得分:0)

您可以使用ECC.construct(**kwargs)调用从各个整数构造键。

我在下面显示了如何对未压缩点进行十六进制,然后为字节而不是数字的操作。未压缩的点本身就是不是数字。因此,对于这些字节数组,我没有在您的问题中包括0x

私钥向量(通常表示为sd,我更喜欢s作为秘密),但是通常会使用字节(如果曾经传输过,通常也会保留在原位)。

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)

输出

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

...用空格稍微格式化一下,以显示第二个密钥确实是包含d的私钥。

xy的值可以从d计算(与基点的点乘法:d*G),这就是为什么私钥可以不包含它们而包含它们的原因在施工期间指定它们。

请注意,我已经使用了Python 3,也许某些Python开发人员能够将其转换为Python 2,并将结果包含在此答案中。想法/呼吁毕竟应该相似。

答案 2 :(得分:0)

ECDSA 库可以做到这一点。

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

请注意,ecdsa.SECP256k1 的曲线不适用于提供的关键数据(“MalformedPointError:点不在曲线上”),但 ecdsa.NIST256p 工作正常。

以下是您签署和验证消息的方式:

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