【发布时间】:2018-10-21 15:24:53
【问题描述】:
我正在尝试连接到由 PHP 创建的安全 websocket,但由于某种原因它不起作用。证书文件对于 PHP 是可读的。
这是我目前的代码(PHP 方面;为简单起见,精简了代码):
$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);
客户端:
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);
}
我没有任何 *.pem 文件。只是在 apache web 服务器中使用的两个文件。如果需要,可以将该文件转换为 pem 文件。但我认为它也应该在 php 中使用这两个文件,不是吗?
为了更好的测试,我们使用了一个带有 let's encrypt 证书的隔离子域。因为我们获得了对该服务器的完全访问权限。但是,从证书生成器中,我只得到了这两个提到的文件。对于 Web 服务器,它可以完美运行。但是如何为 php 中的 websocket 做同样的事情呢?
现在,使用此代码,在向客户端发送一些消息后,服务器脚本告诉我,它无法从证书中获取对等方。我不知道这条消息是什么意思以及如何解决这个问题。我也尝试过将local_cert 和local_pk 相互交换,但无论如何都没有帮助。
编辑:经过一段时间的研究,事实证明,php 失败,每次组合都会出现另一个错误。
我生成的证书文件如下所示:
文件 /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-----
文件 /opt/psa/var/certificates/cert-Du9H8N:
-----BEGIN CERTIFICATE-----
some letters
-----END CERTIFICATE-----
两者的证书字符串中的所有行都在字符位置 65 处结束。
正如您在 php 文档中看到的那样,php 期望得到一个 PEM 格式的文件:http://php.net/manual/en/context.ssl.php#context.ssl.local-cert
我发现本教程将我的两个文件转换为一个 PEM 文件,但我的证书看起来与网站上提到的不同,而且我不知道 php 需要在 PEM 文件中准确包含什么:https://www.digicert.com/ssl-support/pem-ssl-creation.htm
编辑 2: 如下所述,这是我得到的确切错误:
与
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');
警告:stream_socket_accept():无法设置私钥文件 `/opt/psa/var/certificates/cert-0x8zHR' in ...在线...
警告:stream_socket_accept(): 无法在 ... on 中启用加密 线...
警告:stream_socket_accept():accept failed: Success in ... on line ...
警告:socket_read() 期望参数 1 是资源,布尔值 在线...中给出...
注意:未定义的偏移量:1 in ... on line ...
警告:socket_write() 期望参数 1 是资源,布尔值 在线...中给出...
警告:socket_write() 期望参数 1 是资源,布尔值 在线...中给出...
警告:socket_write() 期望参数 1 是资源,布尔值 在线...中给出...
警告:socket_write() 期望参数 1 是资源,布尔值 在线...中给出...
警告:socket_write() 期望参数 1 是资源,布尔值 在线...中给出...
警告:socket_write() 期望参数 1 是资源,布尔值 在线...中给出...
警告:socket_close() 期望参数 1 是资源,布尔值 在线...中给出...
还有
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');
警告:stream_socket_accept():无法设置私钥文件 `/opt/psa/var/certificates/cert-Du9H8N' in ...在线...
警告:stream_socket_accept(): 无法在 ... on 中启用加密 线...
警告:stream_socket_accept():accept failed: Success in ... on line ...
警告:socket_read() 期望参数 1 是资源,布尔值 在线...中给出...
注意:未定义的偏移量:1 in ... on line ...
警告:socket_write() 期望参数 1 是资源,布尔值 在线...中给出...
警告:socket_write() 期望参数 1 是资源,布尔值 在线...中给出...
警告:socket_write() 期望参数 1 是资源,布尔值 在线...中给出...
警告:socket_write() 期望参数 1 是资源,布尔值 在线...中给出...
警告:socket_write() 期望参数 1 是资源,布尔值 在线...中给出...
警告:socket_write() 期望参数 1 是资源,布尔值 在线...中给出...
警告:socket_close() 期望参数 1 是资源,布尔值 在线...中给出...
并且与(只是 cert-0x8zHR 与 cert-Du9H8N 的串联)
$file = dirname(__FILE__, 3) . \DIRECTORY_SEPARATOR . 'fullchain.pem';
stream_context_set_option($cont, 'ssl', 'local_cert', $file);
警告:stream_socket_accept():无法在...中获取对等证书 ...
警告:stream_socket_accept():无法在 ... ...中启用加密
警告:stream_socket_accept():accept failed: Success in ... ...
警告:socket_read() 期望参数 1 是资源,布尔值 在线...中给出...
注意:未定义的偏移量:1 in ... on line ...
警告:socket_write() 期望参数 1 是资源,布尔值 在线...中给出...
警告:socket_write() 期望参数 1 是资源,布尔值 给... ...
警告:socket_write() 期望参数 1 是资源,布尔值 给... ...
警告:socket_write() 期望参数 1 是资源,布尔值 给... ...
警告:socket_write() 期望参数 1 是资源,布尔值 给... ...
警告:socket_write() 期望参数 1 是资源,布尔值 给... ...
警告:socket_close() 期望参数 1 是资源,布尔值 在线...中给出...
编辑 3: 是的,确实存在 openssl 错误。在第三个 socket_stream_accept 警告之后,我现在只使用 php doc 示例中的代码就得到了这个错误:
error:0B080074:x509 certificate routines:X509_check_private_key:key values mismatch
我在互联网上搜索了这个错误。他们说,如果出现这个错误,我选择了错误的证书文件。
另外,如果我播放这个命令:
openssl x509 -noout -in cert-0x8zHR -modulus
我得到了两个不同的模数字符串。但我不知道为什么,也不知道如何解决这个问题。这两个文件都在 apache web server vhost 配置中使用,它工作正常:
SSLEngine on
SSLVerifyClient none
SSLCertificateFile /opt/psa/var/certificates/cert-0x8zHR
SSLCACertificateFile /opt/psa/var/certificates/cert-Du9H8N
PS:如果你知道任何本地转换为 pem 文件的工具,请告诉我。在线转换是不可能的。
【问题讨论】:
-
请您发布您遇到的确切错误吗?
-
@Rich 我刚刚用我尝试的不同组合添加了所有错误消息 :-)
-
“一些信件”对你的帮助不是很好……证书是公共数据,为什么不给呢?同样对于
local_pk属性,您正在向它传递一个名为cert-something的文件,因此如果它确实是一个证书,那么将它作为密钥将永远不会起作用。确保向local_pk提供您证书的相关私钥。
标签: php ssl websocket serversocket phpwebsocket