SSL/TLS 組態操作指南

目錄

快速入門

以下說明使用變數名稱 $CATALINA_BASE 來表示解析大多數相對路徑的基礎目錄。如果您尚未透過設定 CATALINA_BASE 目錄來為 Tomcat 組態多個執行個體,則 $CATALINA_BASE 將設定為 $CATALINA_HOME 的值,也就是您安裝 Tomcat 的目錄。

若要在 Tomcat 上安裝和組態 SSL/TLS 支援,您需要遵循下列簡單步驟。如需更多資訊,請閱讀本操作指南的其餘部分。

  1. 執行下列指令,建立一個金鑰庫檔案來儲存伺服器的私人金鑰和自簽憑證

    Windows

    "%JAVA_HOME%\bin\keytool" -genkey -alias tomcat -keyalg RSA

    Unix

    $JAVA_HOME/bin/keytool -genkey -alias tomcat -keyalg RSA

    並指定密碼值為「changeit」。

  2. 取消註解 $CATALINA_BASE/conf/server.xml 中的「SSL HTTP/1.1 連接器」項目,並根據下方 組態區段 中的說明進行修改。

SSL/TLS 簡介

傳輸層安全性 (TLS) 及其前身安全通訊協定層 (SSL) 是允許網路瀏覽器和網路伺服器透過安全連線進行通訊的技術。這表示傳送的資料會由一方加密,然後傳輸,再由另一方在處理前解密。這是一個雙向的過程,表示伺服器和瀏覽器在傳送資料前都會加密所有流量。

SSL/TLS 協定的另一個重要面向是驗證。這表示在您嘗試透過安全連線與網路伺服器進行通訊時,該伺服器會以「憑證」的形式向您的網路瀏覽器提供一組憑證,作為該網站是其所宣稱的身分和性質的證明。在某些情況下,伺服器也可能會要求您的網路瀏覽器提供憑證,以證明是您所宣稱的身分。這稱為「客戶端驗證」,儘管實際上這較常使用於企業對企業 (B2B) 交易,而非個人使用者。大多數啟用 SSL 的網路伺服器不會要求客戶端驗證。

SSL/TLS 和 Tomcat

請務必注意,只有在以獨立網路伺服器執行 Tomcat 時,才通常需要設定 Tomcat 以利用安全通訊協定。您可以在 安全性考量文件 中找到詳細資訊。當 Tomcat 主要作為 Apache 或 Microsoft IIS 等其他網路伺服器後方的 Servlet/JSP 容器執行時,通常需要設定主要網路伺服器來處理使用者的 SSL 連線。一般來說,此伺服器會協商所有與 SSL 相關的功能,然後在解密這些要求後,再傳遞任何指定給 Tomcat 容器的要求。同樣地,Tomcat 會傳回明文回應,這些回應會在傳回使用者的瀏覽器之前進行加密。在此環境中,Tomcat 知道主要網路伺服器與客戶端之間的通訊是透過安全連線進行的(因為您的應用程式需要能夠詢問此事),但它本身並未參與加密或解密。

Tomcat 能夠使用基礎環境提供的任何加密通訊協定。Java 本身透過 JCE/JCA 提供加密功能,並透過 JSSE 提供加密通訊功能。任何相容的加密「提供者」都可以向 Tomcat 提供加密演算法。內建提供者 (SunJCE) 包含對各種 SSL/TLS 版本(例如 SSLv3、TLSv1、TLSv1.1 等)的支援。請查看您 Java 版本的文件,以取得有關通訊協定和演算法支援的詳細資訊。

如果您使用選用的 tcnative 函式庫,您可以透過 JCA/JCE/JSSE 使用 OpenSSL 加密提供者,這可能會提供不同選擇的加密演算法和/或相對於 SunJCE 提供者的效能優勢。請查看您 OpenSSL 版本的文件,以取得有關通訊協定和演算法支援的詳細資訊。

憑證

