啟用 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 (伺服器名稱指示) 可用,則必須使用 OpenSSL 0.9.8j 或更高版本編譯 PHP。使用
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.dev.org.tw/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_match 驗證用戶端的 CN (無效),如下所示:
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