我有一个Python后端,它生成公共/私钥,生成一个有效负载,然后需要获得由客户机(ReactJS或纯JS)签名的有效负载,稍后将对其进行验证。
Python中的实现如下所示:
进口
import json
import uuid
from backend.config import STARTING_BALANCE
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.asymmetric import ec
from cryptography.hazmat.primitives.asymmetric.utils import (
encode_dss_signature,
decode_dss_signature
)
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.exceptions import InvalidSignature
from cryptography.hazmat.primitives.serialization import load_pem_private_key
import base64
import hashlib生成密钥:
class User:
def __init__(self):
self.address = hashlib.sha1(str(str(uuid.uuid4())[0:8]).encode("UTF-8")).hexdigest()
self.private_key = ec.generate_private_key(
ec.SECP256K1(),
default_backend()
)
self.private_key_return = self.private_key.private_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PrivateFormat.TraditionalOpenSSL,
encryption_algorithm=serialization.NoEncryption()
)
self.public_key = self.private_key.public_key()
self.serialize_public_key()
def serialize_public_key():
"""
Reset the public key to its serialized version.
"""
self.public_key = self.public_key.public_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PublicFormat.SubjectPublicKeyInfo
).decode('utf-8')符号:
def sign(self, data):
"""
Generate a signature based on the data using the local private key.
"""
return decode_dss_signature(self.private_key.sign(
json.dumps(data).encode('utf-8'),
ec.ECDSA(hashes.SHA256())
))验证:
@staticmethod
def verify(public_key, data, signature):
"""
Verify a signature based on the original public key and data.
"""
deserialized_public_key = serialization.load_pem_public_key(
public_key.encode('utf-8'),
default_backend()
)
(r, s) = signature
try:
deserialized_public_key.verify(
encode_dss_signature(r, s),
json.dumps(data).encode('utf-8'),
ec.ECDSA(hashes.SHA256())
)
return True
except InvalidSignature:
return False我现在需要的是在客户端加载(甚至生成) PEM密钥,然后根据请求签署一个JSON有效负载,稍后可以从Python后端进行验证。
我试着研究了网络密码学和cryptoJS的使用,但没有成功。
我可以使用另一种更兼容的算法,但至少我需要完全使用签名功能。
我还尝试使用Brython和Pyodide将Python编译成JS,但两者都不能支持所有所需的包。
简单地说,我想了解以下几点:
生成有效载荷
如有任何帮助/建议,将不胜感激。
发布于 2021-12-18 11:51:16
CryptoJS只支持对称加密,因此不支持ECDSA。WebCrypto支持ECDSA,但不支持secp256k1。
WebCrypto的优点是它得到了所有主流浏览器的支持。由于您可以根据您的评论使用其他曲线,所以我将用WebCrypto支持的曲线描述一个解决方案。
否则,sjcl也将是另一种选择,它是一个纯JavaScript库,支持ECDSA,特别是支持secp256k1,s.这里。
WebCrypto是一个低级别的API,它提供了您需要的功能,如密钥生成、密钥导出和签名。对于ECDSA,WebCrypto支持P-256 (又名secp256r1)、P-384 (又名secp384r1)和p-521 (又名secp521r1)曲线.在下面我使用P-256。
以下JavaScript代码为P-256生成一对密钥,导出X.509/SPKI格式的公钥,DER编码(以便可以发送到Python站点),并签名一条消息:
(async () => {
// Generate key pair
var keypair = await window.crypto.subtle.generateKey(
{
name: "ECDSA",
namedCurve: "P-256", // secp256r1
},
false,
["sign", "verify"]
);
// Export public key in X.509/SPKI format, DER encoded
var publicKey = await window.crypto.subtle.exportKey(
"spki",
keypair.publicKey
);
document.getElementById("pub").innerHTML = "Public key: " + ab2b64(publicKey);
// Sign data
var data = {
"data_1":"The quick brown fox",
"data_2":"jumps over the lazy dog"
}
var dataStr = JSON.stringify(data)
var dataBuf = new TextEncoder().encode(dataStr).buffer
var signature = await window.crypto.subtle.sign(
{
name: "ECDSA",
hash: {name: "SHA-256"},
},
keypair.privateKey,
dataBuf
);
document.getElementById("sig").innerHTML = "Signature: " + ab2b64(signature);
})();
// Helper
function ab2b64(arrayBuffer) {
return window.btoa(String.fromCharCode.apply(null, new Uint8Array(arrayBuffer)));
}<p style="font-family:'Courier New', monospace;" id="pub"></p>
<p style="font-family:'Courier New', monospace;" id="sig"></p>
一项可能的产出是:
Public key: MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEWzC5lPNifcHNuKL+/jjhrtTi+9gAMbYui9Vv7TjtS7RCt8p6Y6zUmHVpGEowuVMuOSNxfpJYpnGExNT/eWhuwQ==
Signature: XRNTbkHK7H8XPEIJQhS6K6ncLPEuWWrkXLXiNWwv6ImnL2Dm5VHcazJ7QYQNOvWJmB2T3rconRkT0N4BDFapCQ==在Python方面,可以通过以下方法成功地进行验证:
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.asymmetric import ec
from cryptography.hazmat.primitives.asymmetric.utils import encode_dss_signature
from cryptography.hazmat.primitives.serialization import load_der_public_key
from cryptography.hazmat.primitives import hashes
from cryptography.exceptions import InvalidSignature
import base64
import json
publikKeyDer = base64.b64decode("MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEWzC5lPNifcHNuKL+/jjhrtTi+9gAMbYui9Vv7TjtS7RCt8p6Y6zUmHVpGEowuVMuOSNxfpJYpnGExNT/eWhuwQ==")
data = {
"data_1":"The quick brown fox",
"data_2":"jumps over the lazy dog"
}
signature = base64.b64decode("XRNTbkHK7H8XPEIJQhS6K6ncLPEuWWrkXLXiNWwv6ImnL2Dm5VHcazJ7QYQNOvWJmB2T3rconRkT0N4BDFapCQ==")
publicKey = load_der_public_key(publikKeyDer, default_backend())
r = int.from_bytes(signature[:32], byteorder='big')
s = int.from_bytes(signature[32:], byteorder='big')
try:
publicKey.verify(
encode_dss_signature(r, s),
json.dumps(data, separators=(',', ':')).encode('utf-8'),
ec.ECDSA(hashes.SHA256())
)
print("verification succeeded")
except InvalidSignature:
print("verification failed")其中,与发布的Python代码不同,load_der_public_key()被使用而不是load_pem_public_key()。
此外,WebCrypto返回IEEE格式的签名,但作为级联的P1363 r_s,需要将这两个部分转换为整数,这样就可以使用encode_dss_signature()将格式转换为ASN.1/DER。
对于JSON,必须将分隔符重新定义为最紧凑的表示形式(但这取决于JavaScript侧的设置)。
https://stackoverflow.com/questions/70394906
复制相似问题