Sunday, May 19, 2024
16
rated 0 times [  20] [ 4]  / answers: 1 / hits: 17957  / 8 Years ago, tue, march 15, 2016, 12:00:00

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>

More From » digital-signature

 Answers
69

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


[#62930] Sunday, March 13, 2016, 8 Years  [reply] [flag answer]
Only authorized users can answer the question. Please sign in first, or register a free account.
jameson

Total Points: 534
Total Questions: 103
Total Answers: 102

Location: Lithuania
Member since Fri, Sep 4, 2020
4 Years ago
;