简体   繁体   English

CryptoKey ArrayBuffer到base64和Back

[英]CryptoKey ArrayBuffer to base64 and Back

I was wondering how do I solve this problem. 我想知道如何解决这个问题。 I generate RSA-OAEP keypair using WebCrypto API, then I export private key in pkcs8 from the keypair which exports as ArrayBuffer and I want to encode this ArrayBuffer into base64 so I can store it as a PEM. 我使用WebCrypto API生成RSA-OAEP密钥对,然后我从密钥对导出pkcs8中的私钥,该密钥对作为ArrayBuffer导出,我想将此ArrayBuffer编码为base64,因此我可以将其存储为PEM。

In this testing example I am exporting key as pkcs8 and importing this pkcs8 back to CryptoKey. 在此测试示例中,我将密钥导出为pkcs8并将此pkcs8导回到CryptoKey。 The problem is that sometimes it works and sometimes it does not. 问题在于它有时会起作用,有时却不起作用。

These are results of the code: NOTE: Only happens one of these states not all at once. 这些是代码的结果:注意:只有这些状态中的一个不会同时发生。 NOTE2: This example does not contain -----BEGIN PRIVATE KEY----- prefix and suffix I am only encoding the key. 注2:此示例不包含----- BEGIN PRIVATE KEY -----前缀和后缀我只对密钥进行编码。

Case1: Uncaught (in promise) URIError: URI malformed(…)b64DecodeUnicode @ try.php:20b64toab @ try.php:70wayBack @ try.php:66(anonymous function) @ try.php:56 案例1:未捕获(在承诺中)URIError:URI格式错误(...)b64DecodeUnicode @ try.php:20b64toab @ try.php:70wayBack @ try.php:66(匿名函数)@ try.php:56

Case2: undefined:1 Uncaught (in promise) DOMException Case2:undefined:1 Uncaught(在promise中)DOMException

Case3: OK - works all the way back. 案例3:好的 - 一直回来工作。

I don't know what causes the errors but I think it has something to do with base64 encoding. 我不知道导致错误的原因,但我认为它与base64编码有关。 As I said sometimes private key generates OK and sometimes not. 正如我所说,有时私钥生成正常,有时不生成。

Thank you very much for every help in advance. 非常感谢你提前帮助。

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);
    }

     return finalString;
}

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 byteArray = new Uint8Array(exportedPrivateKey);
        console.log(byteArray);
        var byteString = '';
        for(var i=0; i < byteArray.byteLength; i++) {
            byteString += String.fromCodePoint(byteArray[i]);
        }

        wayBack(addNewLines(b64EncodeUnicode(byteString)));
    });
});

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

function b64toab(b64) {
    var byteString = b64DecodeUnicode(b64);
    console.log(byteString);
    var byteArray = new Uint8Array(byteString.length);
    for(var i=0; i < byteString.length; i++) {
        byteArray[i] = byteString.codePointAt(i);
    }
    console.log(byteArray);

    window.crypto.subtle.importKey(
        "pkcs8",
        byteArray,
        {
            name: "RSA-OAEP",
            hash: {name: "SHA-256"}
        },
        true,
        ["decrypt"]
    ).then(function(importedPrivateKey) {
        console.log(importedPrivateKey);
    });
}

You forgot to include the last part of PEM when you split the string in blocks of 64 characters. 当您将字符串拆分为64个字符的块时,您忘记包含PEM的最后一部分。 Just add finalString += str; 只需添加finalString += str; to addNewLines 添加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;
}

I have refactorized your example to see what is happening. 我已经重构了你的例子,看看发生了什么。 Use the below code if you consider it useful 如果您认为它有用,请使用以下代码

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);
        });
    });
});

I am posting additional working code: (NOTE: Code is without -----BEGIN PRIVATE KEY----- and ----- END PRIVATE KEY ----- base64 only) 我发布了额外的工作代码:(注意:代码没有----- BEGIN PRIVATE KEY -----和----- END PRIVATE KEY ----- base64 only)

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

    return finalString;
}

function removeLines(str) {
    return str.replace("\n", "");
}

function arrayBufferToBase64(arrayBuffer) {
    var byteArray = new Uint8Array(arrayBuffer);
    var byteString = '';
    for(var i=0; i < byteArray.byteLength; i++) {
        byteString += String.fromCharCode(byteArray[i]);
    }
    var b64 = window.btoa(byteString);

    return b64;
}

function base64ToArrayBuffer(b64) {
    var byteString = window.atob(b64);
    var byteArray = new Uint8Array(byteString.length);
    for(var i=0; i < byteString.length; i++) {
        byteArray[i] = byteString.charCodeAt(i);
    }

    return byteArray;
}

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 pem = addNewLines(arrayBufferToBase64(exportedPrivateKey));
        importKey(pem);
    });
});

function importKey(b64) {
    b64 = removeLines(b64);
    arrayBuffer = base64ToArrayBuffer(b64);

    window.crypto.subtle.importKey(
        "pkcs8",
        arrayBuffer,
        {
            name: "RSA-OAEP",
            hash: {name: "SHA-256"}
        },
        true,
        ["decrypt"]
    ).then(function(importedPrivateKey) {
        console.log(importedPrivateKey);
    });
}

UPDATE: I wrote a little crypto library that you can use for PEM converting and many more. 更新:我写了一个小的加密库,你可以用它来进行PEM转换等等。 https://github.com/PeterBielak/OpenCrypto https://github.com/PeterBielak/OpenCrypto

Usage example: 用法示例:

var crypt = new OpenCrypto();

crypt.getKeyPair().then(function(keyPair) {
    crypt.cryptoPrivateToPem(keyPair.privateKey).then(function(pemPrivateKey) {
        console.log(pemPrivateKey);
    });
});

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM