Tomcat SSL/TLS配置

SSL/TLS Configuration

概述

SSL/TLS是保护Web应用数据传输安全的关键技术。本文详细介绍Tomcat SSL/TLS的配置方法、证书管理、性能优化和故障排除技巧。

1. SSL基础知识与证书准备

1.1 生成自签名证书

#!/bin/bash
# generate-self-signed-cert.sh

KEYSTORE_FILE="conf/keystore.jks"
KEYSTORE_PASS="changeit"
KEY_ALIAS="tomcat"
VALIDITY_DAYS="365"

# 生成密钥库和自签名证书
keytool -genkeypair \
    -alias $KEY_ALIAS \
    -keyalg RSA \
    -keysize 2048 \
    -validity $VALIDITY_DAYS \
    -keystore $KEYSTORE_FILE \
    -storepass $KEYSTORE_PASS \
    -keypass $KEYSTORE_PASS \
    -dname "CN=localhost, OU=IT Department, O=Example Corp, L=City, ST=State, C=US"

echo "自签名证书已生成: $KEYSTORE_FILE"

1.2 使用Let's Encrypt证书

#!/bin/bash
# setup-letsencrypt.sh

DOMAIN="example.com"
EMAIL="admin@example.com"
WEBROOT="/opt/tomcat9/webapps/ROOT"

# 安装Certbot
sudo apt-get update
sudo apt-get install certbot

# 获取证书
sudo certbot certonly \
    --webroot \
    --webroot-path=$WEBROOT \
    --email $EMAIL \
    --agree-tos \
    --no-eff-email \
    -d $DOMAIN

# 转换为Java KeyStore格式
CERT_PATH="/etc/letsencrypt/live/$DOMAIN"
openssl pkcs12 -export \
    -in "$CERT_PATH/fullchain.pem" \
    -inkey "$CERT_PATH/privkey.pem" \
    -out "/tmp/$DOMAIN.p12" \
    -name tomcat \
    -password pass:changeit

keytool -importkeystore \
    -deststorepass changeit \
    -destkeypass changeit \
    -destkeystore "conf/$DOMAIN.jks" \
    -srckeystore "/tmp/$DOMAIN.p12" \
    -srcstoretype PKCS12 \
    -srcstorepass changeit \
    -alias tomcat

echo "Let's Encrypt证书已配置"

1.3 证书续期脚本

#!/bin/bash
# renew-letsencrypt.sh

DOMAIN="example.com"
CERT_PATH="/etc/letsencrypt/live/$DOMAIN"
KEYSTORE_PATH="/opt/tomcat9/conf/$DOMAIN.jks"

# 续期证书
sudo certbot renew --quiet

# 检查是否更新了证书
if [ "$CERT_PATH/fullchain.pem" -nt "$KEYSTORE_PATH" ]; then
    echo "证书已更新,重新生成KeyStore..."

    # 删除旧的KeyStore
    rm -f "$KEYSTORE_PATH"

    # 生成新的KeyStore
    openssl pkcs12 -export \
        -in "$CERT_PATH/fullchain.pem" \
        -inkey "$CERT_PATH/privkey.pem" \
        -out "/tmp/$DOMAIN.p12" \
        -name tomcat \
        -password pass:changeit

    keytool -importkeystore \
        -deststorepass changeit \
        -destkeypass changeit \
        -destkeystore "$KEYSTORE_PATH" \
        -srckeystore "/tmp/$DOMAIN.p12" \
        -srcstoretype PKCS12 \
        -srcstorepass changeit \
        -alias tomcat

    # 重启Tomcat
    systemctl restart tomcat
    echo "证书已更新并重启Tomcat"
else
    echo "证书未更新"
fi

2. 基础SSL连接器配置

2.1 简单SSL配置

<!-- server.xml - 基础SSL连接器 -->
<Connector port="8443" 
           protocol="org.apache.coyote.http11.Http11NioProtocol"
           maxThreads="150" 
           SSLEnabled="true"
           scheme="https" 
           secure="true"
           clientAuth="false" 
           sslProtocol="TLS">

  <SSLHostConfig>
    <Certificate certificateKeystoreFile="conf/keystore.jks"
                 certificateKeystorePassword="changeit"
                 type="RSA" />
  </SSLHostConfig>
</Connector>

2.2 高安全性SSL配置

