.NET Core如何配置TLS Cipher(套件)?

語言: CN / TW / HK

前言

前不久我發表了一篇關於TLS協議配置被我鑽了空子,經過第三方合作伙伴驗證,針對此TLS協議存在不安全套件,急催速速解決,那麼我們本篇開始繼續整活!第三方合作伙伴對平臺安全嚴苛要求,我們已連續發版十幾次進行處理,在此過程中使得我對安全有了進一步認識,具體認識則是在技術解決方案和密碼學盲點兩方面。下面我們來了解兩個方面,可能沒有完全深入,至少對作為開發者的我們而言,應已基本足夠

.NET Core Cipher(套件)配置

如果沒有專案上的苛刻要求,我斷然也就無法在此方面展開研究和實踐。本文具以.NET 5為例,只不過針對.NET Core 3或3.1通過工具掃描出的協議套件結果略有所差異,但不影響我們對安全套件的配置,我們使用OpenSSL生成自簽名證書,後續我會發表文章講解OpenSSL自簽名證書等等

webBuilder.ConfigureKestrel(serverOptions =>
{
    serverOptions.Listen(IPAddress.Any, 8000, listenOptions =>
    {
        listenOptions.UseHttps("ssl.pfx", "123456", adapterOptions =>
        {
          adapterOptions.SslProtocols = SslProtocols.Tls12;
        });
    });
});

HTTPS結合TLS 協議1.0或1.1不安全,所以TLS協議需使用1.2+,這裡我們如上述程式碼使用版本1.2,接下來我們將其部署在Linux上,然後安裝nmap,通過nmap工具掃描(至於nmap是什麼,可自行了解)

通過nmap掃描指定埠號並列舉其支援TLS 套件需要注意一點,我們可能蒐羅出來大多數文章的命令結果一掃,壓根沒有結果,其實nmap只對指定埠掃描才有效(比如443等等),比如使用如下命令無效果

nmap --script ssl-enum-ciphers localhost -p 8000

若是其他埠,可使用如下命令進行掃描

nmap --script +ssl-enum-ciphers localhost -p 8000

最終我們掃描出來的結果如下:

AES-CBC模式在中SSL或者TLS中存在一些已知的安全漏洞,如BEAST攻擊、Lucky 13攻擊等,雖然TLS1.1、TLS1.2未受到BEAST攻擊的影響、Lucky 13(影響涉及到TLS1.1/1.2)攻擊也在Openssl等知名加密演算法庫得到了修復,但這些漏洞均暴漏出CBC模式在SSL/TLS協議實現時容易引入安全漏洞,HTTP/2中也明確將CBC模式加密套件列為黑名單

上述是在.NET 5 TLS 1.2預設行為,但第三方規定禁止使用AES-CBC即使掃描出來的套件強度為A,並給定了其支援的安全套件

在上述配置通過路徑讀取檔案和使用密碼啟用HTTPS過載方法中,如下

public static ListenOptions UseHttps(this ListenOptions listenOptions, string fileName, string password, Action<HttpsConnectionAdapterOptions> configureOptions);

最後可針對連線做配置,在該類中有如下屬性

public Action<ConnectionContext, SslServerAuthenticationOptions> OnAuthenticate { get; set; }

該輸入第二個類引數裡面,有針對套件的配置,如下(我也是結合github找了老半天才翻到)

所以最終我們配置支援的安全套件如下(諸多套件,聞所未聞,不打緊,下面會歸納總結):

