使用 JavaScript 签名和验证表单





5.00/5 (7投票s)
客户端签名和验证。
引言
演示如何创建一个表单,要求用户使用他们的私钥对其进行签名,然后验证签名。我假设你已经了解 PKI 机制。 我在各种地方搜索过,一篇有帮助的文章是 http://stackoverflow.com/questions/36018233/how-to-load-a-pkcs12-digital-certificate-with-javascript-webcrypto-api 以及其他文章。
这里讨论的 API 仅在安全(或本地文件)连接中有效。
使用 WebCrypto API 签名
通常我们有一个用户将要填写表单,并且希望对其进行数字签名。例如,像这样的表单
//
<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>
//
我们可以使用 jQuery 获取此表单的内容
//
$('#form1').serialize();
//
我们还需要让用户选择他的 PFX 文件并输入私钥密码
<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>
现在我们需要使用 forge.js 将 PFX 文件读取到一个结构中
// Get PFX var fileInput = document.getElementById('pfx'); var file = fileInput.files[0]; // Read it 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); // load keys 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) { //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... cert = safeBag.cert; } } } } reader.readAsArrayBuffer(file);
这将把我们的私钥和证书读取到两个变量中,分别是 privatekey 和 cert。 现在我们需要将其导入到 WebCrypto PKCS#8
function importCryptoKeyPkcs8(privateKey,extractable) { var privateKeyInfoDerBuff = privateKeyToPkcs8(privateKey); //Importa la clave en la webcrypto return crypto.subtle.importKey( 'pkcs8', privateKeyInfoDerBuff, { name: "RSASSA-PKCS1-v1_5", hash:{name:"SHA-256"}}, extractable, ["sign"]); }
现在我们可以签名了
importCryptoKeyPkcs8(privateKey,true).then(function(cryptoKey) { // Imported! // Empty stuff 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); }); });
我们可以存储原始文本、PEM 格式的证书以及 base64 格式的签名。
验证签名
为了验证,我们需要三个项目(数据、签名、证书)
// From Public Key to a PKCS#8 function publicKeyToPkcs8(pk) { var subjectPublicKeyInfo = forge.pki.publicKeyToAsn1(pk); var der = forge.asn1.toDer(subjectPublicKeyInfo).getBytes(); return stringToArrayBuffer(der); } // Verify it function Verify() { var pem = ... var signature64 = ... var signature = forge.util.decode64(signature64); var data = ... var cert = forge.pki.certificateFromPem(pem); // Import the certifcate 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, //from generateKey or importKey above stringToArrayBuffer(signature), //ArrayBuffer of the signature stringToArrayBuffer(data) //ArrayBuffer of the data ).then(function(isvalid) { //returns a boolean on whether the signature is true or not if (!isvalid) { } else { // Valid signature } }).catch(function(err) { // Invalid sig or something not worked }); } ); }
HTML 文件
你可以使用附带的 HTML 文件进行实验。 截至此版本,并非所有证书都适用于此过程。 如果你发现错误,请告诉我!
历史
06 - 07 - 2016 : 首次发布。