為了實作 SSL,網路伺服器必須為接受安全連線的每個外部介面 (IP 位址) 擁有相關憑證。此設計背後的理論是,伺服器應提供某種合理的保證,證明其所有者就是您所認為的那個人,特別是在收到任何敏感資訊之前。雖然對憑證的更廣泛說明超出了本文的範圍,但請將憑證視為網際網路位址的「數位護照」。它說明該網站與哪個組織相關,以及有關網站所有者或管理員的一些基本聯絡資訊。

此憑證由其擁有者以密碼學方式簽署,因此其他人極難偽造。若要讓憑證在訪客瀏覽器中運作而不會出現警告,需要由受信任的第三方簽署。這些稱為憑證授權機構 (CA)。若要取得已簽署的憑證,您需要選擇 CA,並遵循您選擇的 CA 提供的指示來取得您的憑證。有許多 CA 可供選擇,其中有些 CA 提供免費憑證。

Java 提供一個相對簡單的命令列工具,稱為 keytool,可以輕鬆建立「自我簽署」憑證。自我簽署憑證只是使用者產生的憑證,並未由知名的 CA 簽署,因此並未真正保證其真實性。雖然自我簽署憑證可用於某些測試情境,但並不適合任何形式的生產使用。

執行 SSL 的一般提示

使用 SSL 保護網站時,務必確保網站使用的所有資源都透過 SSL 提供,這樣攻擊者便無法透過在 JavaScript 檔案或類似檔案中注入惡意內容來繞過安全性。若要進一步增強網站安全性,您應評估使用 HSTS 標頭。它讓您可以傳達給瀏覽器,您的網站應始終透過 https 存取。

在安全連線上使用基於名稱的虛擬主機,需要小心設定單一憑證中指定的那些名稱,或 Tomcat 8.5 以上版本,其中支援伺服器名稱指示 (SNI)。SNI 允許將具有不同名稱的多個憑證與單一 TLS 連接器關聯。

組態

準備憑證金鑰庫

Tomcat 目前僅在 JKSPKCS11PKCS12 格式的儲存區中運作。JKS 格式是 Java 的標準「Java 儲存區」格式,也是 keytool 命令列工具建立的格式。此工具包含在 JDK 中。PKCS12 格式是網際網路標準,可透過 (包括) OpenSSL 和 Microsoft 的金鑰管理員來處理。

儲存區中的每個項目都由別名字串識別。雖然許多儲存區實作以不區分大小寫的方式處理別名,但有區分大小寫的實作。例如,PKCS11 規格要求別名區分大小寫。若要避免與別名大小寫敏感性相關的問題,建議不要使用僅在大小寫上不同的別名。

若要將現有憑證匯入 JKS 儲存區,請閱讀關於 keytool 的文件 (在您的 JDK 文件套件中)。請注意,OpenSSL 通常會在金鑰之前加入可讀的註解,但 keytool 不支援此功能。因此,如果您的憑證在金鑰資料之前有註解,請在使用 keytool 匯入憑證之前將其移除。

若要使用 OpenSSL 將由您自己的 CA 簽署的現有憑證匯入 PKCS12 儲存區,您可以執行類似下列的指令

openssl pkcs12 -export -in mycert.crt -inkey mykey.key
                       -out mycert.p12 -name tomcat -CAfile myCA.crt
                       -caname root -chain

對於更進階的案例,請參閱 OpenSSL 文件

若要從頭建立新的 JKS 儲存區,其中包含單一自簽憑證,請從終端機命令列執行下列動作

Windows

"%JAVA_HOME%\bin\keytool" -genkey -alias tomcat -keyalg RSA

Unix

$JAVA_HOME/bin/keytool -genkey -alias tomcat -keyalg RSA

(RSA 演算法應優先作為安全演算法,這也能確保與其他伺服器和組件的一般相容性。)

此命令將在您執行命令的使用者家目錄中建立一個新檔案,命名為「.keystore」。若要指定不同的位置或檔案名稱,請將 -keystore 參數新增到上面顯示的 keytool 命令,後接儲存區檔案的完整路徑。您還需要在 server.xml 組態檔案中反映此新位置,如下所述。例如

Windows

"%JAVA_HOME%\bin\keytool" -genkey -alias tomcat -keyalg RSA
  -keystore \path\to\my\keystore

Unix

