下列說明將使用變量名 $CATALINA_BASE
來表示多數相對路徑所基于的基本目錄。如果沒有為 Tomcat 多個實例設置 CATALINA_BASE 目錄,則 $CATALINA_BASE
就會設定為 $CATALINA_HOME
的值,也就是你安裝 Tomcat 的目錄。
在 Tomcat 中安裝并配置 SSL/TLS 支持,只需遵循下列幾步即可。詳細信息可參看文檔后續(xù)介紹。
創(chuàng)建一個 keystore 文件保存服務器的私有密鑰和自簽名證書:
Windows:
"%JAVA_HOME%\bin\keytool" -genkey -alias tomcat -keyalg RSA
UNIX:
$JAVA_HOME/bin/keytool -genkey -alias tomcat -keyalg RSA
指定密碼為“changeit”。
$CATALINA_BASE/conf/server.xml
中 “SSL HTTP/1.1 Connector” 一項的注釋狀態(tài)。按照下文中配置一節(jié)所描述的方式進行修改。 安全傳輸層協(xié)議(TLS)與其前輩加密套接字(SSL)都是用于保證 Web 瀏覽器與 Web 服務器通過安全連接進行通信的技術。利用這些技術,我們所要傳送的數據會在一端進行加密,傳輸到另一端后再進行解密(在處理數據之前)。這是一種雙向的操作,服務器和瀏覽器都能在發(fā)送數據前對它們進行加密處理。
SSL/TLS 協(xié)議的另一個重要方面是認證。當我們初始通過安全連接與 Web 服務器進行通信時,服務器將提供給 Web 瀏覽器一組“證書”形式的憑證,用來證明站點的歸屬方以及站點的具體聲明。某些情況下,服務器也會請求瀏覽器出示證書,來證明作為操作者的“你”所宣稱的身份是否屬實。這種證書叫做“客戶端證書”,但事實上它更多地用于 B2B(企業(yè)對企業(yè)電子商務)的交易中,而并非針對個人用戶。大多數啟用了 SSL 協(xié)議的 Web 服務器都不需要客戶端認證。
一定要注意的是,通常只有當 Tomcat 是獨立運行的 Web 服務器時,才有必要去配置 Tomcat 以便利用加密套接字。具體細節(jié)可參看 Security Considerations Document。當 Tomcat 以 Servlet/JSP 容器的形式在其他 Web 服務器(比如 Apache 或 Microsoft IIS)背后運行時,通常需要配置的是主 Web 服務器,用主服務器來處理與用戶的 SSL 連接。主服務器一般會利用所有的 SSL 相關功能,將任何只有 Tomcat 才能處理的請求進行解密后再傳給 Tomcat。同樣,Tomcat 會返回明文形式的響應,這些響應在被傳輸到用戶瀏覽器之前會被主服務器進行加密處理。在這種情境下,Tomcat 知道主服務器與客戶端的所有通信都是通過安全連接進行的(因為應用要求),但 Tomcat 自身無法參與到加密與解密的過程中。
為了實現 SSL,Web 服務器必須對每一個接受安全連接的外部接口(IP 地址)配備相應的證書。這種設計方式的理論在于,服務器必須提供某種可信的保證(尤其對于接收敏感信息而言),保證它的擁有者是你所認為的角色。限于本章篇幅,不再贅述關于證書的詳細解釋,只需要把它認為成是一種 IP 地址的“數字駕照”即可。它聲明了與站點相關的組織,以及一些關于站點擁有者或管理者的基本聯系信息。
這種“數字駕照”的持有者對其進行了加密簽名,從而使得它極難偽造。對于參與電子商務的站點或者其他一些身份驗證顯得非常重要的商業(yè)交易來說,證書通常都是從一些比較權威公正的 CA ( Certificate Authority,認證機構)購買的,比較知名的 CA 有 VeriSign 、Thawte 等。這些證書可經電子驗證。實際上,CA 會保證所頒發(fā)證書的真實性,所以你完全可以放心。
不過,在很多情況下,驗證并不是問題的關鍵。管理員可能只想保證服務器所傳輸與接收的數據是秘密的,不會被某些人通過連接來竊取。幸運的是,Java 提供了一個簡單的命令行工具:keytool
。它能輕松創(chuàng)建一個“自簽名”的證書,這種自簽名證書是由用戶生成的一種證書,未經任何知名 CA 給予官方保證,因此它的真實性也無法確定。再說一次,是否認證,完全根據你的需求。
當用戶首次訪問你站點上的安全頁面時,頁面通常會提供給他一個對話框,包含證書相關細節(jié)(比如組織及聯系方式等),并且詢問他是否愿意承認該證書為有效證書,然后再進行下一步的事務。一些瀏覽器可能會提供一個選項,允許永遠承認給出的證書的有效性,這樣就不會在用戶每次訪問站點時打擾他們了。但有些瀏覽器不會提供這種選項。一旦用戶承認了證書的有效性,那么在整個的瀏覽器會話期間,證書都被認為是有效的。
雖然 SSL 協(xié)議的意圖是盡可能有助于提供安全且高效的連接,但從性能角度來考慮,加密與解密是非茶館耗費計算資源的,因此將整個 Web 應用都運行在 SSL 協(xié)議下是完全沒有必要的,開發(fā)者需要挑選需要安全連接的頁面。對于一個相當繁忙的網站來說,通常只會在特定頁面上使用 SSL 協(xié)議,也就是可能交換敏感信息的頁面,比如:登錄頁面、個人信息頁面、購物車結賬頁面(可能會輸入信用卡信息),等等。應用中的任何一個頁面都可以通過加密套接字來請求訪問,只需將頁面地址的前綴 http:
換成 https:
即可。絕對需要安全連接的頁面應該檢查該頁面請求所關聯的協(xié)議類型,如果發(fā)現沒有指定 https:
,則采取適當行為。
最后,在安全連接上使用基于名稱的虛擬主機可能會造成一定的問題。這正是 SSL 協(xié)議的局限。SSL 握手過程,即客戶端瀏覽器接收服務器證書,必須發(fā)生在 HTTP 請求被訪問前。因此包含虛擬主機名稱的請求信息不能先于認證而確定,也不可能為單個 IP 地址指定多個證書。如果單個 IP 地址的所有虛擬主機都需要利用同樣證書來認證的話,那么多個虛擬主機不應該干涉服務器通常的 SSL 操作。但是要注意一點,多數客戶端瀏覽器會將服務器域名與證書中的多個域名(如果存在的話,)進行對比,如果域名出現不匹配,則瀏覽器會向用戶顯示警告信息。一般來說,生產環(huán)境中,通常只有使用基于地址的虛擬主機利用 SSL。
Tomcat 目前只能操作 JKS
、PKCS11
、PKCS12
格式的密鑰存儲庫。JKS
是 Java 標準的“Java 密鑰存儲庫”格式,是通過 keytool
命令行工具創(chuàng)建的。該工具包含在 JDK 中。PKCS12
格式一種互聯網標準,可以通過 OpenSSL 和 Microsoft 的 Key-Manager 來。
密鑰存儲庫中的每一項都通過一個別名字符串來標識。盡管許多密碼存儲庫實現都在處理別名時不區(qū)分大小寫,但區(qū)分大小寫的實現也是允許的。比如,PKCS11
規(guī)范需要別名是區(qū)分大小寫的。為了避免別名大小寫敏感的問題,不建議使用只有大小寫不同的別名。
為了將現有的證書導入 JKS
密碼存儲庫,請查閱關于 keytool
的相關文檔(位于 JDK 文檔包里)。注意,OpenSSL 經常會在密碼前加上易于理解的注釋,但 keytool
并不支持這一點。所以如果證書里的密碼數據前面有注釋的話,在利用 keytool
導入證書前,一定要清除它們。
要想把一個已有的由你自己的 CA 所簽名的證書導入使用 OpenSSL 的 PKCS12
密碼存儲庫,應該執(zhí)行如下命令:
openssl pkcs12 -export -in mycert.crt -inkey mykey.key
-out mycert.p12 -name tomcat -CAfile myCA.crt
-caname root -chain
更復雜的實例,請參考 OpenSSL 文檔的相關內容。
下面這個實例展示的是如何利用終端命令行,從零開始創(chuàng)建一個新的 JKS
密碼存儲庫,該密碼庫包含一個自簽名的證書。
Windows:
"%JAVA_HOME%\bin\keytool" -genkey -alias tomcat -keyalg RSA
Unix:
$JAVA_HOME/bin/keytool -genkey -alias tomcat -keyalg RSA
(RSA 算法應該作為首選的安全算法,這同樣也能保證與其他服務器和組件的大體的兼容性。)
該命令將在用戶的主目錄下創(chuàng)建一個新文件:.keystore
。要想指定一個不同的位置或文件名,可以在上述的 keytool
命令上添加 -keystore
參數,后跟到達 keystore 文件的完整路徑名。你還需要把這個新位置指定到 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
執(zhí)行該命令后,首先會提示你提供 keystore 的密碼。Tomcat 默認使用的密碼是 changeit
(全部字母都小寫),當然你可以指定一個自定義密碼(如果你愿意)。同樣,你也需要將這個自定義密碼在 server.xml
配置文件內進行指定,稍后再予以詳述。
接下來會提示關于證書的一般信息,比如組織、聯系人名稱,等等。當用戶試圖在你的應用中訪問一個安全頁面時,該信息會顯示給用戶,所以一定要確保所提供的信息與用戶所期望看到的內容保持一致。
最后,還需要輸入密鑰密碼(key password),這個密碼是這一證書(而不是存儲在同一密碼存儲庫文件中的其他證書)的專有密碼。keytool
提示會告訴你,如果按下回車鍵,則自動使用密碼存儲庫 keystore 的密碼。當然,除了這個密碼,你也可以自定義自己的密碼。如果選擇自定義密碼,那么不要忘了在 server.xml
配置文件中指定這一密碼。
如果操作全部正常,我們現在就會得到一個服務器能使用的有證書的密碼存儲庫文件。
Tomcat 能夠使用兩種 SSL 實現:
詳細的配置信息依賴于所用的實現方式。如果通過指定通用的 protocol="HTTP/1.1"
來配置連接起,那么就會自動選擇 Tomcat 所要用到的實現方式。如果安裝使用的是 APR(比如你安裝了 Tomcat 的原生庫),那么它將使用 APR 的 SSL 實現,否則就將使用 Java 所提供的 JSSE 實現。
由于這兩種 SSL 實現在 SSL 支持的配置屬性上有很大差異,所以強烈建議不要自動選擇實現方式。選擇實現應該采取這種方式:在連接器的 protocol
屬性中,通過指定類名來確立實現方式。
定義 Java(JSSE)連接器,不管 APR 庫是否已經加載,都可以使用下列方式:
<!-- Define a HTTP/1.1 Connector on port 8443, JSSE NIO implementation -->
<Connector protocol="org.apache.coyote.http11.Http11NioProtocol"
port="8443" .../>
<!-- Define a HTTP/1.1 Connector on port 8443, JSSE NIO2 implementation -->
<Connector protocol="org.apache.coyote.http11.Http11Nio2Protocol"
port="8443" .../>
<!-- Define a HTTP/1.1 Connector on port 8443, JSSE BIO implementation -->
<Connector protocol="org.apache.coyote.http11.Http11Protocol"
port="8443" .../>
另一種方法,指定 APR 連接器(APR 庫必須可用),則使用:
<!-- Define a HTTP/1.1 Connector on port 8443, APR implementation -->
<Connector protocol="org.apache.coyote.http11.Http11AprProtocol"
port="8443" .../>
如果使用 APR,則會出現一個選項,從而可以配置另一種 OpenSSL 引擎。
<Listener className="org.apache.catalina.core.AprLifecycleListener"
SSLEngine="someengine" SSLRandomSeed="somedevice" />
默認值為:
<Listener className="org.apache.catalina.core.AprLifecycleListener"
SSLEngine="on" SSLRandomSeed="builtin" />
所以要想使用 APR 實現,一定要確保 SSLEngine
屬性值不能為 off
。該屬性值默認為 on
,如果指定的是其他值,它也會成為一個有效的引擎名稱。
SSLRandomSeed
屬性指定了一個熵源。生產系統(tǒng)需要可靠的熵源,但熵可能需要大量時間來采集,因此測試系統(tǒng)會使用非阻塞的熵源,比如像“/dev/urandom”,從而能夠更快地啟動 Tomcat。
最后一步是在 $CATALINA_BASE/conf/server.xml
中配置連接器,$CATALINA_BASE
表示的是 Tomcat 實例的基本目錄。Tomcat 安裝的默認 server.xml
文件中包含一個用于 SSL 連接器的 <Connector>
元素的范例。為了配置使用 JSSE 的 SSL 連接器,你可能需要清除注釋并按照如下的方式來編輯它。
<!-- Define a SSL Coyote HTTP/1.1 Connector on port 8443 -->
<Connector
protocol="org.apache.coyote.http11.Http11NioProtocol"
port="8443" maxThreads="200"
scheme="https" secure="true" SSLEnabled="true"
keystoreFile="${user.home}/.keystore" keystorePass="changeit"
clientAuth="false" sslProtocol="TLS"/>
APR 連接器會使用很多不同的屬性來設置 SSL,特別是密鑰和證書。 APR 配置范例如下:
<!-- Define a SSL Coyote HTTP/1.1 Connector on port 8443 -->
<Connector
protocol="org.apache.coyote.http11.Http11AprProtocol"
port="8443" maxThreads="200"
scheme="https" secure="true" SSLEnabled="true"
SSLCertificateFile="/usr/local/ssl/server.crt"
SSLCertificateKeyFile="/usr/local/ssl/server.pem"
SSLVerifyClient="optional" SSLProtocol="TLSv1+TLSv1.1+TLSv1.2"/>
每個屬性所用的配置信息與選項都是強制的,可查看 HTTP 連接器配置參考文檔中的 SSL 支持部分。一定要確保對所使用的連接器采用正確的屬性。BIO、NIO 以及 NIO2 連接器都使用 JSSE,然而APR以及原生的連接器則使用 APR。
port
屬性指的是 Tomcat 用以偵聽安全連接的 TCP/IP 端口號。你可以隨意改變它,比如改成 https
的默認端口號 443。但是在很多操作系統(tǒng)中,在低于 1024 的端口上運行 Tomcat 必須進行一番特殊的配置,限于篇幅,本文檔不再贅述。
如果在這里,你更改了端口號,那么也應該在 非 SSL 連接器的 redirectPort
屬性值。從而使 Tomcat 能夠根據 Servlet 規(guī)范,自動對訪問帶有安全限制(指定需要 SSL)頁面的用戶進行重定向。
配置完全部信息后,你應該像往常一樣,重新啟動 Tomcat,從而能夠利用 SSL 來訪問任何 Tomcat 所支持的 Web 應用了。比如:
https://localhost:8443/
你將看到跟往常一樣的 Tomcat 主頁面(除非你修改了 ROOT 應用)。如果出現問題,這樣做沒有任何效果,請看下面的故障排除小節(jié)。
如果想從 CA(比如 verisign.com、thawte.com、trustcenter.de)處獲取并安裝證書,請閱讀之前的內容,然后按照下列操作進行:
為了從選擇的 CA 處獲取證書,必須創(chuàng)建一個證書簽名請求(CSR)。CA 通過 CSR 來創(chuàng)建出一個證書,用來證明你的網站是“安全的”。創(chuàng)建 CSR 的步驟如下:
keytool -genkey -alias tomcat -keyalg RSA
-keystore <your_keystore_filename>
注意:在某些情況下,為了創(chuàng)建一個能耐生效的證書,你必須在“first- and lastname”字段中輸入網站的域名(比如:`www.myside.org`)。
keytool -certreq -keyalg RSA -alias tomcat -file certreq.csr
-keystore <your_keystore_filename>
現在,我們得到了一個 certreq.csr
的文件,可以把它提交給 CA 了(CA 的網站上應有關于如何提交的文檔),審核通過后就會收到一個證書。
接下來可以把證書導入到本地密鑰存儲庫中。首先你需要把鏈證書(Chain Certificate)或根證書(Root Certificate)導入到密鑰存儲庫中。然后繼續(xù)導入證書。
從獲得證書的 CA 那里下載鏈證書。
如選擇 Verisign.com 的商業(yè)證書,則點擊: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>
以下列出了一些在設置 SSL 通信時經常會遇到的問題及其解決方法:
當 Tomcat 啟動時,出現這樣的異常信息:“ava.io.FileNotFoundException: {some-directory}/{some-file} not found.”
這很有可能是因為 Tomcat 無法在指定位置處找到密鑰存儲庫。默認情況下,密鑰存儲庫文件應以 .keystore
為后綴,位于用戶的 home 目錄下(也許很你的設置不同)。如果密鑰存儲庫文件在別處,則需要在 Tomcat 配置文件的 <Factory>
元素中添加一個 keystoreFile
屬性。
當 Tomcat 啟動時,出現這樣的異常信息:“java.io.FileNotFoundException: Keystore was tampered with, or password was incorrect.”
假如排除了有人惡意篡改密鑰存儲庫文件的因素,那么出現這樣的異常,最有可能是因為 Tomcat 現在所用的密碼不同于你當初創(chuàng)建密鑰存儲庫文件時所用密碼。為了解決這個問題,你可以重新創(chuàng)建密鑰存儲庫文件,或者在 Tomcat 配置文件的 <Connector>
元素中添加或更新一個 keystoreFile
屬性。注意:密碼都是區(qū)分大小寫的。
當 Tomcat 啟動時,出現這樣的異常:“java.net.SocketException: SSL handshake error javax.net.ssl.SSLException: No available certificate or key corresponds to the SSL cipher suites which are enabled.”
出現這樣的異常,很有可能是因為 Tomcat 無法在指定的密鑰存儲庫中找到服務器密鑰的別名。查看一下 Tomcat 配置文件的 <Connector>
元素中所指定的 keystoreFile
和 keyAlias
值是否正確。注意:keyAlias
值可能區(qū)分大小寫。
如果出現了其他問題,可以求助 TOMCAT-USER 郵件列表,你可以在該郵件列表內找到之前消息的歸檔文件,以及訂閱和取消訂閱的相關信息。Tomcat 郵件列表的鏈接是:http://tomcat.apache.org/lists.html。
這是一個 Servlet 3.0 規(guī)范中的新功能。由于它將 SSL 會話 ID 關聯到物理的客戶端服務器連接上,所以導致了一些約束與局限:
true
的連接器。 為了開啟 SSL 會話跟蹤,只需使用上下文偵聽器將上下文的跟蹤模式設定為 SSL 即可(如果還開啟了其他跟蹤模式,則會優(yōu)先使用它)。如下所示:
package org.apache.tomcat.example;
import java.util.EnumSet;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.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 會話跟蹤是針對 BIO、NIO 以及 NIO2 連接器來實現的,目前還沒有針對 APR 連接器的實現。
要想從請求中訪問 SSL 會話 ID,可使用:
String sslID = (String)request.getAttribute("javax.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("javax.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");
注意:由于使用了 SSLSessionManager 類,所以這段代碼是專門針對 Tomcat 的。當前只適用于 BIO、NIO 以及 NIO2 連接器,不適合 APR/原生連接器。