我有一个javascript正在浏览器中为文本字符串签名。它在Internet Explorer下使用capicom,在Mozilla浏览器下使用window.crypto。在签名过程之后,我收到一个base64编码的签名。
我使用https将签名和文本字符串上传到一个带有PHP应用程序的Web服务器。从ssl(https)接收用户证书。从这个证书中,我可以提取用户的公钥。
现在,我想验证签名是否与签名文本字符串、用户证书和公钥相对应。我试过用openssl验证php函数,但没有成功。
我总是收到一个错误:
error:0408D077:rsa routines:FIPS_RSA_VERIFY:wrong signature length
- 我有证书,没问题;
- 我已经从证书中提取了公钥,它也经过了验证并确定了;
- 我有签名(base64解码);
很遗憾,我无法验证签名?我不能提供演示或示例,因为它目前只在本地网络中。
- php函数的返回值为:如果签名正确,返回值为1;如果签名不正确,返回值为0;如果错误,返回值为-1。你的回报是多少?您是否使用了正确的签名算法,即函数的第四个参数…?
- 我得到0-不正确。然后我请求openssl_error_string()并接收上述错误。
- 目前,我正试图用命令行中的"openssl smime-verify"来验证签名,但我总是收到"验证失败"消息。我在读这篇文章:10552724
好吧,我终于找到了解决办法。好的。
我有一个用UTF-8编码的网页(在接下来的一些步骤中这将非常重要);
生成签名:
- 如果用户使用mozilla/firefox/chrome-则使用window.crypto生成签名。有关详细信息,请阅读此
- 如果用户使用Internet Explorer-则使用capicom(crypot应用程序接口com对象)生成签名。有关详细信息,请阅读"msdn"window.crypto和capicom都没有很好的文档记录用于web(capicom不仅用于web!)
文本字符串和签名正通过POST请求发送到Web服务器。
服务器(Linux、Apache和PHP)必须验证签名。
现在的问题是:好的。
PHP的openssl_verify()函数也没有很好的文档记录,总是返回零签名无效。
命令工具openssl也没有验证签名。
因为我的Web服务器需要SSL证书身份验证,所以我希望用户使用登录的证书对文本字符串进行签名。
那么解决方法是什么:好的。
我发现capicom在签名之前正在将签名字符串转换为utf-16le。不幸的是,WebBrowser将文本字符串发送到以UTF-8编码的Web服务器。这意味着在验证签名之前必须将字符串从utf-8转换为utf-16le。但只有当签名由capicom生成时,这才有效。
openssl-cmd工具现在工作正常。cmd工具和php函数的区别在于:
- 签名和源作为字符串发送到php函数。如果使用命令工具,则签名和源将作为文件路径发送。因此,如果您使用的是cmd而不是php函数,那么必须首先将签名和源文件保存为文件。如果需要,请记住转换编码。
- PHP函数需要作为参数来接收签名者的公钥。因此,在此之前,您必须检查证书是否由受信任的证书颁发机构(CA)颁发。相反,cmd工具期望作为一个参数来接收一个包含受信任证书颁发机构根证书列表的文件。这意味着-在验证之前必须检查签名者。
在firefox/mozilla/chrome浏览器下,似乎不可能精确地限制用户用于签名的证书。但也有可能限制选择。当然,这根本没有记录在案(或者我没有找到任何有关这方面的适当信息)。因此,signText()函数需要第三个参数,该参数必须是可信证书颁发机构名称。首先,我希望这必须是客户证书的颁发者的CN。但事实并非如此。它必须是用comas分隔的所有发行者字符串,如:"C=Country,ST=State,L=Location,O=Organization,CN=CommonName,STREET=Address",不幸的是,该字符串与openssl的发行者字符串略有不同,openssl看起来像issuer=/streetAddress=Address/CN=CommonName/O=Organization/L=Location/ST=State/C=Country。如果有人在Linux下有一个firefox,那么检查这个字符串的格式是否相同将非常有趣。我不知道如何在一个字符串中分离更多的CA名称。
在Internet Explorer下,使用capicom可以只向签名对象发送一个证书对象,这样它就不会打开对话框从列表中选择证书。通过比较根证书指纹(base64 65 chr/行编码证书的sha1哈希,不包括头和尾)和证书序列号,可以找到正确的证书。只需阅读MSDN就可以了。
现在。我的源代码是什么样子的:好的。
如果签名是由EDOCX1的capicom(0)生成的(我从webbrowser收到一个额外的参数如何生成签名),如果使用capicom,我将像这样转换源数据:$_POST['source'] = iconv('UTF-8', 'UTF-16LE', $_POST['source'])
我生成2个临时文件。可以使用php函数uniqid()生成文件名。可以使用sys_get_temp_dir()找到默认临时目录。
源文件与从post数组接收到的文件完全相同。如果签名是由capicom生成的,则必须将其转换为utf-16le。
签名来自编码的Web浏览器base64。不要解码它。只需将其保存在一个新文件中并添加一个特殊的页眉和页脚,如:"-----BEGIN PKCS7-----
".$_POST['signature']."
-----END PKSC7-----"注意页眉和页脚必须在单独的行上。行分隔符只能是(ascii 10)而不能是
(ascii 13)的
(ascii 10)。
您必须有一个包含所有受信任CA根证书的文件。此文件的格式必须如下:
- 一个头"——开始证书——"
- base64编码证书
- 页脚------结束证书------
- 空行
- 如果您有多个受信任的CA根-每个证书必须以一个头和一个尾字符串开头。所有证书都存储在一个文件中
使用以下参数运行命令工具openssl:
- smime—使用命令工具的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非常棘手,而且总是有可能出现问题。希望这能对某人有所帮助。好的。
最好的问候好的。好啊。
- 我认为这在谷歌Chrome上是行不通的
- @Babailiica,你试过用php中的openssl_verify来验证pkcs7签名吗?还是必须用命令行openssl来完成?
好吧,我终于找到了解决办法。好的。
我有一个用UTF-8编码的网页(在接下来的一些步骤中这将非常重要);生成签名:如果用户使用mozilla/firefox/chrome-则使用window.crypto生成签名。有关详细信息,请阅读此如果用户使用Internet Explorer,则使用CAPICOM生成签名(加密应用程序接口COM对象)。要了解更多信息,请阅读msdn,window.crypto和CAPICOM都没有很好的文档记录用于web(CAPICOM不仅用于web!)文本字符串和签名正通过POST请求发送到Web服务器。服务器(Linux、Apache和PHP)必须验证签名。现在的问题是:好的。
PHP的openssl_verify()函数也没有很好的文档记录,总是返回零签名无效。命令工具openssl也没有验证签名。因为我的Web服务器需要SSL证书身份验证,所以我希望用户使用登录的证书对文本字符串进行签名。那么解决方法是什么:好的。
我发现CAPICOM在签名之前正在将签名字符串转换为utf-16le。不幸的是,Web浏览器将文本字符串发送到以UTF-8编码的Web服务器。这意味着在验证签名之前必须将字符串从utf-8转换为utf-16le。但只有当签名是由CAPICOM生成时,这才有效。好的。
opensslcmd工具现在工作正常。cmd工具和php函数的区别在于:好的。
签名和源作为字符串发送到php函数。如果使用命令工具,则签名和源将作为文件路径发送。因此,如果您使用的是cmd而不是php函数,那么必须首先将签名和源文件保存为文件。如果需要,请记住转换编码。好的。
PHP函数需要作为参数来接收签名者的公钥。因此,在此之前,您必须检查证书是否由受信任的证书颁发机构(CA)颁发。相反,cmd工具期望作为一个参数来接收一个包含受信任证书颁发机构根证书列表的文件。这意味着-在验证之前必须检查签名者。好的。
在firefox/mozilla/chrome浏览器下,似乎不可能精确地限制用户用于签名的证书。但也有可能限制选择。当然,这根本没有记录在案(或者我没有找到任何有关这方面的适当信息)。因此,signText()函数需要第三个参数,该参数必须是可信证书颁发机构名称。首先,我希望这必须是客户证书的颁发者的CN。但事实并非如此。它必须是用逗号分隔的所有颁发者字符串,如:好的。
1
| C=Country,ST=State,L=Location,O=Organization,CN=CommonName,STREET=Address |
不幸的是,这个字符串与openssl的发行者字符串稍有不同,后者看起来像好的。
1
| issuer=/streetAddress=Address/CN=CommonName/O=Organization/L=Location/ST=State/C=Country. |
如果有人在Linux下有一个firefox,那么检查这个字符串的格式是否相同将非常有趣。我不知道如何在一个字符串中分离更多的CA名称。在Internet Explorer下,使用CAPICOM可以只向签名对象发送一个证书对象,这样它就不会打开对话框从列表中选择证书。通过比较根证书指纹(base64 65 chr/行编码证书的sha1哈希,不包括头和尾)和证书序列号,可以找到正确的证书。只需阅读MSDN就可以了。现在。我的源代码是什么样子的:好的。
如果签名是由window.crypto的CAPICOM生成的(我从Web浏览器接收到一个额外的参数,如何生成签名),如果使用CAPICOM生成的,我将像这样转换源数据:好的。
1
| $_POST['source'] = iconv('UTF-8', 'UTF-16LE', $_POST['source']) |
我生成2个临时文件。可以使用php函数uniqid()生成文件名。默认的临时目录可以使用sys_get_temp_dir()找到。源文件与从POST数组接收的文件完全相同。如果签名是由CAPICOM生成的,则必须将其转换为utf-16le。好的。
签名来自编码的Web浏览器base64。不要解码它。只需将其保存到一个新文件中并添加一个特殊的页眉和页脚,如下所示:好的。
1 2 3
| "-----BEGIN PKCS7-----
".$_POST['signature']."
-----END PKSC7-----" |
请注意,页眉和页脚必须在单独的行上。行分隔符只能是(ascii 10)而不能是
(ascii 13)或
(ascii 13 10)。您必须有一个包含所有受信任CA根证书的文件。此文件的格式必须如下:好的。
1 2 3 4
| A header"-----BEGIN CERTIFICATE-----"
BASE64 encoded certificate
A footer "-----END CERTIFICATE-----"
empty line |
如果您有多个受信任的CA根-每个证书必须以一个头和一个尾字符串开头。所有证书都存储在一个文件中使用以下参数运行命令工具openssl:好的。
- smime—使用命令工具的smime函数
- -verify—进行验证
- -in filepath—步骤4中创建的签名文件的路径
- -inform PEM—签名的格式是base64编码的文件,带有页眉和页脚。
- -binary—防止源代码从二进制转换为文本
- -content filepath—步骤3中创建的源文件的路径
- -CAfile filepath—步骤5中创建的受信任CA根证书文件的路径
所以最后的命令如下:好的。
1
| openssl smime -verify -in file.pem -inform PEM -binary -content source .txt -CAfile root .pem |
现在使用shell_exec()函数从PHP调用这个函数并读取输出。如果输出字符串以验证成功开始-签名正常。如果输出字符串以验证失败开始-签名不正常。如果输出字符串不同-发生了一些错误。错误描述存储在输出字符串中。好的。
以上这些对我有用。不幸的是,openssl、CAPICOM和window.crypto非常棘手,而且总是有可能出现问题。希望这能对某人有所帮助。好的。好啊。