$JAVA_HOME/bin/keytool -genkey -alias tomcat -keyalg RSA
  -keystore /path/to/my/keystore

執行此命令後,系統會先提示您輸入儲存區密碼。Tomcat 使用的預設密碼為「changeit」(全部小寫),不過您可以自行指定密碼。您還需要在 server.xml 組態檔案中指定自訂密碼,如下所述。

接下來,系統會提示您輸入此憑證的一般資訊,例如公司、聯絡人姓名等。此資訊將顯示給嘗試存取應用程式中安全頁面的使用者,因此請務必確保在此提供的資訊與他們預期的一致。

最後,系統會提示您輸入金鑰密碼,這是專門針對此憑證的密碼 (與儲存在同一個儲存區檔案中的其他憑證不同)。keytool 提示會告訴您,按下 ENTER 鍵會自動將金鑰的密碼設為與儲存區相同。您可以自由使用相同的密碼或選擇自訂密碼。如果您選擇與儲存區密碼不同的密碼,您還需要在 server.xml 組態檔案中指定自訂密碼。

如果一切順利,您現在有一個儲存區檔案,其中包含伺服器可使用的憑證。

編輯 Tomcat 組態檔案

Tomcat 可以使用兩種不同的 SSL 實作

  • 作為 Java 執行時期一部分提供的 JSSE 實作
  • 使用 OpenSSL 的 JSSE 實作

確切的組態詳細資料取決於使用的實作。如果您透過指定一般 protocol="HTTP/1.1" 來組態 Connector,則 Tomcat 使用的實作會自動選取。如果安裝使用 APR,也就是說您已安裝 Tomcat 本機程式庫,則它將使用 JSSE OpenSSL 實作,否則它將使用 Java JSSE 實作。

如果需要,可以避免自動選取實作。這可透過在 Connectorprotocol 屬性中指定類別名稱來完成。

要定義 Java (JSSE) 連接器,無論 APR 函式庫是否已載入,請使用下列其中一項

<!-- Define an HTTP/1.1 Connector on port 8443, JSSE NIO implementation -->
<Connector protocol="org.apache.coyote.http11.Http11NioProtocol"
           sslImplementationName="org.apache.tomcat.util.net.jsse.JSSEImplementation"
           port="8443" .../>

<!-- Define an HTTP/1.1 Connector on port 8443, JSSE NIO2 implementation -->
<Connector protocol="org.apache.coyote.http11.Http11Nio2Protocol"
           sslImplementationName="org.apache.tomcat.util.net.jsse.JSSEImplementation"
           port="8443" .../>

如有需要,也可以明確設定 OpenSSL JSSE 執行。如果已安裝 APR 函式庫,使用 sslImplementationName 屬性可以啟用它。使用 OpenSSL JSSE 執行時,設定可以使用 JSSE 屬性或 OpenSSL 屬性,但不得在同一個 SSLHostConfig 或 Connector 元素中混合使用兩種類型的屬性。

<!-- Define an HTTP/1.1 Connector on port 8443, JSSE NIO implementation and OpenSSL -->
<Connector protocol="org.apache.coyote.http11.Http11NioProtocol" port="8443"
           sslImplementationName="org.apache.tomcat.util.net.openssl.OpenSSLImplementation"
           .../>

如果您使用的是 JSSE OpenSSL,您可以選擇設定替代引擎至 OpenSSL。

<Listener className="org.apache.catalina.core.AprLifecycleListener"
          SSLEngine="someengine" SSLRandomSeed="somedevice" />

預設值為

<Listener className="org.apache.catalina.core.AprLifecycleListener"
          SSLEngine="on" SSLRandomSeed="builtin" />

因此,要啟用 OpenSSL,請確定 SSLEngine 屬性已設定為除 off 以外的值。預設值為 on,如果您指定其他值,則必須是有效的 OpenSSL 引擎名稱。

SSLRandomSeed 允許指定熵來源。生產系統需要可靠的熵來源,但熵可能需要花費很多時間才能收集,因此測試系統可以使用非封鎖熵來源,例如「/dev/urandom」,這將允許 Tomcat 更快啟動。

