javascript digital-signature digital-certificate webcrypto-api

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>