<Connector port="8443" 
           protocol="org.apache.coyote.http11.Http11NioProtocol"
           maxThreads="200"
           SSLEnabled="true"
           scheme="https" 
           secure="true">

  <SSLHostConfig protocols="TLSv1.2,TLSv1.3"
                 ciphers="ECDHE-ECDSA-AES256-GCM-SHA384,ECDHE-RSA-AES256-GCM-SHA384,ECDHE-ECDSA-CHACHA20-POLY1305,ECDHE-RSA-CHACHA20-POLY1305,ECDHE-ECDSA-AES128-GCM-SHA256,ECDHE-RSA-AES128-GCM-SHA256"
                 honorCipherOrder="true"
                 disableCompression="true"
                 disableSessionTickets="false"
                 sessionTimeout="86400"
                 revocationEnabled="false">

    <Certificate certificateKeystoreFile="conf/server.jks"
                 certificateKeystorePassword="serverpass"
                 certificateKeyAlias="server"
                 type="RSA" />
  </SSLHostConfig>
</Connector>

3. 多域名SSL配置

3.1 SNI支持配置

<!-- 多域名SSL配置 -->
<Connector port="8443" 
           protocol="org.apache.coyote.http11.Http11NioProtocol"
           SSLEnabled="true">

  <!-- 主域名配置 -->
  <SSLHostConfig hostName="www.example.com"
                 protocols="TLSv1.2,TLSv1.3">
    <Certificate certificateKeystoreFile="conf/www-example-com.jks"
                 certificateKeystorePassword="www123"
                 type="RSA" />
  </SSLHostConfig>

  <!-- API域名配置 -->
  <SSLHostConfig hostName="api.example.com"
                 protocols="TLSv1.2,TLSv1.3">
    <Certificate certificateKeystoreFile="conf/api-example-com.jks"
                 certificateKeystorePassword="api123"
                 type="RSA" />
  </SSLHostConfig>

  <!-- 通配符证书配置 -->
  <SSLHostConfig hostName="*.example.com"
                 protocols="TLSv1.2,TLSv1.3">
    <Certificate certificateKeystoreFile="conf/wildcard-example-com.jks"
                 certificateKeystorePassword="wild123"
                 type="RSA" />
  </SSLHostConfig>

  <!-- 默认配置 -->
  <SSLHostConfig protocols="TLSv1.2,TLSv1.3">
    <Certificate certificateKeystoreFile="conf/default.jks"
                 certificateKeystorePassword="default123"
                 type="RSA" />
  </SSLHostConfig>
</Connector>

3.2 混合证书配置

<!-- RSA + ECC混合证书配置 -->
<Connector port="8443" 
           protocol="org.apache.coyote.http11.Http11NioProtocol"
           SSLEnabled="true">

  <SSLHostConfig hostName="secure.example.com">

    <!-- RSA证书 -->
    <Certificate certificateKeystoreFile="conf/rsa-keystore.jks"
                 certificateKeystorePassword="rsa123"
                 certificateKeyAlias="rsa-key"
                 type="RSA" />

    <!-- ECC证书 -->
    <Certificate certificateKeystoreFile="conf/ecc-keystore.jks"
                 certificateKeystorePassword="ecc123"
                 certificateKeyAlias="ecc-key"
                 type="EC" />
  </SSLHostConfig>
</Connector>

4. 客户端证书认证

4.1 双向SSL配置

<!-- 双向SSL认证配置 -->
<Connector port="8443" 
           protocol="org.apache.coyote.http11.Http11NioProtocol"
           SSLEnabled="true"
           scheme="https" 
           secure="true">

  <SSLHostConfig certificateVerification="required"
                 truststoreFile="conf/truststore.jks"
                 truststorePassword="trustpass"
                 truststoreType="JKS"
                 caCertificateFile="conf/ca-cert.pem"
                 revocationEnabled="true"
                 crlFile="conf/ca-crl.pem">

    <Certificate certificateKeystoreFile="conf/server-keystore.jks"
                 certificateKeystorePassword="serverpass"
                 type="RSA" />
  </SSLHostConfig>
</Connector>

4.2 可选客户端认证

<!-- 可选客户端证书认证 -->
<Connector port="8443" 
           protocol="org.apache.coyote.http11.Http11NioProtocol"
           SSLEnabled="true">

  <SSLHostConfig certificateVerification="optional"
                 truststoreFile="conf/client-truststore.jks"
                 truststorePassword="clientpass">

    <Certificate certificateKeystoreFile="conf/server.jks"
                 certificateKeystorePassword="serverpass"
                 type="RSA" />
  </SSLHostConfig>
</Connector>

4.3 客户端证书验证Valve

// ClientCertificateValve.java
package com.example.ssl;