最後一個步驟是在 $CATALINA_BASE/conf/server.xml 檔案中設定 Connector,其中 $CATALINA_BASE 代表 Tomcat 執行個體的基本目錄。預設安裝在 Tomcat 中的 server.xml 檔案中包含 SSL 連接器的範例 <Connector> 元素。若要設定使用 JSSE 設定樣式的 JSSE 的 SSL 連接器,您需要移除註解並編輯它,使其看起來像這樣

<!-- Define an SSL Coyote HTTP/1.1 Connector on port 8443 -->
<Connector
    protocol="org.apache.coyote.http11.Http11NioProtocol"
    port="8443"
    maxThreads="150"
    SSLEnabled="true"
    maxParameterCount="1000"
    >
  <SSLHostConfig>
    <Certificate
      certificateKeystoreFile="${user.home}/.keystore"
      certificateKeystorePassword="changeit"
      type="RSA"
      />
    </SSLHostConfig>
</Connector>

注意:如果已安裝 tomcat-native,設定將使用具有 OpenSSL 執行的 JSSE。

APR 設定樣式對許多 SSL 設定使用不同的屬性,特別是金鑰和憑證。APR 設定樣式的範例為

<!-- Define an SSL Coyote HTTP/1.1 Connector on port 8443 -->
<Connector
    protocol="org.apache.coyote.http11.Http11NioProtocol"
    port="8443"
    maxThreads="150"
    SSLEnabled="true"
    maxParameterCount="1000"
    >
  <SSLHostConfig>
    <Certificate
        certificateKeyFile="conf/localhost-rsa-key.pem"
        certificateFile="conf/localhost-rsa-cert.pem"
        certificateChainFile="conf/localhost-rsa-chain.pem"
        type="RSA"
        />
  </SSLHostConfig>
</Connector>

設定選項和哪些屬性為強制性的資訊記載在 HTTP 連接器 設定參考的 SSL 支援區段中。Tomcat 支援所有 TLS 連接器的設定樣式(JSSE 或 OpenSSL)。

port 屬性是 Tomcat 將偵聽安全連線的 TCP/IP 埠號。您可以將它變更為任何您想要的埠號(例如預設的 https 通訊埠 443)。但是,在許多作業系統上,在低於 1024 的埠號上執行 Tomcat 需要特殊設定(不在本文檔的範圍內)。

如果您在此變更埠號,您也應該變更非 SSL 連接器上 redirectPort 屬性指定的數值。這允許 Tomcat 自動重新導向嘗試存取頁面的使用者,而該頁面具有指定 SSL 為必要的安全性限制,這符合 Servlet 規格的要求。

完成這些設定變更後,您必須像平常一樣重新啟動 Tomcat,您應該就可以開始了。您應該能夠透過 SSL 存取 Tomcat 支援的任何 Web 應用程式。例如,嘗試

https://127.0.0.1:8443/

您應該會看到一般的 Tomcat 歡迎頁面(除非您已修改 ROOT Web 應用程式)。如果這不起作用,下列區段包含一些疑難排解提示。

從憑證授權機構安裝憑證

若要取得並安裝來自憑證核發機構(例如 verisign.com、thawte.com 或 trustcenter.de)的憑證,請閱讀前一區段,然後遵循這些指示

建立本機憑證簽署要求 (CSR)

要從您選擇的憑證授權機構取得憑證,您必須建立一個所謂的憑證簽署要求 (CSR)。憑證授權機構將使用該 CSR 建立憑證,以將您的網站識別為「安全」。請遵循下列步驟建立 CSR

  • 建立本機自簽憑證(如前一節所述)
    keytool -genkey -alias tomcat -keyalg RSA
        -keystore <your_keystore_filename>
    注意:在某些情況下,您必須在「名字和姓氏」欄位中輸入網站的網域(例如 www.myside.org),才能建立可運作的憑證。
  • 然後使用下列方式建立 CSR
    keytool -certreq -keyalg RSA -alias tomcat -file certreq.csr
        -keystore <your_keystore_filename>

現在您有一個名為 certreq.csr 的檔案,您可以將其提交給憑證授權機構(請參閱憑證授權機構網站上的文件,了解如何執行此操作)。您將會收到憑證作為回報。

