启用 SNI(服务器名称指示)
PEM 必须包含证书和私钥。
<?php
$context = stream_context_create([
'ssl' => [
'SNI_enabled' => true,
'SNI_server_certs' => [
'host1.com' => '/path/host1.com.pem',
'host2.com' => '/path/host2.com.pem',
],
]
]);
?>
SSL 上下文选项 — SSL 上下文选项列表
ssl://
和 tls://
传输的上下文选项。
版本 | 描述 |
---|---|
7.2.0 | 添加了 security_level 。需要 OpenSSL >= 1.1.0。 |
注意:因为
ssl://
是https://
和ftps://
封装器的底层传输,所以适用于ssl://
的任何上下文选项也适用于https://
和ftps://
。
注意:要使用 SNI(服务器名称指示),PHP 必须使用 OpenSSL 0.9.8j 或更高版本编译。使用
OPENSSL_TLSEXT_SERVER_NAME
来确定是否支持 SNI。
启用 SNI(服务器名称指示)
PEM 必须包含证书和私钥。
<?php
$context = stream_context_create([
'ssl' => [
'SNI_enabled' => true,
'SNI_server_certs' => [
'host1.com' => '/path/host1.com.pem',
'host2.com' => '/path/host2.com.pem',
],
]
]);
?>
还有一个 crypto_type 上下文。在旧版本中,这是 crypto_method。这在 https://php.net/manual/en/function.stream-socket-enable-crypto.php 中有引用
我通常从 https://curl.haxx.se/docs/caextract.html 下载根 CA 证书,然后将其作为“cafile”放入,并且几乎每次都能正常工作。
我遇到的唯一问题是当服务器没有正确发送中间 CA 证书时,那么,您必须手动将其添加到文件中。
我无法加载使用 stunnel 工具生成的 PEM。但是,我能够使用 PHP 调用生成一个可用的 PEM,stunnel 和 php 都能识别它,如下所述
http://www.devdungeon.com/content/how-use-ssl-sockets-php
这段代码片段现在对我有用,并且使用 stunnel verify=4,双方都确认了指纹。奇怪的是,如果在下面设置了“tls://”,则会强制使用 TLSv1,但使用“ssl://”允许使用 TLSv1.2
$stream_context = stream_context_create([ 'ssl' => [
'local_cert' => '/path/to/key.pem',
'peer_fingerprint' => openssl_x509_fingerprint(file_get_contents('/path/to/key.crt')),
'verify_peer' => false,
'verify_peer_name' => false,
'allow_self_signed' => true,
'verify_depth' => 0 ]]);
$fp = stream_socket_client('ssl://ssl.server.com:12345',
$errno, $errstr, 30, STREAM_CLIENT_CONNECT, $stream_context);
fwrite($fp, "foo bar\n");
while($line = fgets($fp, 8192)) echo $line;
CN_match 的工作方式与直觉相反。我在使用 PHP 实现的 SSL 服务器开发时遇到了这种情况。我声明(在代码中)
- 不允许自签名证书(有效)
- 根据 CA 证书验证对等方证书(有效)
- 验证客户端的 CN 与 CN_match 是否匹配(不起作用),例如
stream_context_set_option($context, 'ssl', 'CN_match', '*.example.org');
我假设这将匹配任何 CN 在 .example.org 域名下的客户端。
不幸的是,情况并非如此。上面的选项并不会这样做。
它真正做的是
- 获取客户端的 CN 并将其与 CN_match 进行比较
- 如果客户端的 CN 包含星号,例如 *.example.org,则以通配符匹配的方式与 CN_match 进行匹配
示例来说明行为
(CNM = 服务器的 CN_match)
(CCN = 客户端的 CN)
- CNM=host.example.org, CCN=host.example.org ---> OK
- CNM=host.example.org, CCN=*.example.org ---> OK
- CNM=.example.org, CCN=*.example.org ---> OK
- CNM=example.org, CCN=*.example.org ---> ERROR
- CNM=*.example.org, CCN=host.example.org ---> ERROR
- CNM=*.example.org, CCN=*.example.org ---> OK
根据 PHP 源代码,我相信如果您尝试充当客户端并且服务器包含通配符证书,则同样适用。如果您将 CN_match 设置为 myserver.example.org 并且服务器使用 *.example.org 自我介绍,则允许连接。
以上所有内容都适用于 PHP 5.2.12 版本。
我将提供一个补丁来支持以星号开头的 CN_match。
建议使用 "ssl://" 传输。
在 php 5.5 ~ 7.1 中
ssl:// 传输 = ssl_v2|ssl_v3|tls_v1.0|tls_v1.1|tls_v1.2
tls:// 传输 = tls_v1.0
7.2 之后 ssl:// 和 tls:// 传输相同
php 7.2 ~ 7.3 = tls_v1.0|tls_v1.1|tls_v1.2
php 7.4 ~ 8.1 = tls_v1.0|tls_v1.1|tls_v1.2|tls_v1.3
我将此用于 Apple 推送通知服务。
通过 local_cert 选项传入本地证书文件名 `cert.pem`。
直接调用脚本时工作正常。
但是当我在不同位置包含/引用脚本时,它停止工作,没有任何明确的错误消息。
通过传入文件的完整路径 `<FullPathTo>cert.pem` 解决。
如果您想根据已保存的本地证书验证服务器,以进一步验证目标服务器,则必须使用 fullchain.pem。然后 verify_peer 选项将起作用。因此,只需获取服务器证书,搜索根 CA 的 pem 并将所有内容复制到单个文件中。例如
我的证书在链中具有“GeoTrust TLS RSA CA G1”证书,因此您可以在 Google 中搜索该字符串。转到官方的 DigiCert GeoTrust 页面并下载“GeoTrustTLSRSACAG1.crt”证书。然后,您可以使用以下命令将其转换为 pem 格式
openssl x509 -inform DER -in GeoTrustTLSRSACAG1.crt -out GeoTrustTLSRSACAG1.crt.pem -outform PEM