英文:
SSL/TLS Handshake Error "bad certificate" when using stream_socket_client in PHP
问题
我在尝试使用PHP中的stream_socket_client建立安全连接时遇到了SSL/TLS握手错误,错误消息是"bad certificate"。目标是将我们的PHP应用程序与要求SSL/TLS加密的外部服务集成。
以下是相关的代码片段:
private function createSocketConnection($host, $port)
{
// 客户端证书和私钥的路径
// $localCertificateFile = '/var/www/sslcert/suezpublic.crt';
// $privateKeyFile = '/var/www/sslcert/privatekesy.pem';
// 使用客户端证书创建安全的SSL/TLS上下文
$contextOptions = [
'ssl' => [
'local_cert' => '/var/www/sslcert/suezcombined.pem',
// 'local_pk' => '/var/www/sslcert/privatekesy.pem',
'cafile' => '/var/www/sslcert/bizswitch.pem', // GoDaddy Intermediate证书或您的CA证书
'verify_peer' => true, // 启用对等证书验证
'verify_peer_name' => true, // 检查通用名称是否存在且与主机名匹配
'crypto_method' => STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT, // 使用TLSv1.2
// 'crypto_timeout' => 30, // 将握手超时增加到30秒
'CN_match' => 'www.suezelectric.com', // 确保通用名称与服务器的主机名匹配
'ciphers' => 'HIGH:!SSLv2:!SSLv3', // 指定允许的密码套件以确保安全性
'disable_compression' => true, // 为了安全性禁用SSL/TLS压缩
'capture_peer_cert' => true,
'capture_peer_chain' => true,
// 'allow_self_signed' => true,
'debug' => true, // 启用SSL调试
],
];
$context = stream_context_create($contextOptions);
// 创建TCP/IP套接字连接
$socket = stream_socket_client("tls://$host:$port", $errno, $errstr, 30);
if ($socket === false) {
throw new Exception("Failed to create socket: [$errno] $errstr");
}
// 在流上启用SSL/TLS
$secured = stream_socket_enable_crypto($socket, true, STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT);
if ($secured === false) {
throw an Exception("Failed to enable SSL/TLS on the socket");
}
return $socket;
// 现在您可以使用$socket与服务器使用SSL/TLS发送/接收数据
// 例如,您可以使用fwrite($socket, $data)将数据发送到服务器。
}
我已经验证了以下内容:
- 客户端证书(public_cert.crt)和私钥(private_key.pem)是有效的,并正确匹配。
- CA证书(server_cert.pem)已提供,并包含必要的中间证书。
- STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT和'ciphers'设置与服务器的配置兼容。
有趣的是,当服务器管理员在服务器端禁用身份验证时,SSL/TLS握手成功完成。但是,当启用身份验证时,会出现"bad certificate"错误,并且还注意到服务器收到了空的证书链。
我已经尝试使用error_log进行调试,并验证了证书文件的路径是正确的。
在我的SSL/TLS配置中是否有任何遗漏或在使用PHP中的stream_socket_client时可能导致此错误的潜在陷阱?对于这个问题的任何指导或见解将不胜感激。谢谢!
英文:
I am encountering an SSL/TLS handshake error with the message "bad certificate" while attempting to establish a secure connection using stream_socket_client in PHP. The goal is to integrate our PHP application with an external service that requires SSL/TLS encryption.
Here is the relevant code snippet:
private function createSocketConnection($host, $port)
{
// Path to the client certificate and private key
// $localCertificateFile = '/var/www/sslcert/suezpublic.crt';
// $privateKeyFile = '/var/www/sslcert/privatekesy.pem';
// Create a secure SSL/TLS context with client certificates
$contextOptions = [
'ssl' => [
'local_cert' => '/var/www/sslcert/suezcombined.pem',
// 'local_pk' => '/var/www/sslcert/privatekesy.pem',
'cafile' => '/var/www/sslcert/bizswitch.pem', // GoDaddy Intermediate certificate or your CA certificate
'verify_peer' => true, // Enable peer certificate verification
'verify_peer_name' => true, // Check that the common name exists and matches the host name
'crypto_method' => STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT, // Use TLSv1.2
// 'crypto_timeout' => 30, // Increase the handshake timeout to 30 seconds
'CN_match' => 'www.suezelectric.com', // Ensure the common name matches the server's hostname
'ciphers' => 'HIGH:!SSLv2:!SSLv3', // Specify allowed ciphers for security
'disable_compression' => true, // Disable SSL/TLS compression for security
'capture_peer_cert' => true,
'capture_peer_chain' => true,
// 'allow_self_signed' => true,
'debug' => true, // Enable SSL debugging
],
];
$context = stream_context_create($contextOptions);
// Create a TCP/IP socket connection
$socket = stream_socket_client("tls://$host:$port", $errno, $errstr, 30);
if ($socket === false) {
throw new Exception("Failed to create socket: [$errno] $errstr");
}
// Enable SSL/TLS on the stream
$secured = stream_socket_enable_crypto($socket, true, STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT);
if ($secured === false) {
throw new Exception("Failed to enable SSL/TLS on the socket");
}
return $socket;
// Now you can use the $socket to send/receive data with the server using SSL/TLS
// For example, you can use fwrite($socket, $data) to send data to the server.
}
I have verified the following:
- The client certificate (public_cert.crt) and private key (private_key.pem) are valid and correctly matched.
- The CA certificate (server_cert.pem) is provided and contains the necessary intermediate certificates.
- The STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT and 'ciphers' settings are compatible with the server's configuration.
Interestingly, when the server admintrators disable authentication on the server-side, the SSL/TLS handshake completes successfully. However, when authentication is enabled, the "bad certificate" error occurs, and it has also been noticed that the server is receiving an empty certificate chain.
I have already tried debugging with error_log and verified that the certificate files' paths are correct.
Is there anything I am missing in my SSL/TLS configuration or any potential pitfalls when using stream_socket_client in PHP that could be causing this error?
Any guidance or insights into this issue would be greatly appreciated. Thank you!
答案1
得分: 2
感谢 @miken32 指向了 https://www.php.net/manual/en/context.ssl.php 资源,我已成功解决了我的问题。因此,我认为我应该将它作为答案发布。
private function createSocketConnection($host, $port)
{
// 创建带有客户端证书的安全 SSL/TLS 上下文
$contextOptions = [
'ssl' => [
'local_cert' => '/filepath/public_cert.pem',
'cafile' => '/filepath/server_cert.pem',
'verify_peer' => true,
'verify_peer_name' => true,
'allow_self_signed' => true,
],
];
$context = stream_context_create($contextOptions);
// 创建一个 TCP/IP 套接字连接
$socket = stream_socket_client("tcp://$host:$port", $errno, $errstr, 30, STREAM_CLIENT_CONNECT, $context);
if ($socket === false) {
throw new Exception("Failed to create socket: [$errno] $errstr");
}
return $socket;
}
通过这种方式,我已成功建立了与服务器的 SSL 连接。还值得注意的是,当使用 stream_socket_client()
连接套接字时,旧的方法如 socket_write
、socket_read
和 socket_close
将不再起作用。我将它们分别更改为 fwrite
、fread
和 fclose
。
英文:
Thank you @miken32 for pointing me to the resource https://www.php.net/manual/en/context.ssl.php I have been able to resolve my issue. Hence, I thought I should post it as an answer
private function createSocketConnection($host, $port)
{
// Create a secure SSL/TLS context with client certificates
$contextOptions = [
'ssl' => [
'local_cert' => '/filepath/public_cert.pem',
'cafile' => '/filepath/server_cert.pem',
'verify_peer' => true,
'verify_peer_name' => true,
'allow_self_signed' => true,
],
];
$context = stream_context_create($contextOptions);
// Create a TCP/IP socket connection
$socket = stream_socket_client("tcp://$host:$port", $errno, $errstr, 30, STREAM_CLIENT_CONNECT, $context);
if ($socket === false) {
throw new Exception("Failed to create socket: [$errno] $errstr");
}
return $socket;
}
With this, I have successfully established an SSL connection to the server. It is also good to note that when using stream_socket_client()
to connect to the socket, old methods like socket_write
, socket_read
, and socket_close
will no longer work. I changed them to fwrite
, fread
and fclose
respectively
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论