RSA SubtleCrypto 解密期间的 DOMException

Posted

技术标签:

【中文标题】RSA SubtleCrypto 解密期间的 DOMException【英文标题】:DOMException during RSA SubtleCrypto decrypt 【发布时间】:2021-06-30 09:05:32 【问题描述】:

我正在尝试使用SubtleCrypto 加密字符串、存储加密字符串并再次解密该字符串,所有这些都使用生成的 RSA-OAEP 密钥对。

以下代码在解密阶段会产生 DOMException,但我似乎无法获得有关该错误的任何详细信息。我尝试使用“SHA-1”进行散列,但有同样的问题。

有什么提示吗?

let encoder = new TextEncoder();
let decoder = new TextDecoder('utf-8');

// Generate a key pair

let keyPair = await window.crypto.subtle.generateKey(
    
        name: "RSA-OAEP",
        modulusLength: 4096,
        publicExponent: new Uint8Array([1, 0, 1]),
        hash: "SHA-256"
    ,
    true,
    ["encrypt", "decrypt"]
);

let publicKey = await crypto.subtle.exportKey('jwk', keyPair.publicKey);
let privateKey = await crypto.subtle.exportKey('jwk', keyPair.privateKey);

// Encrypt a string

let encodedSecret = encoder.encode("MYSECRETVALUE");
let pubcryptokey = await window.crypto.subtle.importKey(
    'jwk',
    publicKey,
    
        name: "RSA-OAEP",
        hash: "SHA-256"
    ,
    false,
    ["encrypt"]
);
let encrypted = await window.crypto.subtle.encrypt(
    
        name: "RSA-OAEP"
    ,
    pubcryptokey,
    encodedSecret
);
let encDV = new DataView(encrypted);
let ct = decoder.decode(encDV);

// Decrypt the string

let encodedCiphertext = encoder.encode(ct);
let privcryptokey = await window.crypto.subtle.importKey(
    'jwk',
    privateKey,
    
        name: "RSA-OAEP",
        hash: "SHA-256"
    ,
    false,
    ["decrypt"]
);
console.log("Before decrypt");
let decrypted = await window.crypto.subtle.decrypt(
    
        name: "RSA-OAEP"
    ,
    privcryptokey,
    encodedCiphertext
);
console.log("After decrypt");
let decDV = new DataView(decrypted);
let pt = decoder.decode(decDV);

console.log(pt);

【问题讨论】:

对于现代加密算法,例如 WebCrypto 中的加密算法,无论是 RSA 还是对称,密文是二进制,因此尝试将其解码为 UTF8 然后再次对其进行编码会改变其中的大部分变成垃圾,对于现代加密货币来说,即使是一点点改变也会破坏你的数据。不要那样做。如果您无法按原样存储或处理它,UInt8Array,并且需要一个字符串,请使用保留二进制的数据编码(不是字符编码)之一,如 base64 或十六进制。此外,由于它不包含大于字节的元素,DataView 是无用且不必要的。 【参考方案1】:

问题在于密文的 UTF-8 编码/解码,这会破坏数据。如果要将任意二进制数据(例如密文)存储在字符串中,则必须使用 binary-to-text encoding(例如 Base64),参见例如here.

如果解决了这个问题,解密就可以了:

(async () => 

    let encoder = new TextEncoder();
    let decoder = new TextDecoder('utf-8');

    // Generate a key pair

    let keyPair = await window.crypto.subtle.generateKey(
        
            name: "RSA-OAEP",
            modulusLength: 4096,
            publicExponent: new Uint8Array([1, 0, 1]),
            hash: "SHA-256"
        ,
        true,
        ["encrypt", "decrypt"]
    );

    let publicKey = await crypto.subtle.exportKey('jwk', keyPair.publicKey);
    let privateKey = await crypto.subtle.exportKey('jwk', keyPair.privateKey);

    // Encrypt a string

    let encodedSecret = encoder.encode("MYSECRETVALUE");
    let pubcryptokey = await window.crypto.subtle.importKey(
        'jwk',
        publicKey,
        
            name: "RSA-OAEP",
            hash: "SHA-256"
        ,
        false,
        ["encrypt"]
    );
    
    let encrypted = await window.crypto.subtle.encrypt(
        
            name: "RSA-OAEP"
        ,
        pubcryptokey,
        encodedSecret
    );
    
    //let encDV = new DataView(encrypted);         // Bug: UTF-8 decoding damages the ciphertext
    //let ct = decoder.decode(encDV);
    let ct = ab2b64(encrypted);                    // Fix: Use a binary to text encoding like Base64
    console.log(ct.replace(/(.48)/g,'$1\n'));

    // Decrypt the string

    //let encodedCiphertext = encoder.encode(ct);  // Bug: s. above
    let encodedCiphertext = b642ab(ct);            // Fix: s. above 
    
    let privcryptokey = await window.crypto.subtle.importKey(
        'jwk',
        privateKey,
        
            name: "RSA-OAEP",
            hash: "SHA-256"
        ,
        false,
        ["decrypt"]
    );
    
    console.log("Before decrypt");
    let decrypted = await window.crypto.subtle.decrypt(
        
            name: "RSA-OAEP"
        ,
        privcryptokey,
        encodedCiphertext
    );
    console.log("After decrypt");

    let decDV = new DataView(decrypted);
    let pt = decoder.decode(decDV);

    console.log(pt);
    
)();

// https://***.com/a/11562550/9014097
function ab2b64(arrayBuffer) 
    return btoa(String.fromCharCode.apply(null, new Uint8Array(arrayBuffer)));


// https://***.com/a/41106346 
function b642ab(base64string)
    return Uint8Array.from(atob(base64string), c => c.charCodeAt(0));

【讨论】:

以上是关于RSA SubtleCrypto 解密期间的 DOMException的主要内容,如果未能解决你的问题,请参考以下文章

java 加解密

加密微妙解密参数未键入“CryptoKey”

rsa解密错误

rsa解密乱码,重启应用后正常

存储 RSA 私钥 Android

Delphi RSA加解密 (RSA公钥加密,私钥解密)(RSA私钥加密,公钥解密)MD5加密SHA加密