快速开始

下面的描述使用变量名称 $CATALINA_BASE 来引用解析大多数相对路径所依据的基目录。 如果没有通过设置 CATALINA_BASE 目录为多个实例配置 Tomcat, 则 $CATALINA_BASE 将设置为 $CATALINA_HOME 的值,即安装 Tomcat 的目录。

要在 Tomcat 上安装和配置 SSL/TLS 支持,需要遵循以下简单步骤。 有关更多信息,请阅读本操作指南的其余部分。

  • 通过执行以下命令,创建一个密钥库文件来存储服务器的私钥和自签名证书:

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” 条目,并按照下面的 Configuration 部分所述进行修改。

SSL/TLS 简介

传输层安全性 (TLS) 及其前身安全套接字层 (SSL) 是允许 Web 浏览器和 Web 服务器通过安全连接进行通信的技术。 这意味着发送的数据由一端加密、传输,然后由另一端解密,然后再进行处理。 这是一个双向过程,这意味着服务器和浏览器在发送数据之前都会加密所有流量。

SSL/TLS 协议的另一个重要方面是身份验证。 这意味着,在首次尝试通过安全连接与 Web 服务器通信时, 该服务器将以“证书”的形式向Web 浏览器提供一组凭据, 以证明该网站是它所声称的身份和身份。 在某些情况下,服务器还可能从 Web 浏览器请求证书,要求证明是声称的身份。 这被称为 “Client Authentication”(客户端身份验证), 尽管在实践中,它更多地用于企业对企业 (B2B) 交易,而不是个人用户。 大多数启用 SSL 的 Web 服务器不请求客户端身份验证。

SSL/TLS 和 Tomcat

需要注意的是,通常只有在将 Tomcat 作为独立的 Web 服务器运行时, 才需要配置 Tomcat 以利用安全套接字。有关详细信息,请参阅安全注意事项文档。 当 Tomcat 主要作为其他 Web 服务器(如 Apache 或 Microsoft IIS)后面的 Servlet/JSP 容器运行时, 通常需要配置主 Web 服务器以处理来自用户的 SSL 连接。 通常,此服务器将协商所有与 SSL 相关的功能,然后仅在解密这些请求后传递发往 Tomcat 容器的任何请求。 同样,Tomcat 将返回明文响应,这些响应将在返回给用户的浏览器之前进行加密。 在此环境中,Tomcat 知道主 Web 服务器和客户端之间的通信是通过安全连接进行的 (因为应用程序需要能够询问这一点),但它不参与加密或解密本身。

Tomcat 能够使用底层环境提供的任何加密协议。 Java 本身通过 JCE/JCA 提供加密功能,通过 JSSE 提供加密通信功能。 任何兼容的加密“提供程序”都可以向 Tomcat 提供加密算法。 内置提供程序 (SunJCE) 包括对各种 SSL/TLS 版本(如 SSLv3、TLSv1、TLSv1.1 等)的支持。 有关协议和算法支持的详细信息,请查看您的 Java 版本的文档。

如果使用可选的 tcnative 库,则可以通过 JCA/JCE/JSSE 使用 OpenSSL 加密提供程序, 这可能提供与 SunJCE 提供程序不同的加密算法选择和/或性能优势。 有关协议和算法支持的详细信息,请查看 OpenSSL 版本的文档。

证书

为了实施 SSL,Web 服务器必须为接受安全连接的每个外部接口(IP 地址)提供关联的证书。 这种设计背后的理论是,服务器应该提供某种合理的保证, 证明其所有者是认为的那个人,尤其是在收到任何敏感信息之前。 虽然对证书的更广泛解释超出了本文档的范围,但请将证书视为 Internet 地址的“数字护照”。 说明了站点与哪个组织相关联,以及有关站点所有者或管理员的一些基本联系信息。

此证书由其所有者加密签名,因此其他人极难伪造。 要使证书在访客浏览器中正常工作而不发出警告,需要由受信任的第三方签名。 这些称为证书颁发机构 (CA)。要获取签名证书,需要选择一个 CA 并按照所选 CA 提供的说明进行操作

