Cómo cargar un certificado digital PKCS#12 con Javascript WebCrypto API
digital-signature digital-certificate (1)
La API de criptografía web no admite PKCS # 12. Puede usar una biblioteca de terceros para decodificar el p12 como forge https://github.com/digitalbazaar/forge#pkcs12 y cargar privateKey en webcrypto
Lectura del certificado PKCS # 12
PKCS # 12 se almacena en DER, así que primero léalo desde un archivo o use una base pre-almacenada64
//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);
Decodifique PKCS # 12 con falsificar y extraer clave privada
Luego decodifique el formato DER en ASN1 y deje que forge lea el contenido
var pkcs12Asn1 = forge.asn1.fromDer(pkcs12Der);
var pkcs12 = forge.pkcs12.pkcs12FromAsn1(pkcs12Asn1, false, password);
Luego obtenga la clave privada de
pkcs12
del certificado deseado (consulte el documento de falsificación) y conviértalo a PKCS # 8 para importarlo con 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...
}
}
}
Convertir a 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;
}
Importar clave en Webcrypto
Y finalmente importa la clave en 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!!!
});
Firma digital
Con la clave criptográfica importada devuelta del método anterior, puede firmar con 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))
});
Incluyo codificación de base64 porque las conversiones de datos no son triviales
En pkc12 también tiene la cadena de certificación si necesita crear formatos avanzados como AdES
Estoy tratando de firmar datos usando la API de WebCrypto, pero en lugar de crear una clave privada / pública y exportarla a pkcs # 1 u 8, realmente me gustaría usar el PKCS # 12 de un usuario para firmar datos. He leído la especificación W3C, pero no puedo sacarle mucho provecho y no puedo encontrar ningún buen material sobre cómo hacerlo. En este momento quiero dejar a un lado los Applets ActiveX y Java. ¿Hay alguna manera de modificar lo siguiente:
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
¿Algún puntero?
ACTUALIZACIÓN Aquí está el código que he estado trabajando
<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>