webBuilder.ConfigureKestrel(serverOptions =>
{
  serverOptions.Listen(IPAddress.Any, 8000, listenOptions =>
  {
    listenOptions.UseHttps("ssl.pfx", "123456", adapterOptions =>
    {
      adapterOptions.SslProtocols = SslProtocols.Tls12;

      adapterOptions.OnAuthenticate = (connectionContext, authenticationOptions) =>
      {
        var ciphers = new List<TlsCipherSuite>()
        {
          TlsCipherSuite.TLS_DHE_RSA_WITH_AES_128_GCM_SHA256,
          TlsCipherSuite.TLS_DHE_RSA_WITH_AES_256_GCM_SHA384,
          TlsCipherSuite.TLS_DHE_DSS_WITH_AES_128_GCM_SHA256,
          TlsCipherSuite.TLS_DHE_DSS_WITH_AES_256_GCM_SHA384,
          TlsCipherSuite.TLS_PSK_WITH_AES_128_GCM_SHA256,
          TlsCipherSuite.TLS_PSK_WITH_AES_256_GCM_SHA384,
          TlsCipherSuite.TLS_DHE_PSK_WITH_AES_128_GCM_SHA256,
          TlsCipherSuite.TLS_DHE_PSK_WITH_AES_256_GCM_SHA384,
          TlsCipherSuite.TLS_DHE_PSK_WITH_CHACHA20_POLY1305_SHA256,
          TlsCipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
          TlsCipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
          TlsCipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
          TlsCipherSuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
          TlsCipherSuite.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
          TlsCipherSuite.TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256,
          TlsCipherSuite.TLS_ECDHE_PSK_WITH_AES_128_GCM_SHA256,
          TlsCipherSuite.TLS_ECDHE_PSK_WITH_AES_256_GCM_SHA384,
          TlsCipherSuite.TLS_ECDHE_PSK_WITH_AES_128_CCM_SHA256,
          TlsCipherSuite.TLS_DHE_RSA_WITH_AES_128_CCM,
          TlsCipherSuite.TLS_DHE_RSA_WITH_AES_256_CCM,
          TlsCipherSuite.TLS_DHE_RSA_WITH_AES_128_CCM_8,
          TlsCipherSuite.TLS_DHE_RSA_WITH_AES_256_CCM_8,
          TlsCipherSuite.TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
          TlsCipherSuite.TLS_PSK_WITH_AES_128_CCM,
          TlsCipherSuite.TLS_PSK_WITH_AES_256_CCM,
          TlsCipherSuite.TLS_DHE_PSK_WITH_AES_128_CCM,
          TlsCipherSuite.TLS_DHE_PSK_WITH_AES_256_CCM,
          TlsCipherSuite.TLS_PSK_WITH_AES_128_CCM_8,
          TlsCipherSuite.TLS_PSK_WITH_AES_256_CCM_8,
          TlsCipherSuite.TLS_PSK_DHE_WITH_AES_128_CCM_8,
          TlsCipherSuite.TLS_PSK_DHE_WITH_AES_256_CCM_8,
          TlsCipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CCM,
          TlsCipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CCM,
          TlsCipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8,
          TlsCipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8,
          TlsCipherSuite.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256
        };

        authenticationOptions.EnabledSslProtocols = SslProtocols.Tls12;
        authenticationOptions.CipherSuitesPolicy = new CipherSuitesPolicy(ciphers);
      };
    });
  });
});

咳咳,到這裡為止,我們滿心歡喜,是不是就這樣愉快結束了?接觸到一個新的點時,一定一定要先看下解釋,別一頓操作後不好使,耗費時間和勞力而全是無用功,最終才發現問題癥結點所在,這也是我最近才有深刻體會

據我初步的瞭解應該是在.NET Core 3.0+才開始支援配置OpenSSL的套件,且版本必須是1.1.1+,但不支援Windows,僅支援Linux或OSX!同時在.NET Core 3.1以下版本預設協議是1.1或1.2,但在.NET 5預設協議變更為了1.3,其支援套件配置也與OpenSSL配置也有了一定關聯。見連結《 https://docs.microsoft.com/en-us/dotnet/core/whats-new/dotnet-core-3-0

當接觸到某一知識點時(比如TLS),我認為稍微看下更高版本對此方面是否有變更很有必要,假設我們不知道.NET 5+預設變更為了1.3,當進行版本升級過後,如果第三方對接此前使用的是TLS 1.2,那麼資料對接就會斷層,業務必將受影響。僅我個人建議,至於其他我就管不到了~~~基於上述所述,若是在Windows開發環境,勢必要在對應基礎上判斷作業系統

if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX) || RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
{
  ......
}

最終我們掃描結果如下,已將AES-CBC不安全套件給去除,完全滿足安全要求

密碼學基礎

密碼套件所用到的演算法大致有四種:對稱加密演算法、非對稱金鑰交換演算法、數字簽名演算法(DSA)、可選的基於雜湊的訊息身份驗證程式碼(HMAC)

說白了,密碼學套件就是一組演算法,開啟HTTPS和TLS並協同使用密碼學套件,才能使得傳輸更加安全

我們知道演算法都是公開的,唯一不同的是演算法依賴於金鑰,只有對接雙方知道,從而保護加密過後的密文而不能被窺探,那麼接下來我們拆分講解上述幾種演算法

非對稱加密演算法,說白了就是一方擁有公鑰,另一方擁有私鑰,它是瀏覽器和伺服器開始時通過TLS握手時協商作為加密套件使用,至於HTTPS的其餘部分可使用商定的密碼套件進行使用

最初通過TLS握手時所使用的最主要的三種演算法是:

DHE:Diffie-Hellman Ephemeral (金鑰交換演算法)

RSA:以其發明者Rivest-Shamir-Adleman的名字而命名

ECDHE:Elliptic-curve Diffie–Hellman(橢圓曲線交換:翻譯而來)

對稱演算法,說白了就是雙方都知道的單一金鑰,相比較非對稱加密演算法而言,它的計算速度更快,但並不適用於作為Web證書,因為瀏覽器和伺服器不知道也完全不信任彼此,因此無法共享金鑰,然而,在進行初始握手協議之後,再使用對稱加密演算法非常合適即建立一個共享的金鑰以在HTTPS通訊的其他期間使用