import org.apache.catalina.valves.ValveBase;
import org.apache.catalina.connector.Request;
import org.apache.catalina.connector.Response;

import javax.servlet.ServletException;
import java.io.IOException;
import java.security.cert.X509Certificate;

public class ClientCertificateValve extends ValveBase {

    @Override
    public void invoke(Request request, Response response) 
            throws IOException, ServletException {

        // 获取客户端证书
        X509Certificate[] certs = (X509Certificate[]) 
            request.getAttribute("javax.servlet.request.X509Certificate");

        if (certs != null && certs.length > 0) {
            X509Certificate clientCert = certs[0];

            // 验证证书
            if (isValidClientCertificate(clientCert)) {
                // 提取用户信息
                String username = extractUsername(clientCert);
                request.setAttribute("ssl.client.username", username);

                // 继续处理请求
                getNext().invoke(request, response);
            } else {
                response.sendError(403, "Invalid client certificate");
            }
        } else {
            response.sendError(401, "Client certificate required");
        }
    }

    private boolean isValidClientCertificate(X509Certificate cert) {
        try {
            // 检查证书有效期
            cert.checkValidity();

            // 检查证书颁发者
            String issuer = cert.getIssuerDN().getName();
            if (!issuer.contains("CN=Example CA")) {
                return false;
            }

            // 检查证书用途
            boolean[] keyUsage = cert.getKeyUsage();
            if (keyUsage == null || !keyUsage[0]) { // Digital Signature
                return false;
            }

            return true;
        } catch (Exception e) {
            return false;
        }
    }

    private String extractUsername(X509Certificate cert) {
        String subject = cert.getSubjectDN().getName();
        // 提取CN值作为用户名
        String[] parts = subject.split(",");
        for (String part : parts) {
            if (part.trim().startsWith("CN=")) {
                return part.trim().substring(3);
            }
        }
        return "unknown";
    }
}

5. SSL性能优化

5.1 会话缓存优化

<!-- SSL会话缓存优化 -->
<Connector port="8443" 
           protocol="org.apache.coyote.http11.Http11NioProtocol"
           SSLEnabled="true">

  <SSLHostConfig sessionCacheSize="10000"
                 sessionTimeout="86400"
                 disableSessionTickets="false">

    <Certificate certificateKeystoreFile="conf/keystore.jks"
                 certificateKeystorePassword="changeit"
                 type="RSA" />
  </SSLHostConfig>
</Connector>

5.2 OCSP Stapling配置

<!-- OCSP Stapling配置 -->
<Connector port="8443" 
           protocol="org.apache.coyote.http11.Http11NioProtocol"
           SSLEnabled="true">

  <SSLHostConfig certificateVerification="none"
                 revocationEnabled="true"
                 caCertificateFile="conf/ca-bundle.crt">

    <Certificate certificateKeystoreFile="conf/server.jks"
                 certificateKeystorePassword="serverpass"
                 type="RSA" />
  </SSLHostConfig>
</Connector>

5.3 SSL性能监控

// SSLPerformanceMonitor.java
package com.example.ssl;

import javax.management.MBeanServer;
import javax.management.ObjectName;
import java.lang.management.ManagementFactory;

public class SSLPerformanceMonitor {

    private MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();

    public void monitorSSLConnector() throws Exception {
        ObjectName connectorName = new ObjectName(
            "Catalina:type=Connector,port=8443"
        );

        // SSL连接统计
        Long bytesReceived = (Long) mbs.getAttribute(connectorName, "bytesReceived");
        Long bytesSent = (Long) mbs.getAttribute(connectorName, "bytesSent");
        Integer requestCount = (Integer) mbs.getAttribute(connectorName, "requestCount");
        Long processingTime = (Long) mbs.getAttribute(connectorName, "processingTime");

        System.out.println("SSL连接器性能统计:");
        System.out.println("接收字节数: " + formatBytes(bytesReceived));
        System.out.println("发送字节数: " + formatBytes(bytesSent));
        System.out.println("请求总数: " + requestCount);
        System.out.println("平均处理时间: " + (processingTime / Math.max(requestCount, 1)) + "ms");

        // SSL会话信息
        monitorSSLSessions();
    }

    private void monitorSSLSessions() {
        // 监控SSL会话缓存
        try {
            ObjectName sslName = new ObjectName(
                "Catalina:type=SSLHostConfig,host=*"
            );

            // 获取SSL相关统计(如果可用)
            System.out.println("SSL会话监控信息:");
            System.out.println("会话缓存状态: 正常");

        } catch (Exception e) {
            System.out.println("SSL会话监控不可用: " + e.getMessage());
        }
    }

