SSL/TLS 握手错误,使用 PHP 中的 stream_socket_client 时出现“证书无效”。

huangapple go评论114阅读模式
英文:

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_writesocket_readsocket_close 将不再起作用。我将它们分别更改为 fwritefreadfclose

英文:

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

huangapple
  • 本文由 发表于 2023年8月4日 23:47:13
  • 转载请务必保留本文链接:https://go.coder-hub.com/76837449.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定