I am trying to connect to a secure websocket created by PHP, but for some reason it doesn't work. The certificate files are readable for PHP.
This is my code so far (PHP side; stripped down code for simplicity):
$context = stream_context_create();
stream_context_set_option($context, 'ssl', 'allow_self_signed', false);
stream_context_set_option($context, 'ssl', 'verify_peer', true);
stream_context_set_option($context, 'ssl', 'peer_name', 'example.com');
stream_context_set_option($context, 'ssl', 'CN_match', 'example.com');
stream_context_set_option($context, 'ssl', 'SNI_enabled', true);
stream_context_set_option($context, 'ssl', 'local_cert', '/path/to/ssl/cert/example.com');
stream_context_set_option($context, 'ssl', 'local_pk', '/path/to/ssl/private/example.com');
$serverSocket = stream_socket_server(
'tls://example.com:8090',
$errNo,
$errStr,
\STREAM_SERVER_BIND | \STREAM_SERVER_LISTEN,
$context
);
$client = stream_socket_accept($serverSocket);
// send initial websocket connection stuff
$request = socket_read($client, 5000);
preg_match('#Sec-WebSocket-Key: (.*)\r\n#', $request, $matches);
$key = base64_encode(pack(
'H*',
sha1($matches[1] . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11')
));
$headers = "HTTP/1.1 101 Switching Protocols\r\n";
$headers .= "Upgrade: websocket\r\n";
$headers .= "Connection: Upgrade\r\n";
$headers .= 'Sec-WebSocket-Version: 13' . "\r\n";
$headers .= 'Sec-WebSocket-Accept: ' . $key . "\r\n\r\n";
socket_write($client, $headers, \mb_strlen($headers));
// do something here...
socket_close($client);
socket_close($serverSocket);
The client side:
var con = new WebSocket('wss://' + host + ':' + port);
var $chat = $('#chat');
con.onmessage = function(e) {
$chat.append('<p>' + e.data + '</p>');
};
con.onopen = function(e) {
con.send('Hello Me!');
};
con.onclose = function (e) {
console.log('connection closed.', arguments);
}
I do not have any *.pem file. Just the two files, that are used in apache web server. It would be possible to convert that files into a pem file, if needed. But I think it should also work in php with these both files, shouldn't it?
For a better testing, we are using an isolated subdomain with an let's encrypt certificate. Because we got full access to this server. However, from the certifacte generator I got only those two mentioned files. For the web server it works perfectly. But how to do the same for a websocket in php?
Now, with this code, after sending some messages to the client the server script tells me, that it was not able to get the peer from the certificates. I don't know what this message means and how to fix that. I also alreay tried to swap local_cert
and local_pk
with each other, but it hasn't helped anyway.
Edit: After researching a while, it turns out, that php fails with every combination with another error.
My generated certificate files look like this:
File /opt/psa/var/certificates/cert-0x8zHR:
-----BEGIN CERTIFICATE REQUEST-----
some letters
-----END CERTIFICATE REQUEST-----
-----BEGIN PRIVATE KEY-----
some letters
-----END PRIVATE KEY-----
-----BEGIN CERTIFICATE-----
some letters
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
some letters
-----END CERTIFICATE-----
File /opt/psa/var/certificates/cert-Du9H8N:
-----BEGIN CERTIFICATE-----
some letters
-----END CERTIFICATE-----
All lines from the certificate strings from both of them end at character position 65.
As you can read in php documentation, php expects to get a file in PEM format: http://php.net/manual/en/context.ssl.php#context.ssl.local-cert
I found this tutorial to convert my two files into one PEM file, but my certificates look different to mentioned on the site and also I don't know what to exactly include into the PEM file, that php needs: https://www.digicert.com/ssl-support/pem-ssl-creation.htm
Edit 2: As mentioned below, here are the exact erros I get:
with
stream_context_set_option($cont, 'ssl', 'local_pk', '/opt/psa/var/certificates/cert-0x8zHR');
stream_context_set_option($cont, 'ssl', 'local_cert', '/opt/psa/var/certificates/cert-Du9H8N');
Warning: stream_socket_accept(): Unable to set private key file `/opt/psa/var/certificates/cert-0x8zHR' in ... on line ...
Warning: stream_socket_accept(): Failed to enable crypto in ... on line ...
Warning: stream_socket_accept(): accept failed: Success in ... on line ...
Warning: socket_read() expects parameter 1 to be resource, boolean given in ... on line ...
Notice: Undefined offset: 1 in ... on line ...
Warning: socket_write() expects parameter 1 to be resource, boolean given in ... on line ...
Warning: socket_write() expects parameter 1 to be resource, boolean given in ... on line ...
Warning: socket_write() expects parameter 1 to be resource, boolean given in ... on line ...
Warning: socket_write() expects parameter 1 to be resource, boolean given in ... on line ...
Warning: socket_write() expects parameter 1 to be resource, boolean given in ... on line ...
Warning: socket_write() expects parameter 1 to be resource, boolean given in ... on line ...
Warning: socket_close() expects parameter 1 to be resource, boolean given in ... on line ...
And with
stream_context_set_option($cont, 'ssl', 'local_cert', '/opt/psa/var/certificates/cert-0x8zHR');
stream_context_set_option($cont, 'ssl', 'local_pk', '/opt/psa/var/certificates/cert-Du9H8N');
Warning: stream_socket_accept(): Unable to set private key file `/opt/psa/var/certificates/cert-Du9H8N' in ... on line ...
Warning: stream_socket_accept(): Failed to enable crypto in ... on line ...
Warning: stream_socket_accept(): accept failed: Success in ... on line ...
Warning: socket_read() expects parameter 1 to be resource, boolean given in ... on line ...
Notice: Undefined offset: 1 in ... on line ...
Warning: socket_write() expects parameter 1 to be resource, boolean given in ... on line ...
Warning: socket_write() expects parameter 1 to be resource, boolean given in ... on line ...
Warning: socket_write() expects parameter 1 to be resource, boolean given in ... on line ...
Warning: socket_write() expects parameter 1 to be resource, boolean given in ... on line ...
Warning: socket_write() expects parameter 1 to be resource, boolean given in ... on line ...
Warning: socket_write() expects parameter 1 to be resource, boolean given in ... on line ...
Warning: socket_close() expects parameter 1 to be resource, boolean given in ... on line ...
And with (just a concatenation of cert-0x8zHR with cert-Du9H8N)
$file = dirname(__FILE__, 3) . \DIRECTORY_SEPARATOR . 'fullchain.pem';
stream_context_set_option($cont, 'ssl', 'local_cert', $file);
Warning: stream_socket_accept(): Could not get peer certificate in ... ...
Warning: stream_socket_accept(): Failed to enable crypto in ... ...
Warning: stream_socket_accept(): accept failed: Success in ... ...
Warning: socket_read() expects parameter 1 to be resource, boolean given in ... on line ...
Notice: Undefined offset: 1 in ... on line ...
Warning: socket_write() expects parameter 1 to be resource, boolean given in ... on line ...
Warning: socket_write() expects parameter 1 to be resource, boolean given in ... ...
Warning: socket_write() expects parameter 1 to be resource, boolean given in ... ...
Warning: socket_write() expects parameter 1 to be resource, boolean given in ... ...
Warning: socket_write() expects parameter 1 to be resource, boolean given in ... ...
Warning: socket_write() expects parameter 1 to be resource, boolean given in ... ...
Warning: socket_close() expects parameter 1 to be resource, boolean given in ... on line ...
Edit 3: Yes, indeed, there is an openssl error. After the third socket_stream_accept
warning I now get this error by just using the code from the php doc example:
error:0B080074:x509 certificate routines:X509_check_private_key:key values mismatch
I searched for this error in the internet. They say, if this error appears, I have chosen the wrong certificate file.
Also, if I play this command:
openssl x509 -noout -in cert-0x8zHR -modulus
I got two different strings for the modulus. But I don't know why, neither how to fix this. These both files are used in apache web server vhost config and it works fine:
SSLEngine on
SSLVerifyClient none
SSLCertificateFile /opt/psa/var/certificates/cert-0x8zHR
SSLCACertificateFile /opt/psa/var/certificates/cert-Du9H8N
PS: If you know any tool for local conversion to a pem file, please let me know. Online conversion is impossible.
local_pk
attribute you are passing it a file namedcert-something
, so if it is indeed a certificate, giving it as key will never work. Make sure to provide tolocal_pk
the associated private key of your certificate. – Bartizan