    private String formatBytes(long bytes) {
        if (bytes < 1024) return bytes + " B";
        if (bytes < 1024 * 1024) return String.format("%.2f KB", bytes / 1024.0);
        if (bytes < 1024 * 1024 * 1024) return String.format("%.2f MB", bytes / (1024.0 * 1024));
        return String.format("%.2f GB", bytes / (1024.0 * 1024 * 1024));
    }
}

6. SSL故障排除

6.1 SSL诊断脚本

#!/bin/bash
# ssl-diagnostics.sh

HOSTNAME="localhost"
PORT="8443"
KEYSTORE="/opt/tomcat9/conf/keystore.jks"
KEYSTORE_PASS="changeit"

echo "=== SSL/TLS诊断工具 ==="

# 1. 检查端口连通性
test_ssl_connection() {
    echo "1. 测试SSL连接..."
    if timeout 10 openssl s_client -connect $HOSTNAME:$PORT -servername $HOSTNAME < /dev/null 2>/dev/null; then
        echo "   ✓ SSL连接成功"
    else
        echo "   ✗ SSL连接失败"
    fi
}

# 2. 检查证书有效性
check_certificate() {
    echo "2. 检查证书..."

    # 检查KeyStore中的证书
    if [ -f "$KEYSTORE" ]; then
        echo "   检查KeyStore证书:"
        keytool -list -keystore "$KEYSTORE" -storepass "$KEYSTORE_PASS" -v | \
        grep -E "(Alias name|Valid from|until|Subject|Issuer)"
    fi

    # 检查服务器证书
    echo "   检查服务器证书:"
    echo | openssl s_client -connect $HOSTNAME:$PORT -servername $HOSTNAME 2>/dev/null | \
    openssl x509 -noout -dates -subject -issuer 2>/dev/null
}

# 3. 检查SSL/TLS协议支持
check_protocols() {
    echo "3. 检查支持的协议..."

    protocols=("ssl3" "tls1" "tls1_1" "tls1_2" "tls1_3")

    for protocol in "${protocols[@]}"; do
        if echo | openssl s_client -connect $HOSTNAME:$PORT -$protocol 2>/dev/null | grep -q "Verify return code: 0"; then
            echo "   ✓ $protocol 支持"
        else
            echo "   ✗ $protocol 不支持"
        fi
    done
}

# 4. 检查密码套件
check_ciphers() {
    echo "4. 检查密码套件..."
    echo | openssl s_client -connect $HOSTNAME:$PORT -cipher 'ALL' 2>/dev/null | \
    grep "Cipher is" | head -5
}

# 5. SSL配置评分
ssl_security_check() {
    echo "5. SSL安全评分..."

    score=0
    total=5

    # 检查TLS 1.2支持
    if echo | openssl s_client -connect $HOSTNAME:$PORT -tls1_2 2>/dev/null | grep -q "Protocol.*TLSv1.2"; then
        echo "   ✓ 支持TLS 1.2"
        score=$((score + 1))
    else
        echo "   ✗ 不支持TLS 1.2"
    fi

    # 检查TLS 1.3支持
    if echo | openssl s_client -connect $HOSTNAME:$PORT -tls1_3 2>/dev/null | grep -q "Protocol.*TLSv1.3"; then
        echo "   ✓ 支持TLS 1.3"
        score=$((score + 1))
    else
        echo "   ✗ 不支持TLS 1.3"
    fi

    # 检查弱密码套件
    if ! echo | openssl s_client -connect $HOSTNAME:$PORT -cipher 'LOW:EXP:NULL:aNULL' 2>/dev/null | grep -q "Cipher is"; then
        echo "   ✓ 无弱密码套件"
        score=$((score + 1))
    else
        echo "   ✗ 存在弱密码套件"
    fi

    # 检查证书强度
    key_size=$(echo | openssl s_client -connect $HOSTNAME:$PORT 2>/dev/null | openssl x509 -noout -text | grep "Public-Key" | grep -o "[0-9]*")
    if [ "$key_size" -ge 2048 ]; then
        echo "   ✓ 证书密钥强度足够 (${key_size}位)"
        score=$((score + 1))
    else
        echo "   ✗ 证书密钥强度不足 (${key_size}位)"
    fi

    # 检查HSTS
    if curl -s -I https://$HOSTNAME:$PORT/ 2>/dev/null | grep -q "Strict-Transport-Security"; then
        echo "   ✓ 启用HSTS"
        score=$((score + 1))
    else
        echo "   ✗ 未启用HSTS"
    fi

    echo "   SSL安全评分: $score/$total ($(( score * 100 / total ))%)"
}

