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

Python:从以原始字节表示的私钥和公钥创建ECC密钥,python,cryptography,ecdsa,Python,Cryptography,Ecdsa,我有以下ECC私钥和公钥对: 私钥:0x63bd3b01c5ce749d87f5f7481232a93540acdb0f7b5c014ecd9cd32b041d6f33 公钥:0x04017655E42A892CC71BCCEDCB1CD421D03530E1D7EDB52CEF143C562C4C6F0129FA5A37738013E64A1FF0E6CB7068815A13000EB162CB7A0214DFCF3C8FA101C 曲线:SECP256R1 我想在Python中加载这些键以执

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

私钥:
0x63bd3b01c5ce749d87f5f7481232a93540acdb0f7b5c014ecd9cd32b041d6f33

公钥:
0x04017655E42A892CC71BCCEDCB1CD421D03530E1D7EDB52CEF143C562C4C6F0129FA5A37738013E64A1FF0E6CB7068815A13000EB162CB7A0214DFCF3C8FA101C

曲线:
SECP256R1

我想在Python中加载这些键以执行签名操作。你能建议可能的步骤吗


(如果需要,我可以使用“openssl ec”工具。)

您可以使用
ECC.construct(**kwargs)
调用从相应的整数构造密钥

我在下面展示了如何用十六进制和字节而不是数字来表示未压缩的点。未压缩的点本身不是数字。所以我没有把
0x
包含在这些字节数组的问题中

私钥向量(通常表示为
s
d
,我更喜欢
s
表示秘密)是一个数字,但通常它也会使用字节来传输(如果曾经传输过,通常会保持不变)

输出

EccKey(曲线='NIST P-256',
点x=6613936020139797837984706502604653019684003375182707210783968552030760978,
点(y=722104008892139693899828613989638074103158773998616325431902307461337204789276)
EccKey(曲线='NIST P-256',
点x=6613936020139797837984706502604653019684003375182707210783968552030760978,
点y=722104008892139693899828613989638074103158773998616325431902307461337204789276,
d=4511331335581234673724097146168716458888764597604491161664272788312911667)
…稍微格式化为空白,以显示第二个密钥确实是包含d的私钥

x
y
值可以从
d
(与基点的点乘:
d*G
)计算得出,这就是为什么私钥可以包含它们,而无需在构造过程中指定它们

请注意,我使用了Python3,也许一些Python开发人员能够将其转换为Python2,并在这个答案中包含结果。想法/调用毕竟应该是类似的。

下面是一个简单的示例(使用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
图书馆可以做到这一点

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
的曲线不适用于提供的关键数据(“格式错误的点错误:点不在曲线上”),但
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

第一步:生成一个新的密钥对,因为你刚刚在互联网上发布了这个密钥对。确保你发布的密钥不会在生产中的任何地方使用。走路太容易了~非常感谢你们的警告。这是一个测试密钥。从原始数据生成密钥的详细信息取决于相应的库,例如,在PyCryptodome中,该函数可用于密码学和。哦,天哪,天哪,这花费的时间比我预期的要多得多。然而,在设置PyCryptoDome包之后,我终于完成了它(首先是Python,然后是Python3,安装了
pip3
,没有注意到我使用的是
/
而不是
/
,并且得到了一个相当令人讨厌的切片错误,然后没有理解
**kwarg
,这意味着命名参数的数量可变,使用了
var++
,显然不起作用等等。)感谢“nicolas”。当我执行示例时,签名和点的值不同。但是签名验证成功。--私钥:0x63bd3b01c5ce749d87f5f7481232a93540acdb0f7b5c014ecd9cd32b041d6f33公共点(未压缩):0x04017655E42A892CC71BCCEDCB1CD421D03530E1D7EDB52CEF14C5562C4C6F0129FA5A37738013E64A1FF0E6CB7068815A13000EB162CB7A0214DFCF3C8FA101C签名:0x3044022005278DCB26E785CA59CDAC78A9AC7EE0DF19B4804804F3250B00C2FAF5D22A62020196269DA58FA5EA2E3655FE7AF927723D2863A17CC8B8FD834038B默认情况下需要ECDSA和随机ECDSA验证因此,如果运行同一代码两次,通常会得到不同的签名(但它们都是正确的)。
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