此证书由其所有者加密签名,因此其他人极难伪造。 要使证书在访客浏览器中正常工作而不发出警告,需要由受信任的第三方签名。 这些称为证书颁发机构 (CA)。要获取签名证书,需要选择一个 CA, 然后按照所选 CA 提供的说明获取证书。有一系列 CA 可用,包括一些免费提供证书的 CA。

Java 提供了一个相对简单的命令行工具,称为 keytool,它可以轻松创建“自签名”证书。 自签名证书只是用户生成的证书,这些证书尚未由知名 CA 签名,因此,根本无法真正保证其真实性。 虽然自签名证书对于某些测试方案可能很有用,但它们不适合任何形式的生产使用。

运行 SSL 的一般提示

使用 SSL 保护网站时,请务必确保网站使用的所有资产都通过 SSL 提供, 这样攻击者就无法通过在 JavaScript 文件或类似文件中注入恶意内容来绕过安全性。 为了进一步增强网站的安全性,应该评估使用 HSTS 标头。 它允许与浏览器通信,站点应始终通过 https 访问。

在安全连接上使用基于名称的虚拟主机需要仔细配置在单个证书或 Tomcat 8.5 及更高版本中指定的名称, 其中服务器名称指示 (SNI, Server Name Indication) 支持可用。 SNI 允许将具有不同名称的多个证书与单个 TLS 连接器相关联。

配置

准备证书密钥库

Tomcat 当前仅在 JKSPKCS11PKCS12 格式的密钥库上运行。 JKS 格式是 Java 的标准 “Java KeyStore” 格式, 是由 keytool 命令行实用程序创建的格式。此工具包含在 JDK 中。 PKCS12 格式是一种 Internet 标准,可以通过 OpenSSL 和 Microsoft 的 Key-Manager 进行操作。

密钥库中的每个条目都由别名字符串标识。 虽然许多密钥库实现以不区分大小写的方式处理别名,但可以使用区分大小写的实现。 例如,PKCS11 规范要求别名区分大小写。 为避免与别名区分大小写相关的问题,建议不要使用仅区分大小写的别名。

要将现有证书导入 JKS 密钥库,请阅读有关 keytool 的文档(在 JDK 文档包中)。 请注意,OpenSSL 通常会在 key 之前添加可读的 comments,但 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 参数, 后跟 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 配置文件中指定自定义密码,如下所述。

接下来,系统将提示您输入有关此 Certificate 的一般信息,例如公司、联系人姓名等。 此信息将向尝试访问应用程序中安全页面的用户显示,因此请确保此处提供的信息与他们预期的信息相符。

最后,系统将提示输入密钥密码,这是专门用于此证书的密码 (与存储在同一密钥库文件中的任何其他证书相反)。 keytool 提示符将提示,按 ENTER 键会自动使用与密钥库相同的密钥密码。 可以自由使用相同的密码或选择自定义密码。 如果选择与密钥库密码不同的密码,则还需要在 server.xml 配置文件中指定自定义密码。

如果一切顺利,现在拥有一个密钥库文件,其中包含可供服务器使用的 Certificate。

编辑 Tomcat 配置文件

Tomcat 可以使用两种不同的 SSL 实现:

  • 作为 Java 运行时的一部分提供的 JSSE 实现

  • 使用 OpenSSL 的 JSSE 实现

确切的配置详细信息取决于所使用的实现。 如果通过指定通用的 protocol="HTTP/1.1" 来配置 Connector,则会自动选择 Tomcat 使用的实现。

如果需要,可以避免自动选择实现。 它是通过在 Connector 的 protocol 属性中指定 classname 来完成的。

要定义 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 实现。 如果安装了 Tomcat Native 库或 Java 22, 则使用 sslImplementationName 属性,并允许启用。 使用 OpenSSL JSSE 实现时,配置可以使用 JSSE 属性或 OpenSSL 属性, 但不能在同一 SSLHostConfig 或 Connector 元素中混合使用两种类型的属性。

使用 Tomcat 原生库:

<!-- 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"
           .../>

使用 Java 22 FFM API:

