Python 中的 Web Crypto API JWK 用法

Posted

技术标签:

【中文标题】Python 中的 Web Crypto API JWK 用法【英文标题】:Web Crypto API JWK usage in Python 【发布时间】:2016-08-14 19:32:43 【问题描述】:

我正在开发一个 P2P 基础架构,该基础架构将包含来自一组不同应用程序的数据,通过网络分发。这个 P2P 覆盖由一组 Python Twisted 服务器组成。

我需要为每个应用程序的每个用户保证所存储数据的安全性和隐私性。因此,我在 Web 应用程序的客户端生成一对 RSA 密钥,使用 Web Crypto API。 RSA 密钥对也将存储在 P2P 覆盖中。所以,我在客户端加密私钥,并派生出用户密码。

此外,我正在使用jwk to pem 模块将 JWK 公钥转换为 PEM 密钥,以便在 Python 密码库(PyCrypt 或 m2Crypto)中使用。

最后,我必须保证包含这些凭据的消息以及用户数据保持其完整性。因此,在客户端,我使用用户的私钥对这些数据进行签名。

我将 ArrayBuffer 类型的数据和签名发送到服务器,以 base64 编码。

function signData(private_key, data, callback)

    var dataForHash = str2ab(JSON.stringify(sortObject(data)));
    computeSHA(dataForHash, "SHA-256", function(hash)


        signRSA(private_key, hash, function(data)      
            callback(data.buffer.b64encode(), dataForHash.b64encode());
        );    
    );


function computeSHA(data, mode, callback)
    window.crypto.subtle.digest(
        
            name: mode,
        ,
        data
    )
    .then(function(hash)
        callback(new Uint8Array(hash).buffer);
    )
    .catch(function(err)
        console.error(err);
    );


function signRSA(private_key, data, callback)
    window.crypto.subtle.sign(
        
            name: "RSASSA-PKCS1-v1_5",
        ,
        private_key,
        data
    )
    .then(function(signature)
        callback(new Uint8Array(signature));
    )
    .catch(function(err)
        console.error(err);
    );


ArrayBuffer.prototype.b64encode = function()
    return btoa(String.fromCharCode.apply(null, new Uint8Array(this)));
;

之后,当 Python Server 收到这个 http 请求时,它会从 base64 解码数据和签名。

dataForHash = base64.b64decode(dataReceived['data'])
signature = base64.b64decode(dataReceived['signature'])

为了验证签名,需要公钥。因此:

data = utils.byteify(json.loads(dataForHash.decode("utf-16")))
pub_key = base64.b64decode(data['pub_key']) # Get PEM Public Key

(utils.byteify() 将unicode字符串转换为常规字符串)

验证签名:

Authentication.verifySignature(signature, dataForHash, pub_key)

方法定义:

from Crypto.Signature import PKCS1_v1_5
from Crypto.Hash import SHA256
from Crypto.PublicKey import RSA

def verifySignature(signature, data, pub_key):
    key = RSA.importKey(pub_key)
    h = SHA256.new(data)
    verifier = PKCS1_v1_5.new(key)
    return verifier.verify(h, signature)

但是,签名验证返回 False。我也尝试过使用 m2crypto 库,但它返回 0。

【问题讨论】:

【参考方案1】:

我设法找到了问题。

虽然在 Python (PyCrypto) 中,sign 函数应该接收要签名的数据的哈希值,但使用 Web Cryptography API,sign 方法在对接收到的数据进行签名之前应用哈希函数。

因此,JS 中的数据被哈希了两次,一次在调用 sign 方法之前,一次在 sign 方法中,在创建签名之前。

function signData(private_key, data, callback)

    var dataForHash = str2ab(JSON.stringify(sortObject(data)));

    signRSA(private_key, dataForHash, function(data)           

        callback(data.buffer.b64encode(), dataForHash.b64encode());
    );    


ArrayBuffer.prototype.b64encode = function()
    return btoa(String.fromCharCode.apply(null, new Uint8Array(this)));
;

String.prototype.b64decode = function()
    var binary_string = window.atob(this);
    var len = binary_string.length;
    var bytes = new Uint8Array(new ArrayBuffer(len));
    for (var i = 0; i < len; i++)        
        bytes[i] = binary_string.charCodeAt(i);
    
    return bytes;
;

通过这个修改,python中的验证现在返回True。

【讨论】:

以上是关于Python 中的 Web Crypto API JWK 用法的主要内容,如果未能解决你的问题,请参考以下文章

crypto在web的使用

React中的AES加解密请求

Node.js v0.10.31API手冊-加密

Node.js v0.10.31API手冊-加密

js中的web加密

如何使用Bouncy Castle Crypto API来加密和解密数据