在 PHP 中使用 "openssl_pkcs7_decrypt" 时出错 "BIO_new_file:no such file"
Error "BIO_new_file:no such file" by using "openssl_pkcs7_decrypt" in PHP
感谢 OpenSSL,我使用以下命令加密了一个文件:
openssl smime -encrypt -in myfile.xml -out myfile.p7m -outform DER -binary publicKey.pem
现在,我要用 PHP 解密文件 "myfile.p7m"。目前,我正在使用此代码但没有成功:
$output = "myfile.xml";
$crt = file_get_contents("mycert.crt");
$private = openssl_pkey_get_private (file_get_contents("privateKey.pem"), "password");
openssl_pkcs7_decrypt ("myfile.p7m", $output, $crt, $private);
while($error = openssl_error_string()){
echo $error.'<br />'.PHP_EOL;
}
此刻,我得到这个错误:
error:2006D080:BIO routines:BIO_new_file:no such file
我不知道这是什么意思。
你能帮帮我吗?
PS:
我已经使用 OpenSSL 命令成功解密了这个文件:
openssl smime -decrypt -in myfile.p7m -out myfile.xml -inkey
privateKey.pem -inform DER -passin pass:password
编辑:
听从 Vladimir Kunschikov 的建议,我使用了文件的完整路径。我已将“myfile.xml”替换为“file://c:/wamp/www/test/myfile.xml ”和“myfile.p7m”由“file://c:/wamp/www/test/myfile.p7m”。现在,我还有另外两个错误:
error:0200107B:system library:fopen:Unknown error
error:2006D002:BIO routines:BIO_new_file:system lib
编辑 2
感谢 Giovani 的回复。我按照您的指示替换了所有路径。现在,出现了新的错误!
error:0D0D20CC:asn1 encoding routines:SMIME_read_ASN1:no content type
尝试将 'file://c:/wamp/www/test/myfile.xml' 替换为 'c:\wamp\www\test\myfile.xml',将 'file://c:/wamp/www/test/myfile.p7m' 替换为 'c:\wamp\www\test\myfile.p7m'
终于,我找到了解决这个问题的方法。首先,文件路径错误。我已经按照 Giovani 的建议解决了这个问题。对于我的第二个错误,由于我的 OPENSSL 命令,我刚刚在 base 64 中编码了我的“.P7M”,并且我添加了一个 header:
MIME-Version: 1.0
Content-Disposition: attachment; filename="smime.p7m"
Content-Type: application/x-pkcs7-mime; smime-type=enveloped-data; name="smime.p7m"
Content-Transfer-Encoding: base64
MIIJWAYJKoZIhvcNAQcDoIIJSTCCCUUCAQAxggGnMIIBowIBADCBijB9MQswCQYD
VQQGEwJGUjEVMBMGA1UECAwMUmhvbmVzLUFscGVzMQ0wCwYDVQQHDARMeW9uMQ0w
CwYDVQQKDARZcG9rMQwwCgYDVQQLDANQVmUxDTALBgNVBAMMBFlQVmUxHDAaBgkq
hkiG9w0BCQEWDXlwb2tAeXBvay5jb20CCQCCV/J9OpZ9pjANBgkqhkiG9w0BAQEF
...
这样我就可以正确解密P7M文件了
所以,我注意到通过在我的 OPENSSL 命令中删除选项“-outform DER”,我得到了一个可以使用“openssl_pksc7_decrypt":
openssl smime -encrypt -in myfile.xml -out myfile.p7m -binary publicKey.pem
我使用 Yandex Money 签署信件以获取收银员余额的工作代码示例。
摘自代码,因为请不要严厉批评
(在 Yandex Money 结账处接收余额的信件的 OpenSSL 签名)
private function _make_message($data)
{
$tmp_file_msg_raw = realpath(tempnam('C:\Temp', 'ymr_'));
$tmp_file_msg_sign = realpath(tempnam('C:\Temp', 'yms_'));
$EOL = "\r\n"; // ограничитель строк, некоторые почтовые сервера требуют \n - подобрать опытным путём
$EOL2 = "\n\n";
$boundary = md5(uniqid(time())); // любая строка, которой не будет ниже в потоке данных.
$fd = fopen($tmp_file_msg_raw, 'w');
if(!$fd) {
$error = "Could not open temporary file $tmp_file_msg_raw.";
return array("status" => false, "error_msg" => $error, "error_no" => 0);
}
fwrite($fd, $data);
fclose($fd);
if(!@openssl_pkcs7_sign(
$tmp_file_msg_raw,
$tmp_file_msg_sign,
'file://' . $this->_deposit_crt_sign,
['file://' . $this->_deposit_key, $this->_config['secret_keyword']],
[],
PKCS7_BINARY))
{
unlink($tmp_file_msg_raw);
unlink($tmp_file_msg_sign);
$error = "Could not sign data: ".openssl_error_string();
return FALSE;
}
$signed_data = file_get_contents($tmp_file_msg_sign);
$signed_data_array = explode($EOL2, $signed_data);
$signed_data = $signed_data_array[1];
$signed_data = "-----BEGIN PKCS7-----\n" . $signed_data . "\n-----END PKCS7-----";
$multipart = '--' . $boundary . $EOL;
$multipart .= 'Content-Disposition: form-data; name=smime; filename=smime.p7m' . $EOL;
$multipart .= 'Content-Type: application/pkcs7-mime' . $EOL;
$multipart .= $EOL; // раздел между заголовками и телом html-части
$multipart .= $signed_data;
$multipart .= $EOL . '--' . $boundary . '--' . $EOL;
unlink($tmp_file_msg_raw);
unlink($tmp_file_msg_sign);
return [
'headers' => [
'MIME-Version: 1.0',
"Content-Type: multipart/form-data; boundary=\"$boundary\"",
],
'body' => $multipart
];
}
private function _read_message($data)
{
$EOL = "\r\n";
$pkcs7_headers = 'MIME-Version: 1.0' . $EOL;
$pkcs7_headers .= 'Content-Disposition: attachment' . $EOL;
$pkcs7_headers .= 'Content-Type: application/x-pkcs7-mime' . $EOL;
$pkcs7_headers .= 'Content-Transfer-Encoding: base64' . $EOL;
$pkcs7_headers .= $EOL;
$pkcs7_message = $pkcs7_headers . $data;
$file_pkcs7_msg = realpath(tempnam('C:\Temp', 'ymm'));
$file_pkcs7_outfilename = realpath(tempnam('C:\Temp', 'ymo_'));
$file_pkcs7_content = realpath(tempnam('C:\Temp', 'ymc_'));
$fd_message_response = fopen($file_pkcs7_msg, 'a');
if(!$fd_message_response) {
$error = "Could not open temporary file $file_pkcs7_msg.";
return array("status" => false, "error_msg" => $error, "error_no" => 0);
}
fwrite($fd_message_response, $pkcs7_message);
fclose($fd_message_response);
if (!@openssl_pkcs7_verify(
$file_pkcs7_msg,
PKCS7_BINARY,
$file_pkcs7_outfilename,
[$this->_deposit_crt_verify],
$this->_deposit_crt_verify,
$file_pkcs7_content))
{
// TODO
};
$data = file_get_contents($file_pkcs7_content);
unlink($file_pkcs7_msg);
unlink($file_pkcs7_outfilename);
unlink($file_pkcs7_content);
//while ($msg = openssl_error_string()) echo $msg . "<br />\n";
return $data;
}
$data = '<?xml version="1.0" encoding="UTF-8"?><balanceRequest agentId="123456" clientOrderId="1" requestDT="2015-02-17T16:21:22+04:00"/>';
$data = $this->_make_message($data);
$url = $this->_config['uri_api_deposition'].'webservice/deposition/api/balance';
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_HTTPHEADER, $data['headers']);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $data['body']);
curl_setopt($ch, CURLOPT_VERBOSE, false);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 30);
curl_setopt($ch, CURLOPT_USERAGENT, 'Ymoney CollectMoney');
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($ch, CURLOPT_SSLCERT, $this->_ssl_crt);
curl_setopt($ch, CURLOPT_SSLKEY, $this->_ssl_key);
curl_setopt($ch, CURLOPT_SSLCERTPASSWD, $this->_config['secret_keyword']);
curl_setopt($ch, CURLOPT_FORBID_REUSE, TRUE);
curl_setopt($ch, CURLOPT_FRESH_CONNECT, TRUE);
$response = curl_exec($ch);
//var_dump($response);
curl_close($ch);
$msg = $this->_read_message($response);
var_dump($msg);
感谢 OpenSSL,我使用以下命令加密了一个文件:
openssl smime -encrypt -in myfile.xml -out myfile.p7m -outform DER -binary publicKey.pem
现在,我要用 PHP 解密文件 "myfile.p7m"。目前,我正在使用此代码但没有成功:
$output = "myfile.xml";
$crt = file_get_contents("mycert.crt");
$private = openssl_pkey_get_private (file_get_contents("privateKey.pem"), "password");
openssl_pkcs7_decrypt ("myfile.p7m", $output, $crt, $private);
while($error = openssl_error_string()){
echo $error.'<br />'.PHP_EOL;
}
此刻,我得到这个错误:
error:2006D080:BIO routines:BIO_new_file:no such file
我不知道这是什么意思。
你能帮帮我吗?
PS: 我已经使用 OpenSSL 命令成功解密了这个文件:
openssl smime -decrypt -in myfile.p7m -out myfile.xml -inkey
privateKey.pem -inform DER -passin pass:password
编辑:
听从 Vladimir Kunschikov 的建议,我使用了文件的完整路径。我已将“myfile.xml”替换为“file://c:/wamp/www/test/myfile.xml ”和“myfile.p7m”由“file://c:/wamp/www/test/myfile.p7m”。现在,我还有另外两个错误:
error:0200107B:system library:fopen:Unknown error
error:2006D002:BIO routines:BIO_new_file:system lib
编辑 2
感谢 Giovani 的回复。我按照您的指示替换了所有路径。现在,出现了新的错误!
error:0D0D20CC:asn1 encoding routines:SMIME_read_ASN1:no content type
尝试将 'file://c:/wamp/www/test/myfile.xml' 替换为 'c:\wamp\www\test\myfile.xml',将 'file://c:/wamp/www/test/myfile.p7m' 替换为 'c:\wamp\www\test\myfile.p7m'
终于,我找到了解决这个问题的方法。首先,文件路径错误。我已经按照 Giovani 的建议解决了这个问题。对于我的第二个错误,由于我的 OPENSSL 命令,我刚刚在 base 64 中编码了我的“.P7M”,并且我添加了一个 header:
MIME-Version: 1.0
Content-Disposition: attachment; filename="smime.p7m"
Content-Type: application/x-pkcs7-mime; smime-type=enveloped-data; name="smime.p7m"
Content-Transfer-Encoding: base64
MIIJWAYJKoZIhvcNAQcDoIIJSTCCCUUCAQAxggGnMIIBowIBADCBijB9MQswCQYD
VQQGEwJGUjEVMBMGA1UECAwMUmhvbmVzLUFscGVzMQ0wCwYDVQQHDARMeW9uMQ0w
CwYDVQQKDARZcG9rMQwwCgYDVQQLDANQVmUxDTALBgNVBAMMBFlQVmUxHDAaBgkq
hkiG9w0BCQEWDXlwb2tAeXBvay5jb20CCQCCV/J9OpZ9pjANBgkqhkiG9w0BAQEF
...
这样我就可以正确解密P7M文件了
所以,我注意到通过在我的 OPENSSL 命令中删除选项“-outform DER”,我得到了一个可以使用“openssl_pksc7_decrypt":
openssl smime -encrypt -in myfile.xml -out myfile.p7m -binary publicKey.pem
我使用 Yandex Money 签署信件以获取收银员余额的工作代码示例。 摘自代码,因为请不要严厉批评
(在 Yandex Money 结账处接收余额的信件的 OpenSSL 签名)
private function _make_message($data)
{
$tmp_file_msg_raw = realpath(tempnam('C:\Temp', 'ymr_'));
$tmp_file_msg_sign = realpath(tempnam('C:\Temp', 'yms_'));
$EOL = "\r\n"; // ограничитель строк, некоторые почтовые сервера требуют \n - подобрать опытным путём
$EOL2 = "\n\n";
$boundary = md5(uniqid(time())); // любая строка, которой не будет ниже в потоке данных.
$fd = fopen($tmp_file_msg_raw, 'w');
if(!$fd) {
$error = "Could not open temporary file $tmp_file_msg_raw.";
return array("status" => false, "error_msg" => $error, "error_no" => 0);
}
fwrite($fd, $data);
fclose($fd);
if(!@openssl_pkcs7_sign(
$tmp_file_msg_raw,
$tmp_file_msg_sign,
'file://' . $this->_deposit_crt_sign,
['file://' . $this->_deposit_key, $this->_config['secret_keyword']],
[],
PKCS7_BINARY))
{
unlink($tmp_file_msg_raw);
unlink($tmp_file_msg_sign);
$error = "Could not sign data: ".openssl_error_string();
return FALSE;
}
$signed_data = file_get_contents($tmp_file_msg_sign);
$signed_data_array = explode($EOL2, $signed_data);
$signed_data = $signed_data_array[1];
$signed_data = "-----BEGIN PKCS7-----\n" . $signed_data . "\n-----END PKCS7-----";
$multipart = '--' . $boundary . $EOL;
$multipart .= 'Content-Disposition: form-data; name=smime; filename=smime.p7m' . $EOL;
$multipart .= 'Content-Type: application/pkcs7-mime' . $EOL;
$multipart .= $EOL; // раздел между заголовками и телом html-части
$multipart .= $signed_data;
$multipart .= $EOL . '--' . $boundary . '--' . $EOL;
unlink($tmp_file_msg_raw);
unlink($tmp_file_msg_sign);
return [
'headers' => [
'MIME-Version: 1.0',
"Content-Type: multipart/form-data; boundary=\"$boundary\"",
],
'body' => $multipart
];
}
private function _read_message($data)
{
$EOL = "\r\n";
$pkcs7_headers = 'MIME-Version: 1.0' . $EOL;
$pkcs7_headers .= 'Content-Disposition: attachment' . $EOL;
$pkcs7_headers .= 'Content-Type: application/x-pkcs7-mime' . $EOL;
$pkcs7_headers .= 'Content-Transfer-Encoding: base64' . $EOL;
$pkcs7_headers .= $EOL;
$pkcs7_message = $pkcs7_headers . $data;
$file_pkcs7_msg = realpath(tempnam('C:\Temp', 'ymm'));
$file_pkcs7_outfilename = realpath(tempnam('C:\Temp', 'ymo_'));
$file_pkcs7_content = realpath(tempnam('C:\Temp', 'ymc_'));
$fd_message_response = fopen($file_pkcs7_msg, 'a');
if(!$fd_message_response) {
$error = "Could not open temporary file $file_pkcs7_msg.";
return array("status" => false, "error_msg" => $error, "error_no" => 0);
}
fwrite($fd_message_response, $pkcs7_message);
fclose($fd_message_response);
if (!@openssl_pkcs7_verify(
$file_pkcs7_msg,
PKCS7_BINARY,
$file_pkcs7_outfilename,
[$this->_deposit_crt_verify],
$this->_deposit_crt_verify,
$file_pkcs7_content))
{
// TODO
};
$data = file_get_contents($file_pkcs7_content);
unlink($file_pkcs7_msg);
unlink($file_pkcs7_outfilename);
unlink($file_pkcs7_content);
//while ($msg = openssl_error_string()) echo $msg . "<br />\n";
return $data;
}
$data = '<?xml version="1.0" encoding="UTF-8"?><balanceRequest agentId="123456" clientOrderId="1" requestDT="2015-02-17T16:21:22+04:00"/>';
$data = $this->_make_message($data);
$url = $this->_config['uri_api_deposition'].'webservice/deposition/api/balance';
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_HTTPHEADER, $data['headers']);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $data['body']);
curl_setopt($ch, CURLOPT_VERBOSE, false);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 30);
curl_setopt($ch, CURLOPT_USERAGENT, 'Ymoney CollectMoney');
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($ch, CURLOPT_SSLCERT, $this->_ssl_crt);
curl_setopt($ch, CURLOPT_SSLKEY, $this->_ssl_key);
curl_setopt($ch, CURLOPT_SSLCERTPASSWD, $this->_config['secret_keyword']);
curl_setopt($ch, CURLOPT_FORBID_REUSE, TRUE);
curl_setopt($ch, CURLOPT_FRESH_CONNECT, TRUE);
$response = curl_exec($ch);
//var_dump($response);
curl_close($ch);
$msg = $this->_read_message($response);
var_dump($msg);