Introduction
Α demonstration on how you create a form, require your user to sign it with their private key, and then verify the signature. I assume that you already know the pki mechanisms. I 've searched in various places, one helpful article is http://stackoverflow.com/questions/36018233/how-to-load-a-pkcs12-digital-certificate-with-javascript-webcrypto-api and others.
The APIs discussed here only work in a secure (or a local file) connection.
Signing with WebCrypto API
Usually we have a form which the user will fill, and we want a digital signature on it. For example, a form like that:
<form name="form1" id="form1" method="post" action="">
<label for="firstname">First name:</label>
<input type="text" name="firstname" id="firstname" required><br>
<label for="lastname">Last name:</label>
<input type="text" name="lastname" id="lastname" required><br>
</form>
We can get the contains of this form with jQuery:
$('#form1').serialize();
We will also have the user choose his PFX file and enter a Private Key password:
<label for="pfx">Select PFX/P12 file:</label><br>
<input name="pfx" type="file" id="pfx" accept=".pfx,.p12" required /><br>
<label for="pfxp">Enter Private Key password:</label><br>
<input name="pfxp" type="password" id="pfxp" /><br>
We now need to read the PFX file with forge.js into a structure:
var fileInput = document.getElementById('pfx');
var file = fileInput.files[0];
var reader = new FileReader();
reader.onload = function(e)
{
var contents = e.target.result;
var pkcs12Der = arrayBufferToString(contents)
var pkcs12B64 = forge.util.encode64(pkcs12Der);
var privateKey;
var pkcs12Asn1 = forge.asn1.fromDer(pkcs12Der);
var password = $('#pfxp').val();
var pkcs12 = forge.pkcs12.pkcs12FromAsn1(pkcs12Asn1, false, password);
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];
if(safeBag.type === forge.pki.oids.keyBag)
{
privateKey = safeBag.key;
}
else
if(safeBag.type === forge.pki.oids.pkcs8ShroudedKeyBag)
{
privateKey = safeBag.key;
}
else
if(safeBag.type === forge.pki.oids.certBag)
{
cert = safeBag.cert;
}
}
}
}
reader.readAsArrayBuffer(file);
This will read our private key and the certificate into two variables, privatekey and cert. Now we need to import it to a WebCrypto PKCS#8:
function importCryptoKeyPkcs8(privateKey,extractable)
{
var privateKeyInfoDerBuff = privateKeyToPkcs8(privateKey);
return crypto.subtle.importKey(
'pkcs8',
privateKeyInfoDerBuff,
{ name: "RSASSA-PKCS1-v1_5", hash:{name:"SHA-256"}},
extractable,
["sign"]);
}
And now we can sign:
importCryptoKeyPkcs8(privateKey,true).then(function(cryptoKey)
{
var digestToSignBuf = stringToArrayBuffer(ser);
var pem = forge.pki.certificateToPem(cert);
$('#pfxc').val(forge.util.encode64(pem));
crypto.subtle.sign(
{name: "RSASSA-PKCS1-v1_5"},
cryptoKey,
digestToSignBuf)
.then(function(signature){
sign = arrayBufferToString(signature);
signatureB64 = forge.util.encode64(sign);
});
});
And we can store the original text, the certificate as PEM and the signature as base64.
Verifying the signature
For verification, we need the three items (data,signature,certificate):
function publicKeyToPkcs8(pk)
{
var subjectPublicKeyInfo = forge.pki.publicKeyToAsn1(pk);
var der = forge.asn1.toDer(subjectPublicKeyInfo).getBytes();
return stringToArrayBuffer(der);
}
function Verify()
{
var pem = ...
var signature64 = ...
var signature = forge.util.decode64(signature64);
var data = ...
var cert = forge.pki.certificateFromPem(pem);
window.crypto.subtle.importKey("spki",publicKeyToPkcs8(cert.publicKey),
{
name: "RSASSA-PKCS1-v1_5",
hash: {name: "SHA-256"},
},
false,
["verify"]
).then(function(k)
{
window.crypto.subtle.verify(
{
name: "RSASSA-PKCS1-v1_5",
},
k,
stringToArrayBuffer(signature),
stringToArrayBuffer(data)
).then(function(isvalid)
{
if (!isvalid)
{
}
else
{
}
}).catch(function(err)
{
});
}
);
}
The HTML file
You can experiment with the attached HTML file. As of this release, not all certificates work with this procedure. If you find a bug, let me know!
History
06 - 07 - 2016 : First release.