最常見的四種對稱加密演算法是:

AES:Advanced Encryption Standard

AES-GCM:AES Galois/Counter

AES-CCM:AES Counter with CBC-MAC(密碼塊連結訊息認證碼)

ChaCha20:又名Salsa20

密碼強度是一種安全度量,加密文字在遭受攻擊時有多安全?演算法的強度與金鑰的長度相關,所以更長的金鑰更強大,金鑰長度表示為位數,常用的值為128 和 256,對稱演算法由它們的首字母縮略詞和它們的金鑰長度來標識,比如像 AES128 或 AES256。

說了這麼一大堆,好像就瞭解到幾個演算法名稱,要是脫離本文的內容確實僅在腦後留下些許印象,那麼接下來我們結合配置的密碼套件來進行實踐理解,我們拿上述配置的第一個密碼套件來進行闡述,其餘同理,如下

 TlsCipherSuite.TLS_DHE_RSA_WITH_AES_128_GCM_SHA256

上述TlsCipherSuite是一個列舉,我們僅看列舉內容,將TLS去掉剩餘則是DHE_RSA_WITH_AES_128_GCM_SHA256

我們再結合上述闡述的非對稱和對稱加密演算法將其拆分,則是DHE、RSA、AES-128-GCM

從最終拆分的結果來看,實際上HTTPS使用了一對演算法而非單一演算法,其中一種非對稱加密DHE和RSA用於初始期間TLS握手,而另一種對稱加密演算法AES-128-GCM用於資料傳輸期間的加密和解密!如此一來對於HTTPS使用對稱和非對稱加密演算法則形成多種組合

但簡單地選擇一對非對稱/對稱演算法不足以完全識別密碼套件,所以還需要指定用於確保身份驗證和完整性的規則。比如通過LetsEncrypt證書機構來頒發證書。更深層次的密碼學東東這裡就不再展開了,我也就探究於此

.NET Core中配置的密碼套件與OpenSSL所支援套件吻合,如下截圖一部分:

那麼問題來了,上述我們配置了多種套件,結果通過工具掃描時有三種被支援的套件被掃描出,瀏覽器和伺服器初次進行TLS握手時,到底選擇哪一種呢?那麼我們接下來通過訪問並檢視Web證書能否找到蛛絲馬跡,事先宣告:以下部分內容為我個人推測,至於理論上是否如真如我所言未可知

通過檢視證書反推,我們通過OpenSSL建立自簽名證書所使用的是RSA加sha256演算法,然後再看如下圖可知所使用最終套件

結合這二者是不是就可以說明所使用的密碼套件對應OpenSSL配置則是:ECDHE-RSA-AES128-GCM-SHA256,也就是對應通過nmap工具掃描出來支援的三種套件中的第一個

ECDHE-RSA-AES128-GCM-SHA256結論由上述證書和安全檢視而言,因為是sha256RSA所以密碼套件最後則是SHA256,果真是如此,我能否自圓其說?要是我們將SHA256套件刪除,是不是頁面就不支援,Web頁面無法訪問呢?

經過上述修改和通過nmap掃描出結果如下:

看來與證書上所言演算法無關,最終回到我通過自建立證書命令

openssl genrsa -out ca-key.key 3072

只能匹配到所使用RSA非對稱加密演算法,OpenSSL可指定加密方式

但當我指定aes128或其他位時,其模式其實是CBC而非GCM

最終檢視是否支援GCM

openssl aes-256-gcm

其實OpenSSL支援GCM,只不過不能用過命令列來進行操作,具體原因官方有解釋。分析了這麼多,貌似沒啥用,目前大致能確定的是: 密碼套件的支援還是需要瀏覽器和伺服器來進行握手協商

比如在谷歌瀏覽器中就可設定是否啟用TLS 1.3

到目前為止,我們大致知道了HTTPS所使用的是一組演算法,握手期間用非對稱加密演算法,資料傳輸期間用對稱加密演算法!那麼整個期間,猜測是結合生成的證書,然後遍歷配置套件進行握手,握手成功後再進行資料傳輸

大致握手和資料傳輸過程,簡單描述如下:

客戶端向服務端傳送訊息,我使用TLS 1.2中的ECDHE-RSA-AES128-GCM-SHA256以及還有其他套件,你能處理嗎?服務端向客戶端回覆訊息,ECDHE-RSA-AES128-GCM-SHA256能處理,然後向客戶端發出公鑰證書。客戶端向服務端回覆訊息,證書是合法的,客戶端生成金鑰6p7vUjFz緊接著使用服務端的公鑰證書進行加密,通知服務端以後資料傳輸用指定的金鑰即共享金鑰

當自定義配置所支援套件時,需要額外注意一點的是,千萬別同時指定TLS 1.2和TLS 1.3

總結

啊,又到了總結的地方,貌似沒啥可總結的,具體相關了解到的或體會到的已在文中有闡述,我們下節再會