CryptoKey ArrayBuffer 到 base64 并返回

Pet*_*lak 4 javascript base64 pem arraybuffer webcrypto-api

我想知道如何解决这个问题。我使用 WebCrypto API 生成 RSA-OAEP 密钥对,然后从导出为 ArrayBuffer 的密钥对中导出 pkcs8 中的私钥,并且我想将此 ArrayBuffer 编码为 base64,以便可以将其存储为 PEM。

\n\n

在此测试示例中,我将密钥导出为 pkcs8 并将此 pkcs8 导入回 CryptoKey。问题是有时有效有时无效。

\n\n

这些是代码的结果:\n注意:仅发生这些状态之一,而不是同时发生。\n注意2:此示例不包含 -----BEGIN PRIVATE KEY----- 前缀和后缀 我仅对钥匙。

\n\n

Case1:未捕获(在承诺中)URIError:URI格式错误(\ xe2 \ x80 \ xa6)b64DecodeUnicode @ try.php:20b64toab @ try.php:70wayBack @ try.php:66(匿名函数)@ try.php:56

\n\n

Case2:未定义:1未捕获(承诺中)DOMException

\n\n

Case3:好的 - 一直可以正常工作。

\n\n

我不知道是什么原因导致了错误,但我认为这与 base64 编码有关。正如我所说,有时私钥生成正常,有时则不行。

\n\n

非常感谢您提前提供的每一个帮助。

\n\n
function b64EncodeUnicode(str) {\n    return btoa(encodeURIComponent(str).replace(/%([0-9A-F]{2})/g, function(match, p1) {\n        return String.fromCharCode(\'0x\' + p1);\n    }));\n}\n\nfunction b64DecodeUnicode(str) {\n    return decodeURIComponent(Array.prototype.map.call(atob(str), function(c) {\n        return \'%\' + (\'00\' + c.charCodeAt(0).toString(16)).slice(-2);\n    }).join(\'\'));\n}\n\nfunction addNewLines(str) {\n    var finalString = \'\';\n    for(var i=0; i < str.length; i++) {\n        finalString += str.substring(0, 64) + \'\\n\';\n        str = str.substring(64);\n    }\n\n     return finalString;\n}\n\nwindow.crypto.subtle.generateKey(\n    {\n        name: "RSA-OAEP",\n        modulusLength: 2048,\n        publicExponent: new Uint8Array([0x01, 0x00, 0x01]),\n        hash: {name: "SHA-256"}\n    },\n    true,\n    ["encrypt", "decrypt"]\n).then(function(keyPair) {\n    window.crypto.subtle.exportKey(\n        "pkcs8",\n        keyPair.privateKey\n    ).then(function(exportedPrivateKey) {\n        var byteArray = new Uint8Array(exportedPrivateKey);\n        console.log(byteArray);\n        var byteString = \'\';\n        for(var i=0; i < byteArray.byteLength; i++) {\n            byteString += String.fromCodePoint(byteArray[i]);\n        }\n\n        wayBack(addNewLines(b64EncodeUnicode(byteString)));\n    });\n});\n\nfunction wayBack(pem) {\n    var lines = pem.split(\'\\n\');\n    var encodedString = \'\';\n    for(var i=0; i < lines.length; i++) {\n        encodedString += lines[i].trim();\n    }\n    b64toab(encodedString);\n}\n\nfunction b64toab(b64) {\n    var byteString = b64DecodeUnicode(b64);\n    console.log(byteString);\n    var byteArray = new Uint8Array(byteString.length);\n    for(var i=0; i < byteString.length; i++) {\n        byteArray[i] = byteString.codePointAt(i);\n    }\n    console.log(byteArray);\n\n    window.crypto.subtle.importKey(\n        "pkcs8",\n        byteArray,\n        {\n            name: "RSA-OAEP",\n            hash: {name: "SHA-256"}\n        },\n        true,\n        ["decrypt"]\n    ).then(function(importedPrivateKey) {\n        console.log(importedPrivateKey);\n    });\n}\n
Run Code Online (Sandbox Code Playgroud)\n

ped*_*ofb 7

当您将字符串拆分为 64 个字符的块时,您忘记了包含 PEM 的最后部分。只需添加finalString += str;addNewLines

function addNewLines(str) {
    var finalString = '';
    for(var i=0; i < str.length; i++) {
        finalString += str.substring(0, 64) + '\n';
        str = str.substring(64);
    }
    finalString += str;

    return finalString;
}
Run Code Online (Sandbox Code Playgroud)

我重构了你的例子来看看发生了什么。如果您认为有用,请使用以下代码

function b64EncodeUnicode(str) {
    return btoa(encodeURIComponent(str).replace(/%([0-9A-F]{2})/g, function(match, p1) {
        return String.fromCharCode('0x' + p1);
    }));
}

function b64DecodeUnicode(str) {
    return decodeURIComponent(Array.prototype.map.call(atob(str), function(c) {
        return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
    }).join(''));
}

function addNewLines(str) {
    var finalString = '';
    for(var i=0; i < str.length; i++) {
        finalString += str.substring(0, 64) + '\n';
        str = str.substring(64);
    }
    finalString += str;

    return finalString;
}

function removeLines(pem) {
    var lines = pem.split('\n');
    var encodedString = '';
    for(var i=0; i < lines.length; i++) {
        encodedString += lines[i].trim();
    }
    return encodedString;
}

function stringToArrayBuffer(byteString){
    var byteArray = new Uint8Array(byteString.length);
    for(var i=0; i < byteString.length; i++) {
        byteArray[i] = byteString.codePointAt(i);
    }
    return byteArray;
}

function  arrayBufferToString(exportedPrivateKey){
    var byteArray = new Uint8Array(exportedPrivateKey);
    var byteString = '';
    for(var i=0; i < byteArray.byteLength; i++) {
        byteString += String.fromCodePoint(byteArray[i]);
    }
    return byteString;
}

window.crypto.subtle.generateKey(
    {
        name: "RSA-OAEP",
        modulusLength: 2048,
        publicExponent: new Uint8Array([0x01, 0x00, 0x01]),
        hash: {name: "SHA-256"}
    },
    true,
    ["encrypt", "decrypt"]
).then(function(keyPair) {
    window.crypto.subtle.exportKey(
        "pkcs8",
        keyPair.privateKey
    ).then(function(exportedPrivateKey) {

        var privateKeyDer = arrayBufferToString(exportedPrivateKey); //pkcs#8 to DER
        var privateKeyB64 = b64EncodeUnicode(privateKeyDer); //btoa(privateKeyDer);
        var privateKeyPEMwithLines = addNewLines(privateKeyB64);  //split PEM into 64 character strings
        var privateKeyPEMwithoutLines = removeLines(privateKeyPEMwithLines);  //join PEM
        var privateKeyDerDecoded = b64DecodeUnicode(privateKeyPEMwithoutLines);  // atob(privateKeyB64);
        var privateKeyArrayBuffer = stringToArrayBuffer(privateKeyDerDecoded);  //DER to arrayBuffer
        window.crypto.subtle.importKey(  //importKEy
            "pkcs8",
            privateKeyArrayBuffer,
            {
                name: "RSA-OAEP",
                hash: {name: "SHA-256"}
            },
            true,
            ["decrypt"]
        ).then(function(importedPrivateKey) {
            console.log(importedPrivateKey);
        });
    });
});
Run Code Online (Sandbox Code Playgroud)