使用 .p12 SSL 证书的 curl / soap 请求
curl / soap request with .p12 SSL certificate
我需要使用 PHP7.2.
的 SOAP 请求 https://address?wsdl
需要 .p12
证书
经过几个小时的研究,我唯一能做的就是从 bash:
发出这个请求
$ curl -k -E cert.crt.pem --key cert.key.pem https://address?wsdl
返回 WSDL。但是我不得不拆分 .p12
来分隔文件并使用 -k
选项,这使得所有这些东西都不安全。
通过此命令完成拆分:
openssl pkcs12 -in mycert.p12 -out cert.key.pem -nocerts -nodes
openssl pkcs12 -in mycert.p12 -out cert.crt.pem -clcerts -nokeys
问题是:
如何使用 PHP 中的 cURL
请求此 WSDL 或如何配置 new \SoapClient()
使其正常工作?
这可能只有 .p12
文件和密码吗?或者我必须转换它?
希望这描述了我已经能够做的事情:
<?php
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,2);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5);
curl_setopt($ch, CURLOPT_TIMEOUT, 5);
curl_setopt($ch, CURLOPT_VERBOSE, true);
/**
* cert.p12 (with password) -> cert.pem (contains encrypted PKey & client ?unencrypted? cert)
* $ openssl pkcs12 -in cert.p12 -out cert.pem -clcerts
*
* Result:
*
* This works. But:
* - I don't have peer verification
* - Is such file safe? It has encrypted pkey & certificate (I think not encrypted).
* I don't know much about that topic. Maybe someone with more experience will be able to tell more.
* Maybe some better solution to output this. Maybe as 2 separate files?
*/
curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,false); // DO NOT VERIFY!
curl_setopt($ch,CURLOPT_SSLCERT,__DIR__ . '/cert.pem');
//curl_setopt($ch, CURLOPT_SSLCERTPASSWD, $pass); // This is not required :/
curl_setopt($ch,CURLOPT_SSLKEY,__DIR__ . '/cert.pem');
curl_setopt($ch,CURLOPT_SSLKEYPASSWD, $pass);
/**
* cert.p12 (with password) -> cert.pem (contains encrypted PKey & client ?unencrypted? cert)
* $ openssl pkcs12 -in cert.p12 -out cert.pem -clcerts
*
* Result:
*
* TCP_NODELAY set
* Connected to XXX
* ALPN, offering http/1.1
* SSL certificate problem: self signed certificate in certificate chain
* stopped the pause stream!
* Closing connection 0
*/
curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,true);
curl_setopt($ch,CURLOPT_SSLCERT,__DIR__ . '/cert.pem');
curl_setopt($ch,CURLOPT_SSLKEY,__DIR__ . '/cert.pem');
curl_setopt($ch,CURLOPT_SSLKEYPASSWD, $pass);
/**
* cert.p12 (with password) -> cert.pem (contains encrypted PKey & client ?unencrypted? cert)
* $ openssl pkcs12 -in cert.p12 -out cert.pem -clcerts
*
* Result:
*
* TCP_NODELAY set
* Connected to XXX
* ALPN, offering http/1.1
* ignoring certificate verify locations due to disabled peer verification
* error:14094410:SSL routines:ssl3_read_bytes:sslv3 alert handshake failure
* stopped the pause stream!
* Closing connection 0
*/
curl_setopt($ch,CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch,CURLOPT_CAINFO,__DIR__ . '/cert.pem');
curl_setopt($ch,CURLOPT_CAPATH,__DIR__);
curl_setopt($ch,CURLOPT_KEYPASSWD,$pass);
/**
* cert.p12 (with password) -> cert.pem (contains encrypted PKey & client ?unencrypted? cert)
* $ openssl pkcs12 -in cert.p12 -out cert.pem -clcerts
*
* Result:
*
* TCP_NODELAY set
* Connected to XXX
* ALPN, offering http/1.1
* successfully set certificate verify locations:
* CAfile: /www/soap/cert.pem
* CApath: /www/soap
* SSL certificate problem: self signed certificate in certificate chain
* stopped the pause stream!
* Closing connection 0
*/
curl_setopt($ch,CURLOPT_SSL_VERIFYPEER, true);
curl_setopt($ch,CURLOPT_CAINFO,__DIR__ . '/cert.pem');
curl_setopt($ch,CURLOPT_CAPATH,__DIR__);
curl_setopt($ch,CURLOPT_KEYPASSWD,$pass);
$data = curl_exec($ch);
$error = curl_error($ch);
$httpcode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
var_dump($data, $httpcode, $error);
?>
我没能用.p12
,.pfx
,pkcs12
直接是SOAP。
首先我们需要读取 p12
文件并将其转换为 .pem
。稍后您可以缓存结果 ($pemCertContent
) 以避免每次请求都进行转换或将其存储为文件以用作文件路径。
$readSuccessful = openssl_pkcs12_read($content, $certData, $password);
if (!$readSuccessful) {
$msg = sprintf('Could not read pkcs12 file `%s`: `%s`.', $filePath, openssl_error_string());
throw new \Exception($msg);
}
$pemCertContent = '';
if (isset($certData['pkey'])) {
openssl_pkey_export($certData['pkey'], $pem, $pemPassword);
$pemCertContent .= $pem;
}
if (isset($certData['cert'])) {
openssl_x509_export($certData['cert'], $cert);
$pemCertContent .= $cert;
}
$pemCertContent; // This is your .pem certificate content. I store it as a file.
创建SOAP客户端时根据需要可以这样使用:
$soapOptions = [
/* In some cases, when you have 2 certificates you may need to use stream context instead of 'local_cert'
'stream_context' => stream_context_create([
'ssl' => [
'cafile' => $caCert->getFilePath(), // CA Certificate
'local_cert' => $tlsCertificate->getFilePath(), // PEM certificate
'passphrase' => $tlsCertificatePass, // Pass for PEM certificate
],
]),
*/
'local_cert' => $tlsCertificate->getFilePath(),
'passphrase' => $tlsCertificatePass,
'trace' => true,
'exceptions' => true,
];
这允许连接到 SoapServer 以获取 wsdl 并执行请求。就我而言,我还必须使用 WSSE
签署整个请求正文。为此,我将其与 openssl_pkcs12_read
函数打开的 .p12
证书一起使用:
RobRichards\WsePhp\WSSESoap;
RobRichards\XMLSecLibs\XMLSecurityKey;
但这是另外一回事了。
我需要使用 PHP7.2.
的 SOAP 请求https://address?wsdl
需要 .p12
证书
经过几个小时的研究,我唯一能做的就是从 bash:
发出这个请求$ curl -k -E cert.crt.pem --key cert.key.pem https://address?wsdl
返回 WSDL。但是我不得不拆分 .p12
来分隔文件并使用 -k
选项,这使得所有这些东西都不安全。
通过此命令完成拆分:
openssl pkcs12 -in mycert.p12 -out cert.key.pem -nocerts -nodes
openssl pkcs12 -in mycert.p12 -out cert.crt.pem -clcerts -nokeys
问题是:
如何使用 PHP 中的 cURL
请求此 WSDL 或如何配置 new \SoapClient()
使其正常工作?
这可能只有 .p12
文件和密码吗?或者我必须转换它?
希望这描述了我已经能够做的事情:
<?php
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,2);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5);
curl_setopt($ch, CURLOPT_TIMEOUT, 5);
curl_setopt($ch, CURLOPT_VERBOSE, true);
/**
* cert.p12 (with password) -> cert.pem (contains encrypted PKey & client ?unencrypted? cert)
* $ openssl pkcs12 -in cert.p12 -out cert.pem -clcerts
*
* Result:
*
* This works. But:
* - I don't have peer verification
* - Is such file safe? It has encrypted pkey & certificate (I think not encrypted).
* I don't know much about that topic. Maybe someone with more experience will be able to tell more.
* Maybe some better solution to output this. Maybe as 2 separate files?
*/
curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,false); // DO NOT VERIFY!
curl_setopt($ch,CURLOPT_SSLCERT,__DIR__ . '/cert.pem');
//curl_setopt($ch, CURLOPT_SSLCERTPASSWD, $pass); // This is not required :/
curl_setopt($ch,CURLOPT_SSLKEY,__DIR__ . '/cert.pem');
curl_setopt($ch,CURLOPT_SSLKEYPASSWD, $pass);
/**
* cert.p12 (with password) -> cert.pem (contains encrypted PKey & client ?unencrypted? cert)
* $ openssl pkcs12 -in cert.p12 -out cert.pem -clcerts
*
* Result:
*
* TCP_NODELAY set
* Connected to XXX
* ALPN, offering http/1.1
* SSL certificate problem: self signed certificate in certificate chain
* stopped the pause stream!
* Closing connection 0
*/
curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,true);
curl_setopt($ch,CURLOPT_SSLCERT,__DIR__ . '/cert.pem');
curl_setopt($ch,CURLOPT_SSLKEY,__DIR__ . '/cert.pem');
curl_setopt($ch,CURLOPT_SSLKEYPASSWD, $pass);
/**
* cert.p12 (with password) -> cert.pem (contains encrypted PKey & client ?unencrypted? cert)
* $ openssl pkcs12 -in cert.p12 -out cert.pem -clcerts
*
* Result:
*
* TCP_NODELAY set
* Connected to XXX
* ALPN, offering http/1.1
* ignoring certificate verify locations due to disabled peer verification
* error:14094410:SSL routines:ssl3_read_bytes:sslv3 alert handshake failure
* stopped the pause stream!
* Closing connection 0
*/
curl_setopt($ch,CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch,CURLOPT_CAINFO,__DIR__ . '/cert.pem');
curl_setopt($ch,CURLOPT_CAPATH,__DIR__);
curl_setopt($ch,CURLOPT_KEYPASSWD,$pass);
/**
* cert.p12 (with password) -> cert.pem (contains encrypted PKey & client ?unencrypted? cert)
* $ openssl pkcs12 -in cert.p12 -out cert.pem -clcerts
*
* Result:
*
* TCP_NODELAY set
* Connected to XXX
* ALPN, offering http/1.1
* successfully set certificate verify locations:
* CAfile: /www/soap/cert.pem
* CApath: /www/soap
* SSL certificate problem: self signed certificate in certificate chain
* stopped the pause stream!
* Closing connection 0
*/
curl_setopt($ch,CURLOPT_SSL_VERIFYPEER, true);
curl_setopt($ch,CURLOPT_CAINFO,__DIR__ . '/cert.pem');
curl_setopt($ch,CURLOPT_CAPATH,__DIR__);
curl_setopt($ch,CURLOPT_KEYPASSWD,$pass);
$data = curl_exec($ch);
$error = curl_error($ch);
$httpcode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
var_dump($data, $httpcode, $error);
?>
我没能用.p12
,.pfx
,pkcs12
直接是SOAP。
首先我们需要读取 p12
文件并将其转换为 .pem
。稍后您可以缓存结果 ($pemCertContent
) 以避免每次请求都进行转换或将其存储为文件以用作文件路径。
$readSuccessful = openssl_pkcs12_read($content, $certData, $password);
if (!$readSuccessful) {
$msg = sprintf('Could not read pkcs12 file `%s`: `%s`.', $filePath, openssl_error_string());
throw new \Exception($msg);
}
$pemCertContent = '';
if (isset($certData['pkey'])) {
openssl_pkey_export($certData['pkey'], $pem, $pemPassword);
$pemCertContent .= $pem;
}
if (isset($certData['cert'])) {
openssl_x509_export($certData['cert'], $cert);
$pemCertContent .= $cert;
}
$pemCertContent; // This is your .pem certificate content. I store it as a file.
创建SOAP客户端时根据需要可以这样使用:
$soapOptions = [
/* In some cases, when you have 2 certificates you may need to use stream context instead of 'local_cert'
'stream_context' => stream_context_create([
'ssl' => [
'cafile' => $caCert->getFilePath(), // CA Certificate
'local_cert' => $tlsCertificate->getFilePath(), // PEM certificate
'passphrase' => $tlsCertificatePass, // Pass for PEM certificate
],
]),
*/
'local_cert' => $tlsCertificate->getFilePath(),
'passphrase' => $tlsCertificatePass,
'trace' => true,
'exceptions' => true,
];
这允许连接到 SoapServer 以获取 wsdl 并执行请求。就我而言,我还必须使用 WSSE
签署整个请求正文。为此,我将其与 openssl_pkcs12_read
函数打开的 .p12
证书一起使用:
RobRichards\WsePhp\WSSESoap;
RobRichards\XMLSecLibs\XMLSecurityKey;
但这是另外一回事了。