How to load a PKCS#12 Digital Certificate with Javascript WebCrypto API How to load a PKCS#12 Digital Certificate with Javascript WebCrypto API javascript javascript

How to load a PKCS#12 Digital Certificate with Javascript WebCrypto API


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 fieldvar 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 formatvar 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