# 执行所有检查
test_ssl_connection
echo
check_certificate
echo
check_protocols
echo
check_ciphers
echo
ssl_security_check

6.2 常见SSL问题解决

#!/bin/bash
# ssl-troubleshoot.sh

fix_common_ssl_issues() {
    echo "=== SSL常见问题修复 ==="

    # 1. 检查Java版本和SSL支持
    echo "1. 检查Java SSL支持..."
    java -Dcom.sun.net.ssl.checkRevocation=false \
         -Dhttps.protocols=TLSv1.2,TLSv1.3 \
         -version

    # 2. 更新Java加密策略
    echo "2. 检查加密策略..."
    if [ -f "$JAVA_HOME/jre/lib/security/local_policy.jar" ]; then
        echo "   发现限制性加密策略,建议更新为无限制策略"
    fi

    # 3. 检查系统时间
    echo "3. 检查系统时间..."
    date
    echo "   确保系统时间准确,避免证书验证失败"

    # 4. 清理SSL缓存
    echo "4. 清理SSL会话缓存..."
    find /tmp -name "hsperfdata_*" -exec rm -rf {} \; 2>/dev/null

    # 5. 验证证书链
    echo "5. 验证证书链..."
    openssl verify -CAfile /etc/ssl/certs/ca-certificates.crt \
                   <(openssl s_client -connect localhost:8443 -showcerts 2>/dev/null | \
                     openssl x509) 2>/dev/null
}

# 执行修复
fix_common_ssl_issues

7. SSL配置最佳实践

7.1 生产环境SSL配置模板

<!-- 生产环境SSL配置模板 -->
<Connector port="443" 
           protocol="org.apache.coyote.http11.Http11NioProtocol"
           maxThreads="300"
           SSLEnabled="true"
           scheme="https" 
           secure="true"
           compression="on"
           compressionMinSize="2048"
           compressibleMimeType="text/html,text/xml,text/css,application/javascript,application/json">

  <!-- 高安全性配置 -->
  <SSLHostConfig protocols="TLSv1.2,TLSv1.3"
                 ciphers="ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256"
                 honorCipherOrder="true"
                 disableCompression="true"
                 sessionCacheSize="20000"
                 sessionTimeout="86400"
                 certificateVerification="none">

    <Certificate certificateKeystoreFile="conf/production.jks"
                 certificateKeystorePassword="prodpass123"
                 certificateKeyAlias="production"
                 type="RSA" />
  </SSLHostConfig>

  <!-- 安全头设置 -->
  <Valve className="org.apache.catalina.valves.HttpHeaderSecurityFilter"
         hstsEnabled="true"
         hstsMaxAgeSeconds="31536000"
         hstsIncludeSubdomains="true"
         hstsPreload="true" />
</Connector>

7.2 SSL监控脚本

#!/bin/bash
# ssl-monitor.sh

DOMAINS=("example.com" "api.example.com" "admin.example.com")
ALERT_DAYS=30

for domain in "${DOMAINS[@]}"; do
    echo "检查域名: $domain"

    # 获取证书到期时间
    expiry_date=$(echo | openssl s_client -connect $domain:443 -servername $domain 2>/dev/null | \
                 openssl x509 -noout -enddate 2>/dev/null | cut -d= -f2)

    if [ ! -z "$expiry_date" ]; then
        expiry_epoch=$(date -d "$expiry_date" +%s)
        current_epoch=$(date +%s)
        days_left=$(( (expiry_epoch - current_epoch) / 86400 ))

        echo "  证书到期时间: $expiry_date"
        echo "  剩余天数: $days_left"

        if [ $days_left -lt $ALERT_DAYS ]; then
            echo "  ⚠️  警告: 证书即将到期!"
        fi
    else
        echo "  ❌ 无法获取证书信息"
    fi
    echo
done

小结

通过本文学习,你应该掌握:

  1. SSL/TLS基础知识和证书管理
  2. Tomcat SSL连接器的详细配置
  3. 多域名和SNI支持配置
  4. 客户端证书认证配置
  5. SSL性能优化技巧
  6. SSL故障排除方法
  7. SSL安全配置最佳实践
  8. SSL监控和维护技术

下一篇文章将介绍Tomcat性能优化技术。

powered by Gitbook© 2025 编外计划 | 最后修改: 2025-08-29 15:40:15

results matching ""

    No results matching ""