Trying to auto apply SSL on TCP socket connection but it doesn't work on host without SSL

Viewed 41

I'm trying to auto resolve if host is supporting certificate for SSL. Here is my piece of code:

$context = stream_context_create([
    'socket' => [
        'tcp_nodelay' => true,
    ],
    'ssl' => [
        'verify_peer' => false,
        'verify_peer_name' => false,
        'SNI_enabled' => false,
        'allow_self_signed' => true,
        'capture_peer_cert' => true,
        'capture_peer_cert_chain' => true
    ]
]);

$this->stream = @stream_socket_client('tcp://' . $this->ip . ':' . $this->port, $errno, $errstr, $this->timeout, STREAM_CLIENT_CONNECT, $context);
if ($this->stream === false) {
    throw new ConnectException($errstr, $errno);
}

if (!stream_set_blocking($this->stream, true)) {
    throw new ConnectException('Cannot set socket into blocking mode');
}

$enableCrypto = stream_socket_enable_crypto($this->stream, true, STREAM_CRYPTO_METHOD_ANY_CLIENT);
if ($enableCrypto === true) {
    $params = stream_context_get_params($this->stream);

    if (isset($params['options']['ssl']['peer_certificate']) && is_resource($params['options']['ssl']['peer_certificate'])) {
        $cert = openssl_x509_parse($params['options']['ssl']['peer_certificate']);
        stream_context_set_params($this->stream, [
            'ssl' => [
                'verify_peer' => true,
                'verify_peer_name' => true,
                'SNI_enabled' => true,
                'peer_name' => $cert['subject']['CN'],
                'allow_self_signed' => count($params['options']['ssl']['peer_certificate_chain']) == 1 && $cert['subject'] == $cert['issuer']
            ]
        ]);
    }
} else {
    stream_socket_enable_crypto($this->stream, false, STREAM_CRYPTO_METHOD_ANY_CLIENT);
}

As you can see the logic is about creating connection over tcp (without ssl) but with requesting certificates. Certificates are not requested until you call stream_socket_enable_crypto. Next I will update stream socket parameters by certificate. This works great with server which have SSL. But when I try to run it against host without SSL (ex. localhost instance) the stream_socket_enable_crypto triggers warning (which is not a big deal because I can suppress it with @) and $enableCrypto contains (int)0:

Warning: stream_socket_enable_crypto(): SSL: The operation completed successfully.

but the connection is aborted after I try to use it:

Notice: fwrite(): send of 16 bytes failed with errno=10053 An established connection was aborted by the software in your host machine. 
0 Answers
Related