Python :从私钥和 public 原始字节表示的密钥创建 ECC 密钥
Python : Create ECC Keys from private and public key represented in raw bytes
我有以下 ECC 私钥和 public 密钥对:
私钥:0x63bd3b01c5ce749d87f5f7481232a93540acdb0f7b5c014ecd9cd32b041d6f33
Public 键:0x04017655e42a892cc71bccedcb1cd421d03530e1d7edb52cef143c5562c4c6f0129fa5a37738013e64a1ff0e6cb7068815a13000eb162cb7a0214dfcf3c8fa101c
曲线:SECP256R1
我想在 Python 中加载这些密钥以执行签名操作。你能建议可能的步骤吗?
(如有必要,我很乐意使用 "openssl ec" 工具。)
您可以使用 ECC.construct(**kwargs)
调用从相应的整数构造键。
我在下面展示了如何针对十六进制的未压缩点 然后是字节 而不是数字来执行此操作。未压缩点本身不是数字。因此,我没有在您针对这些字节数组的问题中包含 0x
。
私钥向量(通常表示为s
或d
,我更喜欢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的私钥。
x
和y
的值可以由d
计算出来(与基点的点乘:d*G
),这就是为什么私钥可以包含他们没有在施工期间指定。
请注意,我使用了 Python 3,也许某些 Python 开发人员能够将其转换为 Python 2 并将结果包含在此答案中。毕竟想法/调用应该是相似的。
这是一个简单的示例(使用 python 3 + 密码模块)将您的密钥加载到 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')
这将显示:
Private key: 0x63bd3b01c5ce749d87f5f7481232a93540acdb0f7b5c014ecd9cd32b041d6f33
Public point (Uncompressed): 0x04017655e42a892cc71bccedcb1cd421d03530e1d7edb52cef143c5562c4c6f0129fa5a37738013e64a1ff0e6cb7068815a13000eb162cb7a0214dfcf3c8fa101c
Signature: 0x304402200308ac7b7a56e7227d665d8f652d849935b4876c5ecef252ed9713c975b0a6280220696c134bb6e115b9ac18790c27009938f081bfaf063e547ce75bad3c9682890b
Verification OK
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
我有以下 ECC 私钥和 public 密钥对:
私钥:0x63bd3b01c5ce749d87f5f7481232a93540acdb0f7b5c014ecd9cd32b041d6f33
Public 键:0x04017655e42a892cc71bccedcb1cd421d03530e1d7edb52cef143c5562c4c6f0129fa5a37738013e64a1ff0e6cb7068815a13000eb162cb7a0214dfcf3c8fa101c
曲线:SECP256R1
我想在 Python 中加载这些密钥以执行签名操作。你能建议可能的步骤吗?
(如有必要,我很乐意使用 "openssl ec" 工具。)
您可以使用 ECC.construct(**kwargs)
调用从相应的整数构造键。
我在下面展示了如何针对十六进制的未压缩点 然后是字节 而不是数字来执行此操作。未压缩点本身不是数字。因此,我没有在您针对这些字节数组的问题中包含 0x
。
私钥向量(通常表示为s
或d
,我更喜欢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的私钥。
x
和y
的值可以由d
计算出来(与基点的点乘:d*G
),这就是为什么私钥可以包含他们没有在施工期间指定。
请注意,我使用了 Python 3,也许某些 Python 开发人员能够将其转换为 Python 2 并将结果包含在此答案中。毕竟想法/调用应该是相似的。
这是一个简单的示例(使用 python 3 + 密码模块)将您的密钥加载到 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')
这将显示:
Private key: 0x63bd3b01c5ce749d87f5f7481232a93540acdb0f7b5c014ecd9cd32b041d6f33
Public point (Uncompressed): 0x04017655e42a892cc71bccedcb1cd421d03530e1d7edb52cef143c5562c4c6f0129fa5a37738013e64a1ff0e6cb7068815a13000eb162cb7a0214dfcf3c8fa101c
Signature: 0x304402200308ac7b7a56e7227d665d8f652d849935b4876c5ecef252ed9713c975b0a6280220696c134bb6e115b9ac18790c27009938f081bfaf063e547ce75bad3c9682890b
Verification OK
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