如何使用PHP检查来自broswer的数字签名

Bab*_*ica 6 javascript php digital-certificate capicom digital-signature

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

使用HTTPS我将签名和文本字符串上传到带有PHP应用程序的Web服务器.从SSL(HTTPS)我收到用户的证书.从这个证书我可以提取用户的公钥.

现在我想验证签名文本字符串的签名以及用户的证书和公钥.我试过openssl_verify PHP函数没有成功.

我总是收到一个错误:

错误:0408D077:rsa例程:FIPS_RSA_VERIFY:签名长度错误

  • 我有证书,没关系;
  • 我有从证书中提取的公钥,它也经过验证和确定;
  • 我有签名(BASE64解码);

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

Bab*_*ica 2

好吧,我终于找到了解决方法。

  1. 我有一个以 UTF-8 编码的网页(这对于后续步骤非常重要);
  2. 生成签名:
  • 如果用户使用 Mozilla/Firefox/Chrome - 签名是使用window.crypto. 欲了解更多信息,请阅读此内容
  • 如果用户使用 Internet Explorer - 签名是使用 CAPICOM(Crypot 应用程序接口 COM 对象)生成的。有关更多信息,请阅读MSDN -window.crypto和 CAPICOM 都没有很好的用于 Web 使用的文档(CAPICOM 不仅适用于 Web!)
  1. 文本字符串和签名通过 POST 请求发送到网络服务器。
  2. 服务器(Linux、Apache 和 PHP)必须验证签名。

现在的问题是:

  1. PHP 的函数openssl_verify()也没有很好的文档记录,并且总是返回零 - 签名无效。
  2. CMD 工具 openssl 也不验证签名。
  3. 因为我的 Web 服务器需要 SSL 证书身份验证,所以我希望用户使用他登录的相同证书对文本字符串进行签名。

那么解决方法是什么:

  1. 我发现 CAPICOM 在签名之前将签名字符串转换为 UTF-16LE。不幸的是,网络浏览器将文本字符串发送到以 UTF-8 编码的网络服务器。这意味着您必须在验证签名之前将字符串从 UTF-8 转换为 UTF-16LE。但这仅当签名由 CAPICOM 生成时才有效。
  2. openssl CMD 工具现在可以正常工作。CMD工具与PHP函数的区别是:
  • 签名和源代码以字符串形式发送到 PHP 函数。如果使用 CMD 工具,签名和源将作为文件路径发送。因此,如果您使用 CMD 而不是 PHP 函数,您必须首先将签名和源代码保存为文件。如果需要,请记住转换编码。
  • PHP 函数期望作为参数接收签名者的公钥。因此,在此之前,您必须检查证书是否由受信任的证书颁发机构(CA)颁发。相反 - CMD 工具期望作为参数接收包含受信任证书颁发机构的根证书列表的文件。这意味着 - 您必须在验证之前检查签名者。
  1. 似乎在 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 名称。
  2. 在 Internet Explorer 下,使用 CAPICOM 可以仅将一个证书对象发送到签名对象,因此不会打开从列表中选择证书的对话框。您可以通过比较根证书指纹(BASE64 65 字符/行编码证书的 SHA1 哈希值,不包括页眉和页脚)和证书的序列号来找到正确的证书。只需阅读 MSDN,它就有详细记录。

现在。我的源代码是什么样的:

  1. 如果签名是由 CAPICOM 生成的window.crypto(我从网络浏览器收到一个附加参数,如何生成签名)如果使用 CAPICOM,我将像这样转换源数据:$_POST['source'] = iconv('UTF-8', 'UTF-16LE', $_POST['source'])
  2. 我生成了2个临时文件。文件的名称可以使用 PHP 函数uniqid()生成。可以使用sys_get_temp_dir()找到默认的临时目录。
  3. 源文件的保存方式与从 POST 数组接收到的完全相同。如果签名是由 CAPICOM 生成的,则必须将其转换为 UTF-16LE。
  4. 签名来自网络浏览器 BASE64 编码。不要解码它。只需将其保存在新文件中并添加特殊的页眉和页脚,如下所示:"-----BEGIN PKCS7-----\n".$_POST['signature']."\n-----END PKSC7-----"请注意,页眉和页脚必须位于单独的行上。行分隔符只能是 \n (ASCII #10),不能是 \r\n (ASCII #13#10) 或 \r\n (ASCII #13)。
  5. 您必须拥有一个包含所有受信任 CA 根证书的文件。该文件的格式必须如下:
  • 标头“-----BEGIN CERTIFICATE-----”
  • BASE64 编码证书
  • 页脚“-----END CERTIFICATE-----”
  • 空行
  • 如果您有多个受信任的 CA 根 - 每个证书必须以标头开头并以页脚字符串结尾。所有证书都存储在一个文件中
  1. 使用以下参数运行 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
  1. 现在使用shell_exec()函数从 PHP 调用它并读取输出。
  • 如果输出字符串以Verification successful- 开头,则签名正常
  • 如果输出字符串以Verification failure- 开头,则签名不正确
  • 如果输出字符串不同 - 发生了一些错误。错误描述存储在输出字符串中。

以上对我有用。不幸的是,openssl、CAPICOM 都window.crypto非常棘手,总是有可能出现问题。希望这会对某人有所帮助。

此致