php - 如何使用PHP从浏览器检查数字签名

标签 php javascript digital-signature digital-certificate capicom

我有一个在浏览器中签署文本字符串的Javascript。它使用Internet Explorer下的CAPICOM和Mozilla浏览器下的window.crypto。签名过程完成后,我收到了BASE64编码的签名。

使用HTTPS,我将签名和文本字符串上载到具有PHP应用程序的Web服务器。从SSL(HTTPS),我收到用户的证书。从该证书中,我可以提取用户的公钥。

现在,我想根据签名的文本字符串以及用户的证书和公钥来验证签名。我尝试使用openssl_verify PHP函数没有成功。

我总是收到一个错误:

error:0408D077:rsa routines:FIPS_RSA_VERIFY:wrong signature length


  • 我有证书,可以;
  • 我已经从证书中提取了公钥,并且还对其进行了验证并确定;
  • 我有签名(已解码BASE64);

  • 不幸的是我无法验证签名?我无法提供演示或示例,因为它目前仅在本地网络中。

    最佳答案

    好的,最后我找到了解决方法。

  • 我有一个用UTF-8编码的网页(这在后续步骤中将非常重要);
  • 生成签名:
  • 如果用户使用Mozilla/Firefox/Chrome,则使用window.crypto生成签名。有关更多信息,请阅读this
  • 如果用户使用Internet Explorer,则使用CAPICOM(Crypot应用程序接口(interface)COM对象)生成签名。有关更多信息,请阅读MSDN
    两者-window.crypto和CAPICOM都没有很好地记录在Web使用中(CAPICOM不仅用于Web!)
  • 文本字符串和签名通过POST请求发送到Web服务器。
  • 服务器(Linux,Apache和PHP)必须验证签名。

  • 现在的问题:
  • PHP的openssl_verify()函数也没有得到很好的说明,并且始终返回零-签名无效。
  • CMD工具openssl也不验证签名。
  • 因为我的Web服务器需要SSL证书身份验证,所以我希望用户使用与他登录的证书相同的文本对文本字符串进行签名。

    那么解决方法是什么:
  • 我发现CAPICOM在对其进行签名之前将已签名的字符串转换为UTF-16LE。不幸的是,Web浏览器将文本字符串发送到以UTF-8编码的Web服务器。这意味着您必须在验证签名之前将字符串从UTF-8转换为UTF-16LE。但这仅在签名由CAPICOM生成时才有效。
  • openssl CMD工具现在可以正常工作。 CMD工具和PHP函数之间的区别是:
  • 签名和源作为字符串发送到PHP函数。如果使用CMD工具,则签名和源作为文件路径发送。因此,如果您使用CMD而不是PHP函数,则必须首先将签名和源保存为文件。请记住,如果需要,请转换编码。
  • PHP函数希望将其作为参数来接收签名者的公钥。因此,在此之前,您必须检查证书是否由受信任的证书颁发机构(CA)颁发。取而代之的是,CMD工具希望将其作为参数来接收一个文件,该文件包含受信任的证书颁发机构的根证书列表。这意味着-您必须在验证之前检查签名者。
  • 在Firefox/Mozilla/Chrome浏览器中,似乎无法完全限制用户使用哪个证书进行签名。但是可以限制这些选择。当然,这根本没有记录在案(或者我没有找到关于此的任何适当信息)。因此,signText()函数需要第三个参数,该参数必须是受信任的证书颁发机构名称。首先,我希望这必须是客户证书发行者的CN。但实际上并非如此。必须使用逗号分隔所有发行者字符串,例如:"C=Country,ST=State,L=Location,O=Organization,CN=CommonName,STREET=Address"不幸的是,该字符串与openssl的发行者字符串(看起来像issuer=/streetAddress=Address/CN=CommonName/O=Organization/L=Location/ST=State/C=Country)略有不同。如果有人使用Linux下的Firefox,检查此字符串的格式是否相同将非常有趣。我不知道如何在单个字符串中分隔更多CA名称。
  • 在Internet Explorer下,使用CAPICOM可以仅将一个证书对象发送到签名对象,因此它不会打开从列表中选择证书的对话框。您可以通过比较根证书指纹(BASE64 65字符/行编码的证书的SHA1哈希,不包括页眉和页脚)和证书的序列号来找到合适的证书。只需阅读MSDN,它有据可查。

  • 现在。我的源代码如下所示:
  • 如果签名是由window.crypto的CAPICOM生成的(我从Web浏览器收到一个附加参数,如何生成签名)如果使用了CAPICOM,我将像这样转换源数据:$_POST['source'] = iconv('UTF-8', 'UTF-16LE', $_POST['source'])
  • 我生成2个临时文件。可以使用PHP函数uniqid()生成文件名。可以使用sys_get_temp_dir()找到默认的临时目录。
  • 源文件的保存与从POST数组接收的完全相同。如果签名是由CAPICOM生成的,则必须将其转换为UTF-16LE。
  • 签名来自经过BASE64编码的Web浏览器。请勿解码。只需将其保存在一个新文件中,然后添加一个特殊的页眉和页脚,如下所示:"-----BEGIN PKCS7-----\n".$_POST['signature']."\n-----END PKSC7-----"注意,页眉和页脚必须位于单独的行上。行分隔符只能是\r\n(ASCII#13#10)的\n(ASCII#10)而不是\r(ASCII#13)。
  • 您必须具有一个包含所有受信任CA根证书的文件。该文件的格式必须如下:
  • 标题“----- BEGIN证书-----”
  • BASE64编码证书
  • 页脚“-----结束证书-----”
  • 空行
  • 如果您拥有多个受信任的CA根-每个证书必须以 header 开头,并以脚注字符串结尾。所有证书都存储在单个文件

  • 使用以下参数运行CMD工具openssl:
  • smime-使用CMD工具
  • 的SMIME函数
  • -verify-做一个验证
  • -in filepath-在步骤4中创建的签名文件的路径
  • -inform PEM-签名的范围是具有页眉和页脚
  • 的BASE64编码文件
  • -binary-防止源代码从二进制转换为文本
  • -content filepath-在步骤3中创建的源文件的路径
  • -CAfile filepath-在步骤5中创建的受信任CA根证书文件的路径
    因此,最终命令如下所示:openssl smime -verify -in file.pem -inform PEM -binary -content source.txt -CAfile root.pem
  • 现在,使用shell_exec()函数从PHP调用它并读取输出。
  • 如果输出字符串以Verification successful开头-签名正常
  • 如果输出字符串以Verification failure开头-签名不正确
  • 如果输出字符串不同-发生了一些错误。错误说明存储在输出字符串中。

  • 以上对我有用。不幸的是,openssl,CAPICOM和window.crypto非常棘手,并且总是有可能发生问题。希望这会帮助到别人。

    此致

    关于php - 如何使用PHP从浏览器检查数字签名,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12617150/

    相关文章:

    javascript - 需要解释术语 [0] == ![0]

    ssl - OCSP数据包的内容?

    ios - 如何在 iPhone 上生成证书签名?

    c - 使用 EVP 和 OpenSSL,用 C 编码

    php - 我过度使用 str_replace 并且想不出更好的方法

    php - linux 和 windows (xampp) 处理错误消息的差异无法修改 header 信息 - headers already sent by php

    javascript - Node.js:错误:使用 GM 模块时生成 ENOENT

    javascript - Google Maps Geocoding API 中的 CORS 错误

    php - 执行时间和内存使用率是 PHP 唯一相关的性能指标吗? CPU呢?

    php - 仅显示前 5 个结果