匯入憑證

取得憑證後,您可以將其匯入本機金鑰庫。首先,您必須將所謂的鏈結憑證或根憑證匯入金鑰庫。之後,您可以繼續匯入您的憑證。

  • 從您取得憑證的憑證授權機構下載鏈結憑證。
    對於 Verisign.com 商業憑證,請前往:http://www.verisign.com/support/install/intermediate.html
    對於 Verisign.com 試用憑證,請前往:http://www.verisign.com/support/verisign-intermediate-ca/Trial_Secure_Server_Root/index.html
    對於 Trustcenter.de,請前往:http://www.trustcenter.de/certservices/cacerts/en/en.htm#server
    對於 Thawte.com,請前往:http://www.thawte.com/certs/trustmap.html
  • 將鏈結憑證匯入金鑰庫
    keytool -import -alias root -keystore <your_keystore_filename>
        -trustcacerts -file <filename_of_the_chain_certificate>
  • 最後匯入您的新憑證
    keytool -import -alias tomcat -keystore <your_keystore_filename>
        -file <your_certificate_filename>

每個憑證授權機構都略有不同。他們可能需要略有不同的資訊和/或提供不同格式的憑證和相關憑證鏈。此外,憑證授權機構用於簽發憑證的規則會隨著時間而改變。因此,您可能會發現上述命令可能需要修改。如果您需要協助,則可透過 Apache Tomcat 使用者郵件清單 取得協助。

使用 OCSP 憑證

若要將線上憑證狀態通訊協定 (OCSP) 與 Apache Tomcat 搭配使用,請務必下載、安裝並設定 Tomcat Native Connector。此外,如果您使用 Windows 平台,請務必下載啟用 OCSP 的 Connector。

若要使用 OCSP,您需要下列項目

  • 啟用 OCSP 的憑證
  • 具有 SSL APR 連接器的 Tomcat
  • 已設定的 OCSP 回應器

產生啟用 OCSP 的憑證

Apache Tomcat 要求啟用 OCSP 的憑證在憑證中編碼 OCSP 回應器位置。openssl.cnf 檔案中的基本 OCSP 相關憑證授權設定如下所示


#... omitted for brevity

[x509]
x509_extensions = v3_issued

[v3_issued]
subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid,issuer
# The address of your responder
authorityInfoAccess = OCSP;URI:http://127.0.0.1:8088
keyUsage = critical,digitalSignature,nonRepudiation,keyEncipherment,dataEncipherment,keyAgreement,keyCertSign,cRLSign,encipherOnly,decipherOnly
basicConstraints=critical,CA:FALSE
nsComment="Testing OCSP Certificate"

#... omitted for brevity

上述設定將 OCSP 回應器地址 127.0.0.1:8088 編碼到憑證中。請注意,對於下列步驟,您必須準備好 openssl.cnf 和 CA 的其他設定。如要產生啟用 OCSP 的憑證

  • 建立私密金鑰
    openssl genrsa -aes256 -out ocsp-cert.key 4096
  • 建立簽署要求 (CSR)
    openssl req -config openssl.cnf -new -sha256 \
      -key ocsp-cert.key -out ocsp-cert.csr
  • 簽署 CSR
    openssl ca -openssl.cnf -extensions ocsp -days 375 -notext \
      -md sha256 -in ocsp-cert.csr -out ocsp-cert.crt
  • 您可以驗證憑證
    openssl x509 -noout -text -in ocsp-cert.crt

疑難排解

透過將下列內容新增到 $CATALINA_BASE/conf/logging.properties,設定專用的 TLS 交握記錄器記錄偵錯層級訊息,可以取得有關 TLS 交握失敗的額外資訊

org.apache.tomcat.util.net.NioEndpoint.handshake.level=FINE
org.apache.tomcat.util.net.Nio2Endpoint.handshake.level=FINE
視所使用的連接器而定。

