首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >使用EC并在Python中验证JS (前端)中的有效负载

使用EC并在Python中验证JS (前端)中的有效负载
EN

Stack Overflow用户
提问于 2021-12-17 14:48:38
回答 1查看 485关注 0票数 4

我有一个Python后端,它生成公共/私钥,生成一个有效负载,然后需要获得由客户机(ReactJS或纯JS)签名的有效负载,稍后将对其进行验证。

Python中的实现如下所示:

进口

代码语言:javascript
复制
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

生成密钥:

代码语言:javascript
复制
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')

符号:

代码语言:javascript
复制
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())
    ))

验证:

代码语言:javascript
复制
@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,但两者都不能支持所有所需的包。

简单地说,我想了解以下几点:

生成有效载荷

如有任何帮助/建议,将不胜感激。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 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站点),并签名一条消息:

代码语言:javascript
复制
(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)));
}
代码语言:javascript
复制
<p style="font-family:'Courier New', monospace;" id="pub"></p>
<p style="font-family:'Courier New', monospace;" id="sig"></p>

一项可能的产出是:

代码语言:javascript
复制
Public key: MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEWzC5lPNifcHNuKL+/jjhrtTi+9gAMbYui9Vv7TjtS7RCt8p6Y6zUmHVpGEowuVMuOSNxfpJYpnGExNT/eWhuwQ==
Signature: XRNTbkHK7H8XPEIJQhS6K6ncLPEuWWrkXLXiNWwv6ImnL2Dm5VHcazJ7QYQNOvWJmB2T3rconRkT0N4BDFapCQ==

在Python方面,可以通过以下方法成功地进行验证:

代码语言:javascript
复制
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侧的设置)。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/70394906

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档