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()
).
的第三个参数中明确指定为摘要,请检查您的代码
我的 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()
).