TLS (傳輸層安全性) 是一種密碼編譯通訊協定,旨在透過因特網保護兩部計算機之間的通訊。 TLS 通訊協定會透過 SslStream 類別在 .NET 中公開。
本文提供設定客戶端與伺服器之間安全通訊的最佳做法,並假設使用 .NET。 如需 .NET Framework 的最佳做法,請參閱 使用 .NET Framework 的傳輸層安全性 (TLS) 最佳做法。
選取 TLS 版本
雖然您可以透過 EnabledSslProtocols 屬性指定要使用的 TLS 協議版本,但建議使用 None 值來遵循作業系統的設定(這是預設值)。
將決策交由 OS 自行決定會自動使用最新的 TLS 版本,並在 OS 升級後讓應用程式自動適應變更。 作系統也可能防止使用不再被視為安全的 TLS 版本。
選取加密套件
SslStream 可讓使用者透過 CipherSuitesPolicy 類別指定哪些加密套件可以在 TLS 交握時進行協商。 如同 TLS 版本,建議讓 OS 決定要與 交涉的最佳加密套件,因此建議避免使用 CipherSuitesPolicy。
備註
CipherSuitesPolicy 在 Windows 上不受支援,而且嘗試具現化它會導致 NotSupportedException 擲回。
指定本地憑證
作為伺服器認證時, SslStream 總是需要憑證。 當以用戶端身分進行驗證時,如果伺服器要求提供用於雙向 TLS(mTLS)的憑證,你也需要提供憑證。 在這兩種角色中,憑證必須是包含私鑰的 X509Certificate2 實例。
近期的 .NET 版本對伺服器與用戶端進行對稱處理,因此以下指引適用於應用程式以伺服器、用戶端或兩者身份認證。
你可以透過多種方式將憑證提供給 SslStream。
當你以伺服器身分進行驗證時:
- 設定屬性 SslServerAuthenticationOptions.ServerCertificate ,或將憑證傳給 SslStream.AuthenticateAsServerAsync。
- 從 SslServerAuthenticationOptions.ServerCertificateSelectionCallback 回呼函式中傳回憑證。
- 在 SslServerAuthenticationOptions.ServerCertificateContext 屬性上設定 SslStreamCertificateContext。
當你以客戶身份驗證時:
- 將憑證加入該 SslClientAuthenticationOptions.ClientCertificates 集合,或將包含該憑證的集合傳遞至 SslStream.AuthenticateAsClientAsync。
- 從 SslClientAuthenticationOptions.LocalCertificateSelectionCallback 回呼函式中回傳憑證。
- 在 SslClientAuthenticationOptions.ClientCertificateContext 屬性上設定 SslStreamCertificateContext。
備註
該ClientCertificateContext屬性從 .NET 8 開始可用。
為了提升效能,請使用憑證上下文屬性(ServerCertificateContext 或 ClientCertificateContext)。 當你透過其他任一方式提供憑證時,SslStream 會在內部建立一個 SslStreamCertificateContext。 建立上下文會建立一個 X509Chain,這是一個 CPU 密集型操作,因此一次建立上下文並在多個 SslStream 實例中重複使用會更有效率。
重用 SslStreamCertificateContext 實例還能在 Linux 伺服器上啟用額外功能,例如 TLS 會話恢復 。
將中繼憑證傳送給對端
當中介憑證授權機構發出本地憑證時,除非握手包含中間憑證,對等端可能無法建立完整的憑證鏈。 要傳送這些中間文件,請用 Create 方法建立 aSslStreamCertificateContext,並在參數additionalCertificates中傳遞中間憑證:
X509Certificate2 leafCertificate = GetLeafCertificate();
X509Certificate2Collection intermediates = GetIntermediateCertificates();
SslStreamCertificateContext certificateContext =
SslStreamCertificateContext.Create(leafCertificate, intermediates);
// When you authenticate as a server.
serverOptions.ServerCertificateContext = certificateContext;
// When you authenticate as a client for mutual TLS.
clientOptions.ClientCertificateContext = certificateContext;
這兩種角色的情境運作方式相同。 在用戶端,憑證上下文是傳送中間體的推薦方式,因為另一種方式——將中間件加入機器或使用者憑證庫——會影響系統上的每個應用程式。
自訂 X509Certificate 驗證
在某些情況下,預設憑證驗證程序不夠充分,而且需要一些自定義驗證邏輯。 您可以藉由指定 SslClientAuthenticationOptions.CertificateChainPolicy 或 SslServerAuthenticationOptions.CertificateChainPolicy來自定義驗證邏輯的一部分。 或者,您可以透過 <System.Net.Security.SslClientAuthenticationOptions.RemoteCertificateValidationCallback> 屬性來提供完全自定義邏輯。 如需詳細資訊,請參閱 自定義憑證信任。
自定義憑證信任
當遇到計算機信任的任何證書頒發機構單位未核發的憑證時(包括自我簽署憑證),預設憑證驗證程式將會失敗。 解決此問題的其中一個可能方法是將必要的簽發者憑證新增至計算機的信任存放區。 不過,這可能會影響系統上的其他應用程式,而且不一定可行。
替代解決方案是透過 X509ChainPolicy 指定自定義信任的根憑證。 若要指定將在驗證期間使用而不是系統信任清單的自訂信任清單,請考慮下列範例:
SslClientAuthenticationOptions clientOptions = new();
clientOptions.CertificateChainPolicy = new X509ChainPolicy()
{
TrustMode = X509ChainTrustMode.CustomRootTrust,
CustomTrustStore =
{
customIssuerCert
}
};
使用上述原則設定的用戶端只會接受 所信任的 customIssuerCert憑證。
忽略特定的驗證錯誤
請考慮沒有持續性時鐘的IoT裝置。 開機後,裝置的時鐘會設定在過去的許多年前,因此,所有憑證都會被視為「尚未生效」。 考慮下列程式碼,該程式碼展示了一種驗證回呼函式的實作,其忽略了效期違規。
static bool CustomCertificateValidationCallback(
object sender,
X509Certificate? certificate,
X509Chain? chain,
SslPolicyErrors sslPolicyErrors)
{
// Anything that would have been accepted by default is OK
if (sslPolicyErrors == SslPolicyErrors.None)
{
return true;
}
// If there is something wrong other than a chain processing error, don't trust it.
if (sslPolicyErrors != SslPolicyErrors.RemoteCertificateChainErrors)
{
return false;
}
Debug.Assert(chain is not null);
// If the reason for RemoteCertificateChainError is that the chain built empty, don't trust it.
if (chain.ChainStatus.Length == 0)
{
return false;
}
foreach (X509ChainStatus status in chain.ChainStatus)
{
// If an error other than `NotTimeValid` (or `NoError`) is present, don't trust it.
if ((status.Status & ~X509ChainStatusFlags.NotTimeValid) != X509ChainStatusFlags.NoError)
{
return false;
}
}
return true;
}
證書固化
另一個需要自定義憑證驗證的情況是用戶端預期伺服器使用特定憑證,或來自一組小型已知憑證的憑證。 這種做法稱為 憑證鎖定。 下列代碼段顯示驗證回呼,其會檢查伺服器是否提供具有特定已知公鑰的憑證。
static bool CustomCertificateValidationCallback(
object sender,
X509Certificate? certificate,
X509Chain? chain,
SslPolicyErrors sslPolicyErrors)
{
// If there is something wrong other than a chain processing error, don't trust it.
if ((sslPolicyErrors & ~SslPolicyErrors.RemoteCertificateChainErrors) != 0)
{
return false;
}
Debug.Assert(certificate is not null);
const string ExpectedPublicKey =
"3082010A0282010100C204ECF88CEE04C2B3D850D57058CC9318EB5C" +
"A86849B022B5F9959EB12B2C763E6CC04B604C4CEAB2B4C00F80B6B0" +
"F972C98602F95C415D132B7F71C44BBCE9942E5037A6671C618CF641" +
"42C546D31687279F74EB0A9D11522621736C844C7955E4D16BE8063D" +
"481552ADB328DBAAFF6EFF60954A776B39F124D131B6DD4DC0C4FC53" +
"B96D42ADB57CFEAEF515D23348E72271C7C2147A6C28EA374ADFEA6C" +
"B572B47E5AA216DC69B15744DB0A12ABDEC30F47745C4122E19AF91B" +
"93E6AD2206292EB1BA491C0C279EA3FB8BF7407200AC9208D98C5784" +
"538105CBE6FE6B5498402785C710BB7370EF6918410745557CF9643F" +
"3D2CC3A97CEB931A4C86D1CA850203010001";
return certificate.GetPublicKeyString().Equals(ExpectedPublicKey);
}
用戶端憑證驗證的考慮
要求和驗證客戶端憑證時,伺服器應用程式必須小心。 憑證可能包含 AIA(授權單位資訊存取) 延伸模組,指定可以下載簽發者憑證的位置。 因此,在建置用戶端憑證的X509Chain時,伺服器可能會嘗試從外部伺服器下載簽發者憑證。 同樣地,伺服器可能需要連絡外部伺服器,以確保客戶端憑證尚未撤銷。
在建置與驗證 X509Chain 時必須聯絡外部伺服器,若外部伺服器反應遲緩,應用程式可能會面臨阻斷服務攻擊。 因此,伺服器應用程式應該透過使用X509Chain來設定CertificateChainPolicy建置行為。
備註
從 .NET 11 開始,SslStream 預設在以伺服器身份驗證客戶端憑證時會停用 AIA 憑證下載。 若未提供自訂 CertificateChainPolicy ,伺服器不會嘗試透過 AIA 擷取遺失的中間憑證。 欲了解更多資訊,請參閱 SslStream 伺服器端 AIA 憑證下載預設被停用。