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 数字证书
基础教程推荐
- 悬停时滑动输入并停留几秒钟 2022-01-01
- 动态更新多个选择框 2022-01-01
- 我什么时候应该在导入时使用方括号 2022-01-01
- 当用户滚动离开时如何暂停 youtube 嵌入 2022-01-01
- Karma-Jasmine:如何正确监视 Modal? 2022-01-01
- 角度Apollo设置WatchQuery结果为可用变量 2022-01-01
- 响应更改 div 大小保持纵横比 2022-01-01
- 在 JS 中获取客户端时区(不是 GMT 偏移量) 2022-01-01
- 在for循环中使用setTimeout 2022-01-01
- 有没有办法使用OpenLayers更改OpenStreetMap中某些要素 2022-09-06