数字签名与验证

在数字安全领域,数字签名是一种验证数据完整性和身份验证的重要技术。本文将介绍如何使用WebCrypto API在Web环境中进行数字签名和验证。

什么是数字签名

数字签名是一种使用私钥对数据进行加密的技术,任何人都可以使用对应的公钥对签名进行解密。如果解密后的数据与原始数据一致,则签名有效,从而证明了数据的完整性和签名者的身份。

WebCrypto API

WebCrypto API是现代浏览器提供的一个原生JavaScript API,用于执行基本的加密操作,如生成密钥、签名和验证签名等。它支持多种加密算法,包括RSA、ECDSA等。

加载PFX文件

通常,私钥和证书存储在PFX(Personal Information Exchange)文件中。要使用WebCrypto API进行签名,首先需要加载PFX文件并提取私钥和证书。以下是加载PFX文件的HTML表单代码:

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

读取PFX文件

使用forge.js库可以读取PFX文件并将其转换为结构化数据。以下是读取PFX文件的JavaScript代码:

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);

这段代码将读取私钥和证书,并存储在变量privateKeycert中。

导入私钥到WebCrypto

将私钥导入到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'] ); }

导入成功后,可以使用私钥进行签名:

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); }); });

这段代码将签名结果存储为Base64编码的字符串。

验证签名

验证签名需要数据、签名和证书。以下是验证签名的JavaScript代码:

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) { // Invalid signature } else { // Valid signature } }).catch(function(err) { // Invalid signature or something not worked }); }); }

这段代码将验证签名是否有效。

沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485