电子签名要求
Electronic signature requirements
你好 Whosebug 社区,
我对电子签名有困难,尤其是在技术要求方面如何适用新加坡法律。
根据法律,新加坡接受电子签名,如果签名是:
- 对使用它的人来说是独一无二的;
- 能够识别这样的人;
- 在
的唯一控制下以某种方式或使用手段创建
使用它的人;和
- 以某种方式链接到与其相关的电子记录,如果记录发生更改,电子签名将失效。
我们可以通过HTML5canvas进行电子签名,然后将其保存为数据库base64字符串。这意味着第 1.、2. 和 3. 点没问题。
第 4 点出现问题,我们确实可以将签名和数据组合在一个加密的字符串中,但是,由于我们同时拥有签名和数据,我们将能够编辑数据并创建一个新字符串:
|id|name|price|signature|final_hash|
------------------------------------
|12|test|40000|data:base|3edcde4642|
于是我们想到了另一种方式,就是在final_hash中混入一些我们不知道的参数,比如签字人的IP地址,his/her用户代理...不过,由于我们不保存这些值,这意味着我们以后将无法检查某些值是否被修改。
这就是我们卡住的地方,你知道如何做到这一点吗?对我来说,似乎不可能同时满足能够检查是否没有数据被修改和之后不能修改数据,但我当然可能是错的。
非常感谢您的帮助!
注意:我使用术语"document"来指代您要签署的任何内容。
一些误解
首先,使用 HTML5 canvas 或类似方法得到的不是签名而是指纹。我的意思是它可以识别客户,但不能签署文件(至少不能单独签署)。两个注意事项:
- 指纹是浏览器的,不是用户的。
- 会有碰撞。事实上,为了保护隐私,有人推动指纹识别浏览器变得更难,主要是因为公司可以在没有 cookie 的情况下使用指纹识别来跟踪你。
您不应该通过将指纹连接到数据来签名。那不是签名,那是 salt,也不是加盐的最佳方法。
顺便说一句,ClientJS是创建浏览器指纹的好工具
您的要求
- unique to the person using it;
- capable of identifying such person;
这些要求拼写 "Identification"。这意味着您需要此人的 ID。
- created in a manner or using a means under the sole control of the
person using it; and
这个要求拼"Authentication"。这意味着您需要验证人们是否是他们声称的那个人。您可能已经看过这个理论,您可以通过以下方式做到这一点:a) 这个人 是 的东西(即生物识别)b)这个人知道的东西(即密码)或 c)这个人拥有的东西(即访问电子邮件帐户、手机等...)。
困扰我的是,您似乎正在使用浏览器指纹来进行身份验证和身份验证。这可能会很容易伪造请求以显示为另一个用户。
通常在这种情况下你会做的是使用用户名和密码进行传统的身份验证过程,然后发出一个 cookie。你为什么要避免这种情况?
- linked to the electronic record to which it relates in a manner such that if the record is changed, the electronic signature would be invalidated.
这个要求拼"Signature"。这可能意味着电子记录将是 public 并且我们希望防止伪造。
it seems impossible to satisfy both being able to check if no data was modified and being unable to modify the data later
大意是当文档被修改时签名失效。如果要正式修改,需要新建一个签名。
为了防止第三方从修改后的消息中创建签名,您需要添加一些密钥。现在,这提出了两个问题:
第三方是否应该能够验证签名?如果他们应该,你需要一个非对称算法。
是否应该保护文件免受您(或有权访问服务器的人)的侵害?如果他们应该受到保护,这表明您需要为每个用户设置一个秘密。最简单的解决方案是设置密码。
非对称解
要在 PHP 中实现非对称解决方案,我建议使用 phpseclib。
关注他们 example to create a key pair:
include('Crypt/RSA.php');
$rsa = new Crypt_RSA();
$pair = $rsa->createKey();
$privatekey = $pair['privatekey'];
$publickey = $pair['publickey'];
代码将使用从服务器收集的熵来生成密钥对。
然后就可以用它来做简单的RSA signature and verification.
include('Crypt/RSA.php');
$rsa = new Crypt_RSA();
$rsa->loadKey('...'); // private key
$plaintext = '...';
$signature = $rsa->sign($plaintext);
$rsa->loadKey('...'); // public key
echo $rsa->verify($plaintext, $signature) ? 'verified' : 'unverified';
如果需要在客户端验证签名,可以使用jsrsasign.
对于您的用例,服务器只需要一对密钥。这里的概念是服务器正在签署文档。如果签名匹配,则表示指纹正确。
无论哪种方式,您都应该对您的私钥保密(永远不要将其发送给客户端),并且您可以与需要验证签名的任何人共享 public 密钥。
使用它,服务器可以签署识别用户/客户端的文档(例如包括指纹)并发布文档加上签名。该文档将完全可读(未加密),但如果有人修改它,签名将不再有效。
要验证签名,他们需要 public 密钥。但是,public 密钥对于创建假签名没有用,为此他们需要获取私钥。
对称解
对称算法的主要优势在于性能。对称算法的缺点是需要密钥来验证。
因此,如果您需要第三方能够验证签名(或者即使您需要在客户端进行验证),您将暴露用于创建签名的密钥。
在这种情况下,签名更容易实现。要签名,请使用 hash_hmac
和密钥。要验证签名,请重复该过程并将其与您获得的签名进行比较。
每个用户一个密钥
我不确定您是否想保护数据免受您自己的侵害。也就是说,如果您的 objective 是为了防止您(或您的团队/或管理服务器的任何人)篡改数据。
如果这是你想要的,你可以用密码保护你的密钥。对于非对称解决方案,phpseclib 允许您设置用于加密私钥的密码(因此需要能够签名)。对于对称方案,可以直接使用密码作为key。
如果您想要从您的指纹生成密码,您可以使用密钥派生函数来实现...但是,请记住会有 fingerprint collisions,并且具有唯一的浏览器指纹可以被视为隐私问题。
密钥推导函数
您可以使用 key derivation function.
从外部输入(浏览器指纹、用户密码等)获取加密密钥
从最差到最好的选择:
- 截断指纹大小。 永远不要这样做。这浪费了指纹的熵。很容易发生碰撞。
- 哈希指纹。 什么都别做。 任何可以获取指纹并知道您使用的哈希值的人都可以获得密钥。
- 散列与 pepper 连接的指纹(每个人都使用相同的盐)。 还是不行
- 散列与盐连接的指纹(每个人都是唯一的)。许多人说这是第一个明智的选择。然而,你应该记住使用一个好的散列算法。
- 使用基于散列的消息验证码。
hash_hmac
。对我来说,这是第一个理智的选择。将串联的散列更改为对 hash_hmac
的调用几乎不需要任何费用。此外,它将保护您免受哈希算法的部分原像漏洞(已知或待发现)的影响。
- 使用专用的密钥派生算法。在标准 PHP 函数中,我建议
hash_hkdf
or hash_pbkdf2
.
注意:有时人们(包括过去的我)将这些密钥推导函数称为散列函数。原因是当你使用它们时,你不会(直接)调用你的常规哈希函数,它们给你的是技术上的哈希。然而,实际上,它们不是散列算法;它们是建立在散列之上的算法……事实上,使用它们的参数之一是内部使用的散列函数。
你好 Whosebug 社区,
我对电子签名有困难,尤其是在技术要求方面如何适用新加坡法律。
根据法律,新加坡接受电子签名,如果签名是:
- 对使用它的人来说是独一无二的;
- 能够识别这样的人;
- 在
的唯一控制下以某种方式或使用手段创建 使用它的人;和 - 以某种方式链接到与其相关的电子记录,如果记录发生更改,电子签名将失效。
我们可以通过HTML5canvas进行电子签名,然后将其保存为数据库base64字符串。这意味着第 1.、2. 和 3. 点没问题。
第 4 点出现问题,我们确实可以将签名和数据组合在一个加密的字符串中,但是,由于我们同时拥有签名和数据,我们将能够编辑数据并创建一个新字符串:
|id|name|price|signature|final_hash|
------------------------------------
|12|test|40000|data:base|3edcde4642|
于是我们想到了另一种方式,就是在final_hash中混入一些我们不知道的参数,比如签字人的IP地址,his/her用户代理...不过,由于我们不保存这些值,这意味着我们以后将无法检查某些值是否被修改。
这就是我们卡住的地方,你知道如何做到这一点吗?对我来说,似乎不可能同时满足能够检查是否没有数据被修改和之后不能修改数据,但我当然可能是错的。
非常感谢您的帮助!
注意:我使用术语"document"来指代您要签署的任何内容。
一些误解
首先,使用 HTML5 canvas 或类似方法得到的不是签名而是指纹。我的意思是它可以识别客户,但不能签署文件(至少不能单独签署)。两个注意事项:
- 指纹是浏览器的,不是用户的。
- 会有碰撞。事实上,为了保护隐私,有人推动指纹识别浏览器变得更难,主要是因为公司可以在没有 cookie 的情况下使用指纹识别来跟踪你。
您不应该通过将指纹连接到数据来签名。那不是签名,那是 salt,也不是加盐的最佳方法。
顺便说一句,ClientJS是创建浏览器指纹的好工具
您的要求
- unique to the person using it;
- capable of identifying such person;
这些要求拼写 "Identification"。这意味着您需要此人的 ID。
- created in a manner or using a means under the sole control of the person using it; and
这个要求拼"Authentication"。这意味着您需要验证人们是否是他们声称的那个人。您可能已经看过这个理论,您可以通过以下方式做到这一点:a) 这个人 是 的东西(即生物识别)b)这个人知道的东西(即密码)或 c)这个人拥有的东西(即访问电子邮件帐户、手机等...)。
困扰我的是,您似乎正在使用浏览器指纹来进行身份验证和身份验证。这可能会很容易伪造请求以显示为另一个用户。
通常在这种情况下你会做的是使用用户名和密码进行传统的身份验证过程,然后发出一个 cookie。你为什么要避免这种情况?
- linked to the electronic record to which it relates in a manner such that if the record is changed, the electronic signature would be invalidated.
这个要求拼"Signature"。这可能意味着电子记录将是 public 并且我们希望防止伪造。
it seems impossible to satisfy both being able to check if no data was modified and being unable to modify the data later
大意是当文档被修改时签名失效。如果要正式修改,需要新建一个签名。
为了防止第三方从修改后的消息中创建签名,您需要添加一些密钥。现在,这提出了两个问题:
第三方是否应该能够验证签名?如果他们应该,你需要一个非对称算法。
是否应该保护文件免受您(或有权访问服务器的人)的侵害?如果他们应该受到保护,这表明您需要为每个用户设置一个秘密。最简单的解决方案是设置密码。
非对称解
要在 PHP 中实现非对称解决方案,我建议使用 phpseclib。
关注他们 example to create a key pair:
include('Crypt/RSA.php');
$rsa = new Crypt_RSA();
$pair = $rsa->createKey();
$privatekey = $pair['privatekey'];
$publickey = $pair['publickey'];
代码将使用从服务器收集的熵来生成密钥对。
然后就可以用它来做简单的RSA signature and verification.
include('Crypt/RSA.php');
$rsa = new Crypt_RSA();
$rsa->loadKey('...'); // private key
$plaintext = '...';
$signature = $rsa->sign($plaintext);
$rsa->loadKey('...'); // public key
echo $rsa->verify($plaintext, $signature) ? 'verified' : 'unverified';
如果需要在客户端验证签名,可以使用jsrsasign.
对于您的用例,服务器只需要一对密钥。这里的概念是服务器正在签署文档。如果签名匹配,则表示指纹正确。
无论哪种方式,您都应该对您的私钥保密(永远不要将其发送给客户端),并且您可以与需要验证签名的任何人共享 public 密钥。
使用它,服务器可以签署识别用户/客户端的文档(例如包括指纹)并发布文档加上签名。该文档将完全可读(未加密),但如果有人修改它,签名将不再有效。
要验证签名,他们需要 public 密钥。但是,public 密钥对于创建假签名没有用,为此他们需要获取私钥。
对称解
对称算法的主要优势在于性能。对称算法的缺点是需要密钥来验证。
因此,如果您需要第三方能够验证签名(或者即使您需要在客户端进行验证),您将暴露用于创建签名的密钥。
在这种情况下,签名更容易实现。要签名,请使用 hash_hmac
和密钥。要验证签名,请重复该过程并将其与您获得的签名进行比较。
每个用户一个密钥
我不确定您是否想保护数据免受您自己的侵害。也就是说,如果您的 objective 是为了防止您(或您的团队/或管理服务器的任何人)篡改数据。
如果这是你想要的,你可以用密码保护你的密钥。对于非对称解决方案,phpseclib 允许您设置用于加密私钥的密码(因此需要能够签名)。对于对称方案,可以直接使用密码作为key。
如果您想要从您的指纹生成密码,您可以使用密钥派生函数来实现...但是,请记住会有 fingerprint collisions,并且具有唯一的浏览器指纹可以被视为隐私问题。
密钥推导函数
您可以使用 key derivation function.
从外部输入(浏览器指纹、用户密码等)获取加密密钥从最差到最好的选择:
- 截断指纹大小。 永远不要这样做。这浪费了指纹的熵。很容易发生碰撞。
- 哈希指纹。 什么都别做。 任何可以获取指纹并知道您使用的哈希值的人都可以获得密钥。
- 散列与 pepper 连接的指纹(每个人都使用相同的盐)。 还是不行
- 散列与盐连接的指纹(每个人都是唯一的)。许多人说这是第一个明智的选择。然而,你应该记住使用一个好的散列算法。
- 使用基于散列的消息验证码。
hash_hmac
。对我来说,这是第一个理智的选择。将串联的散列更改为对hash_hmac
的调用几乎不需要任何费用。此外,它将保护您免受哈希算法的部分原像漏洞(已知或待发现)的影响。 - 使用专用的密钥派生算法。在标准 PHP 函数中,我建议
hash_hkdf
orhash_pbkdf2
.
注意:有时人们(包括过去的我)将这些密钥推导函数称为散列函数。原因是当你使用它们时,你不会(直接)调用你的常规哈希函数,它们给你的是技术上的哈希。然而,实际上,它们不是散列算法;它们是建立在散列之上的算法……事实上,使用它们的参数之一是内部使用的散列函数。