以下是設定 SSL 通訊時可能會遇到的常見問題,以及如何解決這些問題。

  • 當 Tomcat 啟動時,我收到「java.io.FileNotFoundException: {some-directory}/{some-file} not found」等例外狀況。

    可能的解釋是 Tomcat 無法在它尋找的位置找到金鑰庫檔案。預設情況下,Tomcat 預期金鑰庫檔案名稱為 .keystore,位於 Tomcat 執行的使用者家目錄中(可能與您的家目錄相同,也可能不同)。如果金鑰庫檔案位於其他位置,您需要將 certificateKeystoreFile 屬性新增到 Tomcat 設定檔 中的 <Certificate> 元素。

  • 當 Tomcat 啟動時,我收到「java.io.FileNotFoundException: Keystore was tampered with, or password was incorrect」等例外狀況。

    假設沒有人實際上竄改您的金鑰庫檔案,最可能的原因是 Tomcat 使用的密碼與您建立金鑰庫檔案時使用的密碼不同。若要修復此問題,您可以返回 重新建立金鑰庫檔案,或者您可以在 Tomcat 設定檔 中的 <Connector> 元素上新增或更新 keystorePass 屬性。提醒 - 密碼區分大小寫!

  • 當 Tomcat 啟動時,我收到類似「java.net.SocketException: SSL 握手錯誤 javax.net.ssl.SSLException: 沒有可用的憑證或金鑰對應到已啟用的 SSL 加密套件。」的例外狀況。

    可能的解釋是 Tomcat 無法在指定的儲存庫中找到伺服器金鑰的別名。檢查在 Tomcat 組態檔 中的 <Certificate> 元素中是否指定正確的 certificateKeystoreFilecertificateKeyAlias提醒 - keyAlias 值可能區分大小寫!

如果您仍然遇到問題,一個良好的資訊來源是 TOMCAT-USER 郵件清單。您可以在 https://tomcat.dev.org.tw/lists.html 找到此清單中先前訊息的檔案指標,以及訂閱和取消訂閱資訊。

在應用程式中使用 SSL 進行階段追蹤

這是 Servlet 3.0 規範中的新功能。由於它使用與實體客戶端伺服器連線關聯的 SSL 會話 ID,因此有一些限制。它們是

  • Tomcat 必須有一個將屬性 isSecure 設定為 true 的連接器。
  • 如果 SSL 連線是由代理伺服器或硬體加速器管理的,則它們必須填入 SSL 要求標頭(請參閱 SSLValve),以便 Tomcat 可以看到 SSL 會話 ID。
  • 如果 Tomcat 終止 SSL 連線,將無法使用會話複製,因為 SSL 會話 ID 在每個節點上都會不同。

若要啟用 SSL 會話追蹤,您需要使用內容文字監聽器將內容文字的追蹤模式設定為僅 SSL(如果啟用任何其他追蹤模式,它將優先使用)。它可能看起來像

package org.apache.tomcat.example;

import java.util.EnumSet;

import jakarta.servlet.ServletContext;
import jakarta.servlet.ServletContextEvent;
import jakarta.servlet.ServletContextListener;
import jakarta.servlet.SessionTrackingMode;

public class SessionTrackingModeListener implements ServletContextListener {

    @Override
    public void contextDestroyed(ServletContextEvent event) {
        // Do nothing
    }

    @Override
    public void contextInitialized(ServletContextEvent event) {
        ServletContext context = event.getServletContext();
        EnumSet<SessionTrackingMode> modes =
            EnumSet.of(SessionTrackingMode.SSL);

        context.setSessionTrackingModes(modes);
    }

}

其他提示和資訊

若要從要求中存取 SSL 會話 ID,請使用

String sslID = (String)request.getAttribute("jakarta.servlet.request.ssl_session_id");

有關此領域的更多討論,請參閱 Bugzilla

若要終止 SSL 會話,請使用

// Standard HTTP session invalidation
session.invalidate();

// Invalidate the SSL Session
org.apache.tomcat.util.net.SSLSessionManager mgr =
    (org.apache.tomcat.util.net.SSLSessionManager)
    request.getAttribute("jakarta.servlet.request.ssl_session_mgr");
mgr.invalidateSession();

// Close the connection since the SSL session will be active until the connection
// is closed
response.setHeader("Connection", "close");