繁体   English   中英

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

[英]How to check a digital signature from broswer with PHP

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

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

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

我总是收到一个错误:

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

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

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

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

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

现在的问题:

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

那么解决方法是什么:

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

现在。 我的源代码如下所示:

  1. 如果签名是由window.crypto的CAPICOM生成的(我从Web浏览器收到一个附加参数,如何生成签名)如果使用了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编码的Web浏览器。 不要解码。 只需将其保存在一个新文件中,然后添加一个特殊的页眉和页脚,如下所示: "-----BEGIN PKCS7-----\\n".$_POST['signature']."\\n-----END PKSC7-----"注意,页眉和页脚必须位于单独的行上。 行分隔符只能是\\ r \\ n(ASCII#13#10)的\\ n(ASCII#10)而不是\\ r(ASCII#13)。
  5. 您必须具有一个包含所有受信任的CA根证书的文件。 该文件的格式必须如下:
    • 标头“ ----- BEGIN证书-----”
    • BASE64编码的证书
    • 页脚“ -----结束证书-----”
    • 空行
    • 如果您拥有多个受信任的CA根-每个证书必须以标头开头,并以脚注字符串结尾。 所有证书都存储在一个文件中
  6. 使用以下参数运行CMD工具openssl:
    • smime使用CMD工具的SMIME功能
    • -verify进行验证
    • -in filepath在步骤4中创建的签名文件的路径
    • -inform PEM 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
  7. 现在,使用shell_exec()函数从PHP调用此函数并读取输出。
    • 如果输出字符串以“ Verification successful开头-签名正常
    • 如果输出字符串以Verification failure开头-签名不正确
    • 如果输出字符串不同-发生了一些错误。 错误说明存储在输出字符串中。

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

最好的祝福

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

我有一个用UTF-8编码的网页(这在后续步骤中非常重要); 生成签名:如果用户使用Mozilla / Firefox / Chrome,则使用window.crypto生成签名。 有关更多信息,请阅读此内容。如果用户使用Internet Explorer,则使用CAPICOM (加密应用程序接口COM对象)生成签名。 有关更多信息,请阅读MSDN。二者window.cryptoCAPICOM对于使用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.cryptoCAPICOM生成的(我从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-----" 

请注意页眉和页脚必须位于单独的行上。 行分隔符只能是\\ n(ASCII#10)而不是\\ r(ASCII#13)或\\ r \\ n(ASCII#13#10)。 您必须具有一个包含所有受信任的CA根证书的文件。 该文件的格式必须如下:

A header "-----BEGIN CERTIFICATE-----"
BASE64 encoded certificate
A footer "-----END CERTIFICATE-----"
empty line

如果您拥有多个受信任的CA根-每个证书必须以标头开头,并以脚注字符串结尾。 所有证书都存储在一个文件中。使用以下参数运行CMD工具openssl

  • smime使用CMD工具的SMIME功能
  • -verify进行验证
  • -in filepath在步骤4中创建的签名文件的路径
  • -inform PEM 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调用此函数并读取输出。 如果输出字符串以“验证成功”开头-签名确定。 如果输出字符串以“验证失败”开头-签名不正确。 如果输出字符串不同-发生了一些错误。 错误说明存储在输出字符串中。

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM