问题描述
我正在尝试从 Webcrypto 环境中 PEM 编码的给定私钥中导出 RSA 公钥。我在网上和 SO 上搜索了很多,但似乎没有简单的“内置”解决方案可用。
目前我分 5 个步骤完成,我无法相信没有程序化解决方案 [不使用额外的外部库]。
这是我导出公钥的步骤
- 导入 pem 编码的私钥(去掉头部和 页脚行,将剩余数据从 Base64 编码转换为 二进制格式
- 导出jwk编码的私钥
- 删除jwk对象中的5个元素:“d”; “dp”、“dq”、“q”、“qi”和“p”
- 将短路后的jwk导入为jwk编码中的公钥
- 将公钥导出为带有附加页眉和页脚行的“SPKI”。
使用的私钥是PKCS8格式的未加密的示例RSA私钥,这是我的测试结果:
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA8EmWJUZ/Osz4vXtUU2S+
0M4BP9+s423gjMjoX+qP1iCnlcRcFWxthQGN2CWSMZwR/vY9V0un/nsIxhZSWOH9
iKzqUtZD4jt35jqOTeJ3PCSr48JirVDNLet7hRT37Ovfu5iieMN7ZNpkjeIG/CfT
/QQl7R+kO/EnTmL3QjLKQNV/HhEbHS2/44x7PPoHqSqkOvl8GW0qtL39gTLWgAe8
01/w5PmcQ38CKG0oT2gdJmJqIxNmAEHkatYGHcMDtXRBpOhOSdraFj6SmPyHEmLB
ishaq7Jm8NPPNK9QcEQ3q+ERa5M6eM72PpF93g2p5cjKgyzzfoIV09Zb/LJ2aW2g
QwIDAQAB
-----END PUBLIC KEY-----
此密钥与原始公钥匹配。
我的问题:Webcrypto 上还有其他可用的编程解决方案吗?
这是我没有异常处理的粗略代码(derive.html):
<!DOCTYPE html>
<html lang="en">
<head>
<Meta charset="utf-8">
<Meta name="viewport" content="width=device-width">
<title>SO Webcrypto derive an RSA public key from a private key in PEM encoding</title>
<style>
body {background-color: powderblue;}
h1 {color: blue;}
h2 {font-size: 200%;}
p {font-size: 150%; }
button {height: 100%; font-size: 150%;}
textarea {font-size: 150%;}
input[type="text"] {font-size: 100%; }
input[type="button"] {font-size: 100%; }
</style>
</head>
<body>
<h1>SO Webcrypto derive an RSA public key from a private key in PEM encoding</h1>
<hr>
<p>my sample private key (in PEM encoding):</p>
<textarea name="privateKeyPem" id="privateKeyPem" rows="10" cols="64">-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDwSZYlRn86zPi9e1RTZL7QzgE/36zjbeCMyOhf6o/WIKeVxFwVbG2FAY3YJZIxnBH+9j1XS6f+ewjGFlJY4f2IrOpS1kPiO3fmOo5N4nc8JKvjwmKtUM0t63uFFPfs69+7mKJ4w3tk2mSN4gb8J9P9BCXtH6Q78SdOYvdCMspA1X8eERsdLb/jjHs8+gepKqQ6+XwZbSq0vf2BMtaAB7zTX/Dk+ZxDfwIobShPaB0mYmojE2YAQeRq1gYdwwO1dEGk6E5J2toWPpKY/IcSYsGKyFqrsmbw0880r1BwRDer4RFrkzp4zvY+kX3eDanlyMqDLPN+ghXT1lv8snZpbaBDAgMBAAECggEBAIVxmHzjBc11/73bPB2EGaSEg5UhdzZm0wncmZCLB453XBqEjk8nhDsVfdzIimsEVEowHijYz1c4pMq9osXR26eHwCp47AI73H5zjowadPVluEAot/xgn1IdMN/boURmSj44qiI/DcwYrTdOi2qGA+jD4PwrUl4nsxiJRZ/x7PjLhMzRbvDxQ4/Q4ThYXwoeGiIBBK/iB3Z5eR7lFa8E5yAaxM2QP9PENBr/OqkGXLWVqA/YTxs3gAvkUjMhlScOi7PMwRX9HsrAeLKbLuC1KJv1p2THUtZbOHqrAF/uwHajygUblFaa/BTckTN7PKSVIhp7OihbD04bSRrh+nOilcECgYEA/8atV5DmNxFrxF1PODDjdJPNb9pzNrDF03TiFBZWS4Q+2JazyLGjZzhg5Vv9RJ7VcIjPAbMy2Cy5BUffEFE+8ryKVWfdppxpPYOwHCJSw4Bqqdj0Pmp/xw928ebrnUoCzdkUqYYpRWx0T7YVRoA9RiBfQiVHhuJBSDPYJPoP34kCgYEA8H9wLE5L8raUn4NYYRuUVMa+1k4Q1N3XBixm5cccc/Ja4LVvrnWqmFOmfFgpVd8BcTGaPSsqfA4j/oEQp7tmjZqggVFqiM2mJ2YEv18cY/5kiDUVYR7VWSkpqVOkgiX3lK3UkIngnVMGGFnoIBlfBFF9uo02rZpC5o5zebaDimsCgYAE9d5wv0+nq7/STBj4NwKCRUelrsnjOqRriG3GA/TifAsX+jw8XS2VF+PRLuqHhSkQiKazGr2Wsa9Y6d7qmxjEbmGkbGJBC+AioEYvFX9TaU8oQhvihgA6ZRNid58EKuZJBbe/3ek4/nR3A0oAVwZZMNGIH972P7cSZmb/uJXMOQKBgQCsFaQAL+4sN/TUxrkAkylqF+QJmEZ26l2nrzHZjMWROYNJcsn8/XkaEhD4vGSnazCu/B0vU6nMppmezF9Mhc112YSrw8QFK5GOc3NGNBoueqMYy1MG8Xcbm1aSMKVv8xbarh+BZQbxy6x61CpCfaT9hAoA6HaNdeoU6y05lBz1DQKBgAbYiIk56QZHeoZKiZxy4eicQS0sVKKRb24ZUd+04cNSTfeIuuXZrYJ48Jbr0fzjIM3EfHvLgh9rAZ+aHe/L84Ig17KiExe+qyYHjut/SC0wODDtzM/jtrpqyYa5JoEpPIaUSgPuTH/WhO3cDsx63PIW4/CddNs8mCSBOqTnoaxh
-----END PRIVATE KEY-----</textarea>
<br>
<button id="deriveRsaPublicKeyFromPrivateKeyPem" onclick="deriveRsaPublicKeyFromPrivateKeyPem()">derive public key</button>
<br>
<p>exported public key in PEM encoding:</p>
<textarea name="exportedPublicKeyPem" id="exportedPublicKeyPem" rows="10" cols="64">the exported key comes here...</textarea>
</body>
<SCRIPT LANGUAGE="JavaScript">
let privateKeyInternal;
let publicKeyInternal;
let publicKeyJwkInternal;
/*
Convert a string into an ArrayBuffer
from https://developers.google.com/web/updates/2012/06/How-to-convert-ArrayBuffer-to-and-from-String
*/
function str2ab(str) {
const buf = new ArrayBuffer(str.length);
const bufView = new Uint8Array(buf);
for (let i = 0,strLen = str.length; i < strLen; i++) {
bufView[i] = str.charCodeAt(i);
}
return buf;
}
function spkiToPEM(keydata){
var keydataS = arrayBufferToString(keydata);
var keydataB64 = window.btoa(keydataS);
var keydataB64Pem = formatSpkiAsPem(keydataB64);
return keydataB64Pem;
}
function arrayBufferToString( buffer ) {
var binary = '';
var bytes = new Uint8Array( buffer );
var len = bytes.byteLength;
for (var i = 0; i < len; i++) {
binary += String.fromCharCode( bytes[ i ] );
}
return binary;
}
function formatSpkiAsPem(str) {
var finalString = '-----BEGIN PUBLIC KEY-----\n';
while(str.length > 0) {
finalString += str.substring(0,64) + '\n';
str = str.substring(64);
}
finalString = finalString + "-----END PUBLIC KEY-----";
return finalString;
}
// step 5 export the rsa public key in pem encoding
function exportPublicKeyPem() {
var publicKey = publicKeyInternal;
window.crypto.subtle.exportKey(
"spki",// can be "jwk" (public or private),"spki" (public only),or "pkcs8" (private only)
publicKey // can be a publicKey or privateKey,as long as extractable was true
)
.then(function(keydata){
var publicKeyPem = spkiToPEM(keydata);
document.getElementById('exportedPublicKeyPem').value = publicKeyPem;
})
.catch(function(err){
console.error(err);
});
}
// step 4 import the rsa public key in jwk encoding
function importPublicKeyJwk() {
const binaryJwk = publicKeyJwkInternal;
window.crypto.subtle.importKey(
"jwk",or "pkcs8" (private only)
binaryJwk,{ // these are the algorithm options
name: "RSASSA-PKCS1-v1_5",hash: {name: "SHA-256"},//can be "SHA-1","SHA-256","SHA-384",or "SHA-512"
},true,//whether the key is extractable (i.e. can be used in exportKey)
["verify"] //"verify" for public key import,"sign" for private key imports
)
.then(function(publicKey){
//returns a publicKey (or privateKey if you are importing a private key)
publicKeyInternal = publicKey;
exportPublicKeyPem();
})
.catch(function(err){
console.error(err);
});
}
// step 2 export the rsa private key in jwk encoding
function exportPrivateKeyJwk() {
var privateKey = privateKeyInternal;
window.crypto.subtle.exportKey(
"jwk",//can be "jwk" (public or private),or "pkcs8" (private only)
privateKey //can be a publicKey or privateKey,as long as extractable was true
)
.then(function(keydata){
// step 3 strip off some jwk elements
// derive the public key
// by removing private data from JWK
delete keydata.d;
delete keydata.dp;
delete keydata.dq;
delete keydata.q;
delete keydata.qi;
delete keydata.p;
keydata.key_ops = ["encrypt","verify"];
publicKeyJwkInternal = keydata; // save the keydata in publicKeyJwkInternal
importPublicKeyJwk();
})
.catch(function(err){
console.error(err);
});
}
// step 1: import rsa private key in pem encoding
function deriveRsaPublicKeyFromPrivateKeyPem() {
var privateKeyPem = document.getElementById('privateKeyPem').value;
var pem = privateKeyPem;
// fetch the part of the PEM string between header and footer
const pemHeader = "-----BEGIN PRIVATE KEY-----";
const pemFooter = "-----END PRIVATE KEY-----";
const pemContents = pem.substring(pemHeader.length,pem.length - pemFooter.length);
// base64 decode the string to get the binary data
const binaryDerString = window.atob(pemContents);
// convert from a binary string to an ArrayBuffer
const binaryDer = str2ab(binaryDerString);
// import rsa private key
window.crypto.subtle.importKey(
"pkcs8",or "pkcs8" (private only)
binaryDer,//whether the key is extractable (i.e. can be used in exportKey)
["sign"] //"verify" for public key import,"sign" for private key imports
)
.then(function(privateKey){
privateKeyInternal = privateKey;
// Now export the private key as jwk
exportPrivateKeyJwk(); // the key is taken from privateKeyInternal
})
.catch(function(err){
console.error(err);
});
}
</SCRIPT>
</html>
解决方法
暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!
如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。
小编邮箱:dio#foxmail.com (将#修改为@)