<!-- 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.panama.OpenSSLImplementation"
           .../>

或者,可以将侦听器添加到服务器以在所有连接器上启用 OpenSSL, 而无需在每个连接器上添加 sslImplementationName 属性。

使用 Tomcat Native:

<Listener className="org.apache.catalina.core.AprLifecycleListener"/>

使用 Java 22 FFM API:

<Listener className="org.apache.catalina.core.OpenSSLLifecycleListener"/>

侦听器的 SSLRandomSeed 属性允许指定熵源。 生产系统需要一个可靠的熵源,但熵可能需要大量时间来收集, 因此测试系统不能使用像 “/dev/urandom” 这样的阻塞熵源, 这将允许更快地启动 Tomcat。

最后一步是在 $CATALINA_BASE/conf/server.xml 文件中配置 Connector, 其中 $CATALINA_BASE 表示 Tomcat 实例的基目录。 <Connector> SSL 连接器的示例元素包含在随 Tomcat 一起安装的默认 server.xml 文件中。 要配置使用具有 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">
  <SSLHostConfig>
    <Certificate
      certificateKeystoreFile="${user.home}/.keystore"
      certificateKeystorePassword="changeit"
      type="RSA"
      />
    </SSLHostConfig>
</Connector>

OpenSSL 配置样式对许多 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">
  <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 应用程序。例如,尝试:

应该看到通常的 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 的文件,可以将其提交给证书颁发机构 (查看证书颁发机构网站的文档,了解如何执行此操作)。然后将获得证书。

导入证书

现在拥有了证书,可以将其导入到本地密钥库中。 首先,必须将所谓的 Chain Certificate 或 Root Certificate 导入到密钥库中。 之后,可以继续导入证书。

  • 从从中获取证书的证书颁发机构下载链证书。

    • 有关 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

  • 将 Chain Certificate 导入到密钥库中

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 证书

Apache Tomcat 中对在线证书状态协议 (OCSP) 的支持使用 OpenSSL。 这可以通过 Tomcat Native 或 Java 22 及更高版本上的 FFM API 使用。

要使用 OCSP,需要满足以下条件:

  • 启用 OCSP 的证书

  • 具有启用 OpenSSL 的连接器的 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 编码到证书中。 请注意,对于以下步骤,必须准备好 CA 的 openssl.cnf 和其他配置。 要生成启用 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 希望密钥库文件在运行 Tomcat 的用户主目录中命名为 .keystore 该目录可能相同,也可能不同。如果密钥库文件位于其他任何位置, 则需要将 certificateKeystoreFile 属性添加到 <Certificate> Tomcat 配置文件中的元素中。

  • 当 Tomcat 启动时,收到类似“java.io.FileNotFoundException: Keystore was tamed with, or password was incorrect”的异常。

假设有人实际上没有篡改密钥库文件,最可能的原因是 Tomcat 使用的密码与在创建密钥库文件时使用的密码不同。 要解决此问题,可以返回并重新创建 keystore 文件, 也可以在 <Connector> Tomcat 配置文件中的元素上添加或更新 keystorePass 属性。 提醒 - 密码区分大小写!

  • 当 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 在指定的密钥库中找不到服务器密钥的别名。 检查是否在 <Certificate> Tomcat 配置文件的元素中指定了正确的 certificateKeystoreFilecertificateKeyAlias提醒 - keyAlias 值可能区分大小写!

如果仍然遇到问题,TOMCAT-USER 邮件列表是一个很好的信息来源。 可以在此列表中找到指向以前消息存档的指针,以及订阅和退订信息, 网址为 https://tomcat.apache.org/lists.html。

在应用程序中使用 SSL 进行会话跟踪

这是 Servlet 3.0 规范中的一个新功能。 由于它使用与物理客户端-服务器连接关联的 SSL 会话 ID,因此存在一些限制。是:

  • Tomcat 必须具有属性 isSecure 设置为 true 的连接器。

  • 如果 SSL 连接由代理或硬件加速器管理,则它们必须填充 SSL 请求头(请参阅 SSLValve),以便 SSL 会话标识对 Tomcat 可见。

  • 如果 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");