我正在尝试连接到由 PHP 创建的安全网络套接字,但由于某种原因它无法正常工作。证书文件对于 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 网络服务器中使用的两个文件。如果需要,可以将该文件转换为 pem 文件。但我认为它也应该在 php 中与这两个文件一起工作,不是吗?
为了更好的测试,我们使用带有 let's encrypt 证书的隔 ionic 域。因为我们可以完全访问该服务器。但是,从 certifacte 生成器中我只得到了这两个提到的文件。对于 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 文件,但我的证书看起来与网站上提到的不同,而且我不知道要将什么确切地包含到 PEM 文件中,php 需要: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');
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 ...
与
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 ...
还有(只是 cert-0x8zHR 与 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 ...
编辑 3: 是的,确实存在 openssl 错误。在第三个 socket_stream_accept
警告之后,我现在通过使用 php 文档示例中的代码得到这个错误:
error:0B080074:x509 certificate routines:X509_check_private_key:key values mismatch
我在互联网上搜索了这个错误。他们说,如果出现这个错误,我选择了错误的证书文件。
此外,如果我执行此命令:
openssl x509 -noout -in cert-0x8zHR -modulus
我有两个不同的模数字符串。但我不知道为什么,也不知道如何解决这个问题。这两个文件都用于 apache web 服务器虚拟主机配置,并且工作正常:
SSLEngine on
SSLVerifyClient none
SSLCertificateFile /opt/psa/var/certificates/cert-0x8zHR
SSLCACertificateFile /opt/psa/var/certificates/cert-Du9H8N
PS:如果您知道任何本地转换为 pem 文件的工具,请告诉我。在线转换是不可能的。
最佳答案
你说:
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-----
这在很多层面上都是错误的。
首先,一旦您获得证书,“CERTIFICATE REQUEST”部分就完全没用了。你可以忽略这部分。
现在复制“PRIVATE KEY”部分,将标题放在一个文件中。这是您的私钥,您可以在任何有“local_pk”选项的地方使用,也可以在需要 key 的软件中使用。
然后你有两个证书。另一个在另一个文件中。您将需要对所有这些进行排序。如果您提供了真实的证书内容(公开内容),人们可能会更好地帮助您。这里只有“一些字母”,我们只能猜测。
在上面的文件中,两个证书可能是 CA 和中间证书。您应该将它们都复制到另一个文件,这将对应于“ca_cert”选项。
我猜第二个文件是你的试用证书(只是再次猜测,没有你的详细信息)。在任何有“local_cert”或要求证书的地方使用它。此证书应与私钥文件匹配(您无法手动验证,您需要工具)。
一旦你创建了所有正确的文件,第一个就不能再使用了。我建议对文件名使用更好的语义,因为 cert-X
和 cert-Y
会非常困难。请改用网站名称,以便您拥有 www.example.com.cert
、www.example.com-ca.cert
和 www.example 等文件。 com.key
,这样一目了然。或者使用名为 www.example.com/
的目录,然后是 cert.pem
、ca.pem
和 key.pem
目录内。扩展本身不被软件使用,无论内容如何,您只需要定义有意义的内容即可。
所以首先对所有这些进行排序,这样你的问题就更清楚了。现在,您似乎在盲目地尝试一些东西,直到找到可行的东西,这在安全和 TLS 领域并不是理想的情况。
如果可能的话,我还鼓励您尝试使用更高级别的抽象库来处理 TLS,因为这显然太低了,暴露了太多您迷路的选项。
关于php - 如何使用 PHP 读取 TLS 证书 websockets?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50288448/