Javascript (crypto.subtle) 与 Python (pycryptodomex) 密钥包装

Javascript (crypto.subtle) vs Python (pycryptodomex) key wrapping

我的 public 密钥加密在 Javascript 中运行良好。我正在使用用 RSA-OAEP 包装的 AES-GCM。 Encryption/decryption 在 Javascript 版本中运行良好。现在我正在尝试在服务器端 (python) 加密某些内容并使用我现有的解密客户端 (window.crypto.subtle)。当我尝试解包密钥时失败并出现此错误:

OperationError: The operation failed for an operation-specific reason

超级有用的错误信息! python中的相关代码为:

    from Cryptodome.Cipher import AES
    from Cryptodome.Random import get_random_bytes
    from Cryptodome.Cipher import PKCS1_OAEP
    from Cryptodome.PublicKey import RSA

    keyPub = RSA.import_key(base64.b64decode(public_key))
    rsa_cipher = PKCS1_OAEP.new(keyPub)

    aes = get_random_bytes(16)
    aes_cipher = AES.new(aes, AES.MODE_GCM)

    wrapped = str(base64.b64encode(rsa_cipher.encrypt(aes)), 'ascii')

解包的代码是:

    window.crypto.subtle.unwrapKey(
        'raw',
        self.from64(wrapped),
        self.keyPair.privateKey,
        {
            name: "RSA-OAEP"
        },
        {
            name: "AES-GCM"
        },
        false,
        ["decrypt"]
    ).then((aesKey) => {

我对 base64 转换很有信心,因为 Python 代码很乐意使用我的 public 密钥,如果 ASN.1 搞砸了,它就不会。

当我尝试在 Javascript 中解包时,你能看出为什么 Python 中的密钥包失败了吗?

不幸的是,错误消息非常不明确(WebCrypto API 经常出现这种情况)。但是,发布的 WebCrypto 代码有效,如以下代码片段所示。

(async () => {

// For this test a 512 bits RSA key was used. In practice, apply RSA key sizes >= 2048 bits for security reasons!!!
var pkcs8DerB64 = "MIIBVQIBADANBgkqhkiG9w0BAQEFAASCAT8wggE7AgEAAkEA2gdsVIRmg5IH0rG3u3w+gHCZq5o4OMQIeomC1NTeHgxbkrfznv7TgWVzrHpr3HHK8IpLlG04/aBo6U5W2umHQQIDAQABAkEAu7wulGvZFat1Xv+19BMcgl3yhCdsB70Mi+7CH98XTwjACk4T+IYv4N53j16gce7U5fJxmGkdq83+xAyeyw8U0QIhAPIMhbtXlRS7XpkB66l5DvN1XrKRWeB3RtvcUSf30RyFAiEA5ph7eWXbXWpIhdWMoe50yffF7pW+C5z07tzAIH6DKo0CIQCyveSTr917bdIxk2V/xNHxnx7LJuMEC5DcExorNanKMQIgUxHRQU1hNgjIsXXZoKgfaHaa1jUZbmOPlNDvYYVRyS0CIB9ZZee2zubyRla4qN8PQxCJb7DiICmH7nWP7CIvcQwB"
var wrappedKeyB64 = "LVumLtkD20pvX188VR2QTcDRIOuQlw4vxGFvj9vuEOlAsIvOv/vMcC9C6qCVjI4FuzNWEZ7Xo48Pnu2LnB1vcA=="

var wrappedKey = b642ab(wrappedKeyB64);
var wrappingKey = await importWrappingKey(pkcs8DerB64)
var unwrappedKey = await window.crypto.subtle.unwrapKey(
    'raw',
    wrappedKey,
    wrappingKey,
    {name: "RSA-OAEP"},
    {name: "AES-GCM"},
    false,
    ["decrypt"]
);
console.log(unwrappedKey)
 
// Helper --------------------------------------- 
 
async function importWrappingKey(pkcs8DerB64) {     
    return await window.crypto.subtle.importKey(
        "pkcs8",
        b642ab(pkcs8DerB64),
        {name: "RSA-OAEP", hash: "SHA-1"},
        false,
        ["unwrapKey"]
    );
}
  
function b642ab(base64_string){
    return Uint8Array.from(window.atob(base64_string), c => c.charCodeAt(0));
}

function ab2hex(ab) { 
    return Array.prototype.map.call(new Uint8Array(ab), x => ('00' + x.toString(16)).slice(-2)).join('');
}
  
})();

包装密钥是使用发布的 Python 代码创建的。


检查您的代码是否存在差异。一个可能的错误可能是密钥导入。如果 SHA-1 在 importKey() call. The Python code uses SHA-1 by default for the OAEP and MGF1 digest (see PKCS1_OAEP.new()).

的第三个参数中明确指定为摘要,请检查您的代码