How to load a PKCS#12 Digital Certificate with Javascript WebCrypto API(如何使用 Javascript WebCrypto API 加载 PKCS#12 数字证书)
问题描述
I'm trying to sign data using the WebCrypto API, but instead of creating a private/public key and exporting it to pkcs#1 or 8, I would really like to use a user's PKCS#12 to sign data. I've read the W3C spec, but cannot make much of it and can't find any good material on how to do this. Right now I want to leave ActiveX and Java Applets aside. Is there a way to tweak the following:
var buffer = encode(prompt("Please enter your password"));
//TODO:
//implement a prompt for a pfx or cert
return crypto.subtle.importKey("raw", buffer, "PBKDF2", false, usages);
//TODO:
//instead of importing it, ask for the certificate's pass to sign data
//with crypto.subtle.sign
Any pointers?
UPDATE Here's the code I've been working
<script src="forge.min.js"></script>
<script>
var errorsReportedByVerifier;
errorsReportedByVerifier = checkStorage() && checkBrowserAPIs();
if (!errorsReportedByVerifier){
console.log("adding click event");
document.getElementById('btnPfx').addEventListener('click', handlePFXFile, false);
storeVariables();
getVariables();
}
function handlePFXFile(evnt) {
console.log("handling pfx")
//alert(document.getElementById('pfx').value);
//error happens in 1st line
//error object does not accept property replace
//forge.min.js Line 1, Column: 17823
var p12Der = forge.util.decode64(document.getElementById('pfx').valueOf());
//var pkcs12Asn1 = forge.asn1.fromDer(p12Der);
//var pkcs12 = forge.pkcs12.pkcs12FromAsn1(pkcs12Asn1, false, 'pss');
console.log("pkcs12");
}
</script>
Web cryptography api does not support PKCS # 12. You can use a third party library to decode the p12 as forge https://github.com/digitalbazaar/forge#pkcs12 and load privateKey in webcrypto
Reading the PKCS#12 certificate
PKCS#12 is stored in DER, so first read it from a File or use a pre-stored base64
//Reading certificate from a 'file' form field
var reader = new FileReader();
reader.onload = function(e) {
var contents = e.target.result;
var pkcs12Der = arrayBufferToString(contents)
var pkcs12B64 = forge.util.encode64(pkcs12Der);
//do something else...
}
reader.readAsArrayBuffer(file);
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;
}
//p12 certificate stored in Base64 format
var pkcs12Der= forge.util.decode64(pkcs12B64);
Decode PKCS#12 with forge and extract private key
Then decode DER format to ASN1, and let forge reads the content
var pkcs12Asn1 = forge.asn1.fromDer(pkcs12Der);
var pkcs12 = forge.pkcs12.pkcs12FromAsn1(pkcs12Asn1, false, password);
Then get the private key from pkcs12
of the desired certificate (see forge doc) and convert to PKCS # 8 to be imported with webcrypto
// load keypair and cert chain from safe content(s)
for(var sci = 0; sci < pkcs12.safeContents.length; ++sci) {
var safeContents = pkcs12.safeContents[sci];
for(var sbi = 0; sbi < safeContents.safeBags.length; ++sbi) {
var safeBag = safeContents.safeBags[sbi];
// this bag has a private key
if(safeBag.type === forge.pki.oids.keyBag) {
//Found plain private key
privateKey = safeBag.key;
} else if(safeBag.type === forge.pki.oids.pkcs8ShroudedKeyBag) {
// found encrypted private key
privateKey = safeBag.key;
} else if(safeBag.type === forge.pki.oids.certBag) {
// this bag has a certificate...
}
}
}
Convert to PKCS#8
function _privateKeyToPkcs8(privateKey) {
var rsaPrivateKey = forge.pki.privateKeyToAsn1(privateKey);
var privateKeyInfo = forge.pki.wrapRsaPrivateKey(rsaPrivateKey);
var privateKeyInfoDer = forge.asn1.toDer(privateKeyInfo).getBytes();
var privateKeyInfoDerBuff = stringToArrayBuffer(privateKeyInfoDer);
return privateKeyInfoDerBuff;
}
function stringToArrayBuffer(data){
var arrBuff = new ArrayBuffer(data.length);
var writer = new Uint8Array(arrBuff);
for (var i = 0, len = data.length; i < len; i++) {
writer[i] = data.charCodeAt(i);
}
return arrBuff;
}
Import key in Webcrypto
And finally import the key in webcrypto
function _importCryptoKeyPkcs8(privateKey,extractable) {
var privateKeyInfoDerBuff = _privateKeyToPkcs8(privateKey);
//Import the webcrypto key
return crypto.subtle.importKey(
'pkcs8',
privateKeyInfoDerBuff,
{ name: "RSASSA-PKCS1-v1_5", hash:{name:"SHA-256"}},
extractable,
["sign"]);
}
_importCryptoKeyPkcs8(entry.privateKey,extractable).
then(function(cryptoKey) {
//your cryptokey is here!!!
});
Digital signature
With the imported cryptoKey returned from the above method you can sign with webcrypto.
var digestToSign = forge.util.decode64(digestToSignB64);
var digestToSignBuf = stringToArrayBuffer(digestToSign);
crypto.subtle.sign(
{name: "RSASSA-PKCS1-v1_5"},
cryptoKey,
digestToSignBuf)
.then(function(signature){
signatureB64 = forge.util.encode64(arrayBufferToString(signature))
});
I include coding from base64 because data conversions are not trivial
In pkc12 you also have the certification chain if you need to build advanced formats like AdES
这篇关于如何使用 Javascript WebCrypto API 加载 PKCS#12 数字证书的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!
本文标题为:如何使用 Javascript WebCrypto API 加载 PKCS#12 数字证书
- 使用 iframe URL 的 jQuery UI 对话框 2022-01-01
- 如何调试 CSS/Javascript 悬停问题 2022-01-01
- 为什么悬停在委托事件处理程序中不起作用? 2022-01-01
- 是否可以将标志传递给 Gulp 以使其以不同的方式 2022-01-01
- 如何显示带有换行符的文本标签? 2022-01-01
- 我不能使用 json 使用 react 向我的 web api 发出 Post 请求 2022-01-01
- 在不使用循环的情况下查找数字数组中的一项 2022-01-01
- 从原点悬停时触发 translateY() 2022-01-01
- 如何向 ipc 渲染器发送添加回调 2022-01-01
- 为什么我的页面无法在 Github 上加载? 2022-01-01