JavaScript:实现电子签名的一种非常简单的方法

在JS中有一个非常简单的方法来制作电子签名,可以像校验和(或散列)一样轻松地进行处理。

所以如果是这样的情况:

------------------------------------ Locked section for client ------------------------------------ | YYYY.MM.DD ......................| | ........... ......................| | Bla bla bla ......................| | Bla bla bla Bla bla bla..Bla bla .| | Bla bla bla Bla bla bla..Bla bla .| | Bla bla bla Bla bla bla..Bla bla .| | Bla bla bla Bla bla bla..Bla bla .| | Bla bla bla ......................| | Bla bla bla ......................| ------------------------------------ | HASH: HA2S2EM3CA12EDIAJED | ------------------------------------ "Open" comment textfield for clients ------------------------------------ | HE34ADOV2DSASA452123 ...(signer A)| | GHEAVOED12dHSAV2123J ...(signer B)| 

HE34ADOV2DSASA452123由签名者拥有的私钥生成。

然后HE34ADOV2DSASA452123的解密(使用某种公钥)会给出类似于YYYY.MM.DD Bla bla bla或返回该节的散列( HA2S2EM3CA12EDIAJED )的内容。

同样, GHEAVOED12dHSAV2123J的解密会给出类似于YYYY.MM.DD Bla bla bla或返回该节的散列( HA2S2EM3CA12EDIAJED )的内容。


请注意,这是不要求恶魔大师的头脑,只是反对“外行”欺诈…

尝试节点模块XML高级电子签名 。

它使用Web Crypto进行encryption操作。 因此,它可以在浏览器和Node.js中使用。

根据定义,电子签名要比散列复杂得多。 虽然您可以从消息中生成散列,但对于数字签名,您通常需要私钥,并强制只有知道私钥的人才能生成有效签名。 接下来你显然需要相应的公钥来validation消息。

所以通常你有3个步骤:

  1. 您需要创build一个公钥/私钥对。
  2. 您需要在可信的系统上使用私钥签名。 只有这个系统应该有私钥。
  3. 你用公钥validation消息,确保信任系统已经签名。

所以第一个有趣的问题是,你想如何存储/分发你的密钥,以及你想要签署/validation什么样的系统? 使用不同的编程语言进行签名和validation是常见的用例。 然而,现在让我们假设你想在JavaScript中做所有事情。

还要记住一个简单的问题:如果您不能确定邮件来自有效的发件人,那么如何确保您用来validation邮件的公钥是否来自有效的发件人? 你可以用你的软件分发它,但是对于一个网站你必须相信你的TLS连接,如果你信任你的TLS连接,你也可以使用它来传输消息本身。

我认为最好的解决scheme是使用WebencryptionAPI 。 在这里你可以find有用的例子。


首先你需要生成你的密钥

 async function generateKey() { const key = await window.crypto.subtle.generateKey({ name: "RSASSA-PKCS1-v1_5", modulusLength: 4096, publicExponent: new Uint8Array([0x01, 0x00, 0x01]), hash: { name: "SHA-512" }, }, true, ["sign", "verify"] ); return { privateKey: await window.crypto.subtle.exportKey( "jwk", key.privateKey, ), publicKey: await window.crypto.subtle.exportKey( "jwk", key.publicKey, ), }; } 

window.crypto.subtle.exportKey为您提供了一个JSON,您可以通过JSON.strinify / JSON.parse来回转换为一个简单的string,以便您可以将其存储在某处。


下一步是签署你的消息。 注意你可能想在不同的时间做这个,然后生成密钥。

 async function sign(privateKeyJwk, message) { const privateKey = await window.crypto.subtle.importKey("jwk", privateKeyJwk, { name: "RSASSA-PKCS1-v1_5", hash: {name: "SHA-512"}, }, false, ['sign']); const data = new TextEncoder().encode(message); const signature = await window.crypto.subtle.sign({ name: "RSASSA-PKCS1-v1_5", }, privateKey, data, ); // converts the signature to a colon seperated string return new Uint8Array(signature).join(':'); } 

这个函数把我们在第一步创build的私钥的jwk和一个简单的string作为一个消息来签名。 它以冒号分隔的stringforms返回签名。 这现在就行,即使Base64比较有效。


现在最后一步是validation你的消息。 您可能希望稍后在不同的机器上执行此操作,只有公钥和可能损坏的消息,并且要validation消息是否已损坏。 它是绝对必要的,你可以相信私钥没有损坏。

 async function verify(publicKeyJwk, signatureStr, message) { const signatureArr = signatureStr.split(':').map(x => +x); const signature = new Uint8Array(signatureArr).buffer const publicKey = await window.crypto.subtle.importKey("jwk", publicKeyJwk, { name: "RSASSA-PKCS1-v1_5", hash: {name: "SHA-512"}, }, false, ['verify']); const data = new TextEncoder().encode(message); const ok = await window.crypto.subtle.verify({ name: "RSASSA-PKCS1-v1_5", }, publicKey, signature, data ); return ok; } 

为此,您需要我们在第一步创build的公钥的string,第二步中使用的消息作为string,第二步创build的签名作为冒号分隔的string。

这将导致一个布尔值,指示消息是否有效。