SSLHandshakeException:收到致命警报:在 tomcat 7 服务器上设置密码时握手失败

Posted

技术标签:

【中文标题】SSLHandshakeException:收到致命警报:在 tomcat 7 服务器上设置密码时握手失败【英文标题】:SSLHandshakeException: Received fatal alert: handshake_failure when setting ciphers on tomcat 7 server 【发布时间】:2013-03-10 18:16:39 【问题描述】:

我有一个 Tomcat7 网络服务器,我尝试通过将此连接器添加到 server.xml 文件来配置它以接受安全连接:

<Connector SSLEnabled="true"
           acceptCount="100"
           connectionTimeout="20000"
           executor="tomcatThreadPool"
           keyAlias="server"
           keystoreFile="c:\opt\engine\conf\tc.keystore"
           keystorePass="o39UI12z"
           maxKeepAliveRequests="15"
           port="8443"
           protocol="HTTP/1.1"
           redirectPort="8443"
           scheme="https"
           secure="true"
           sslProtocol="TLS"/>

我正在使用使用此命令生成的自签名证书:

%JAVA_HOME%/bin/keytool -genkeypair -keystore c:\opt\engine\conf\tc.keystore -storepass o39UI12z-keypass o39UI12z-dname "cn=Company, ou=Company, o=Com, c=US" -alias server -validity 36500

在客户端,我有一个使用RestTemplate 与服务器连接的spring 应用程序。在应用程序上下文启动时,我以这种方式初始化 restTemplate 实例:

final ClientHttpRequestFactory clientHttpRequestFactory =
        new MyCustomClientHttpRequestFactory(new NullHostNameVerifier(), serverInfo);
    restTemplate.setRequestFactory(clientHttpRequestFactory);

MyCustomClientHttpRequestFactory 类如下所示:

public class MyCustomClientHttpRequestFactory  extends SimpleClientHttpRequestFactory 

private static final Logger LOGGER = LoggerFactory
    .getLogger(MyCustomClientHttpRequestFactory.class);

private final HostnameVerifier hostNameVerifier;
private final ServerInfo serverInfo;

public MyCustomClientHttpRequestFactory (final HostnameVerifier hostNameVerifier,
    final ServerInfo serverInfo) 
    this.hostNameVerifier = hostNameVerifier;
    this.serverInfo = serverInfo;


@Override
protected void prepareConnection(final HttpURLConnection connection, final String httpMethod)
    throws IOException 
    if (connection instanceof HttpsURLConnection) 
        ((HttpsURLConnection) connection).setHostnameVerifier(hostNameVerifier);
        ((HttpsURLConnection) connection).setSSLSocketFactory(initSSLContext()
            .getSocketFactory());
    
    super.prepareConnection(connection, httpMethod);


private SSLContext initSSLContext() 
    try 
        System.setProperty("https.protocols", "TLSv1");

        // Set ssl trust manager. Verify against our server thumbprint
        final SSLContext ctx = SSLContext.getInstance("TLSv1");
        final SslThumbprintVerifier verifier = new SslThumbprintVerifier(serverInfo);
        final ThumbprintTrustManager thumbPrintTrustManager =
            new ThumbprintTrustManager(null, verifier);
        ctx.init(null, new TrustManager[]  thumbPrintTrustManager , null);
        return ctx;
     catch (final Exception ex) 
        LOGGER.error(
            "An exception was thrown while trying to initialize HTTP security manager.", ex);
        return null;
    

直到这里一切正常。当我在 SslThumbprintVerifier 类中设置一个断点时,代码到达了这一点,也到达了 NullHostNameVerifier 类。这已在生产中进行了测试,效果很好。

现在我想通过限制密码套件来扩展安全性,并将这个属性添加到我在开头介绍的连接器中:

ciphers="TLS_KRB5_WITH_RC4_128_SHA,SSL_RSA_WITH_RC4_128_SHA,SSL_DHE_RSA_WITH_AES_256_CBC_SHA,SSL_RSA_WITH_AES_256_CBC_SHA,SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA,SSL_RSA_WITH_3DES_EDE_CBC_SHA,SSL_DHE_RSA_WITH_AES_128_CBC_SHA,SSL_RSA_WITH_AES_128_CBC_SHA,TLS_RSA_WITH_RC4_128_SHA,TLS_RSA_WITH_3DES_EDE_CBC_SHA,TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA,TLS_RSA_WITH_AES_128_CBC_SHA,TLS_RSA_WITH_AES_256_CBC_SHA,TLS_DHE_RSA_WITH_AES_128_CBC_SHA,TLS_DHE_RSA_WITH_AES_256_CBC_SHA,TLS_KRB5_WITH_3DES_EDE_CBC_SHA"

现在当我运行客户端代码时,我得到了这个异常:

javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure

这发生在restTemplate.doExecute() 方法中。代码没有像添加密码之前那样到达指纹验证器类或主机名验证器类。

在调试中,我检查了ctx.getSocketFactory().getSupportedCipherSuites(),它显示:

[SSL_RSA_WITH_RC4_128_MD5, SSL_RSA_WITH_RC4_128_SHA, TLS_RSA_WITH_AES_128_CBC_SHA, TLS_RSA_WITH_AES_256_CBC_SHA, TLS_DHE_RSA_WITH_AES_128_CBC_SHA, TLS_DHE_RSA_WITH_AES_256_CBC_SHA, TLS_DHE_DSS_WITH_AES_128_CBC_SHA, TLS_DHE_DSS_WITH_AES_256_CBC_SHA, SSL_RSA_WITH_3DES_EDE_CBC_SHA, SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA, SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA, SSL_RSA_WITH_DES_CBC_SHA, SSL_DHE_RSA_WITH_DES_CBC_SHA, SSL_DHE_DSS_WITH_DES_CBC_SHA, SSL_RSA_EXPORT_WITH_RC4_40_MD5, SSL_RSA_EXPORT_WITH_DES40_CBC_SHA, SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA, SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA, TLS_EMPTY_RENEGOTIATION_INFO_SCSV, SSL_RSA_WITH_NULL_MD5, SSL_RSA_WITH_NULL_SHA, SSL_DH_anon_WITH_RC4_128_MD5, TLS_DH_anon_WITH_AES_128_CBC_SHA, TLS_DH_anon_WITH_AES_256_CBC_SHA, SSL_DH_anon_WITH_3DES_EDE_CBC_SHA, SSL_DH_anon_WITH_DES_CBC_SHA, SSL_DH_anon_EXPORT_WITH_RC4_40_MD5, SSL_DH_anon_EXPORT_WITH_DES40_CBC_SHA, TLS_KRB5_WITH_RC4_128_SHA, TLS_KRB5_WITH_RC4_128_MD5, TLS_KRB5_WITH_3DES_EDE_CBC_SHA, TLS_KRB5_WITH_3DES_EDE_CBC_MD5, TLS_KRB5_WITH_DES_CBC_SHA, TLS_KRB5_WITH_DES_CBC_MD5, TLS_KRB5_EXPORT_WITH_RC4_40_SHA, TLS_KRB5_EXPORT_WITH_RC4_40_MD5, TLS_KRB5_EXPORT_WITH_DES_CBC_40_SHA, TLS_KRB5_EXPORT_WITH_DES_CBC_40_MD5]

还检查了ctx.getSocketFactory().getDefaultCipherSuites():

[SSL_RSA_WITH_RC4_128_MD5, SSL_RSA_WITH_RC4_128_SHA, TLS_RSA_WITH_AES_128_CBC_SHA, TLS_RSA_WITH_AES_256_CBC_SHA, TLS_DHE_RSA_WITH_AES_128_CBC_SHA, TLS_DHE_RSA_WITH_AES_256_CBC_SHA, TLS_DHE_DSS_WITH_AES_128_CBC_SHA, TLS_DHE_DSS_WITH_AES_256_CBC_SHA, SSL_RSA_WITH_3DES_EDE_CBC_SHA, SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA, SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA, SSL_RSA_WITH_DES_CBC_SHA, SSL_DHE_RSA_WITH_DES_CBC_SHA, SSL_DHE_DSS_WITH_DES_CBC_SHA, SSL_RSA_EXPORT_WITH_RC4_40_MD5, SSL_RSA_EXPORT_WITH_DES40_CBC_SHA, SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA, SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA, TLS_EMPTY_RENEGOTIATION_INFO_SCSV]

最后是ctx.getSupportedSSLParameters().getCipherSuites():

[SSL_RSA_WITH_RC4_128_MD5, SSL_RSA_WITH_RC4_128_SHA, TLS_RSA_WITH_AES_128_CBC_SHA, TLS_RSA_WITH_AES_256_CBC_SHA, TLS_DHE_RSA_WITH_AES_128_CBC_SHA, TLS_DHE_RSA_WITH_AES_256_CBC_SHA, TLS_DHE_DSS_WITH_AES_128_CBC_SHA, TLS_DHE_DSS_WITH_AES_256_CBC_SHA, SSL_RSA_WITH_3DES_EDE_CBC_SHA, SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA, SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA, SSL_RSA_WITH_DES_CBC_SHA, SSL_DHE_RSA_WITH_DES_CBC_SHA, SSL_DHE_DSS_WITH_DES_CBC_SHA, SSL_RSA_EXPORT_WITH_RC4_40_MD5, SSL_RSA_EXPORT_WITH_DES40_CBC_SHA, SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA, SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA, TLS_EMPTY_RENEGOTIATION_INFO_SCSV, SSL_RSA_WITH_NULL_MD5, SSL_RSA_WITH_NULL_SHA, SSL_DH_anon_WITH_RC4_128_MD5, TLS_DH_anon_WITH_AES_128_CBC_SHA, TLS_DH_anon_WITH_AES_256_CBC_SHA, SSL_DH_anon_WITH_3DES_EDE_CBC_SHA, SSL_DH_anon_WITH_DES_CBC_SHA, SSL_DH_anon_EXPORT_WITH_RC4_40_MD5, SSL_DH_anon_EXPORT_WITH_DES40_CBC_SHA, TLS_KRB5_WITH_RC4_128_SHA, TLS_KRB5_WITH_RC4_128_MD5, TLS_KRB5_WITH_3DES_EDE_CBC_SHA, TLS_KRB5_WITH_3DES_EDE_CBC_MD5, TLS_KRB5_WITH_DES_CBC_SHA, TLS_KRB5_WITH_DES_CBC_MD5, TLS_KRB5_EXPORT_WITH_RC4_40_SHA, TLS_KRB5_EXPORT_WITH_RC4_40_MD5, TLS_KRB5_EXPORT_WITH_DES_CBC_40_SHA, TLS_KRB5_EXPORT_WITH_DES_CBC_40_MD5]

据我了解,如果客户端支持/启用的密码套件与服务器支持的密码套件之间存在交集,它应该可以工作(例如,我们与 SSL_RSA_WITH_RC4_128_SHA 有这样的交集)。然而我得到了这个错误。

在下一步中,我添加了这个 java 参数:

-Djavax.net.debug=ssl,handshake,failure

日志只显示客户端问候,没有服务器问候响应:

[2013-03-20 15:29:51.315] [INFO ] data-service-pool-37 System.out trigger seeding of SecureRandom [2013-03-20 15:29:51.315] [INFO ] data-service-pool-37 System.out done seeding SecureRandom [2013-03-20 15:30:38.894] [INFO ] data-service-pool-37 System.out Allow unsafe renegotiation: false [2013-03-20 15:30:38.894] [INFO ] data-service-pool-37 System.out Allow legacy hello messages: true [2013-03-20 15:30:38.894] [INFO ] data-service-pool-37 System.out Is initial handshake: true [2013-03-20 15:30:38.894] [INFO ] data-service-pool-37 System.out Is secure renegotiation: false [2013-03-20 15:30:38.894] [INFO ] data-service-pool-37 System.out %% No cached client session [2013-03-20 15:30:38.894] [INFO ] data-service-pool-37 System.out *** ClientHello, TLSv1 [2013-03-20 15:30:38.941] [INFO ] data-service-pool-37 System.out RandomCookie: GMT: 1363720446 bytes = 99, 249, 173, 214, 110, 82, 58, 52, 189, 92, 74, 169, 133, 128, 250, 109, 160, 64, 112, 253, 50, 160, 255, 196, 85, 93, 33, 172 [2013-03-20 15:30:38.941] [INFO ] data-service-pool-37 System.out Session ID: [2013-03-20 15:30:38.941] [INFO ] data-service-pool-37 System.out Cipher Suites: [SSL_RSA_WITH_RC4_128_MD5, SSL_RSA_WITH_RC4_128_SHA, TLS_RSA_WITH_AES_128_CBC_SHA, TLS_RSA_WITH_AES_256_CBC_SHA, TLS_DHE_RSA_WITH_AES_128_CBC_SHA, TLS_DHE_RSA_WITH_AES_256_CBC_SHA, TLS_DHE_DSS_WITH_AES_128_CBC_SHA, TLS_DHE_DSS_WITH_AES_256_CBC_SHA, SSL_RSA_WITH_3DES_EDE_CBC_SHA, SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA, SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA, SSL_RSA_WITH_DES_CBC_SHA, SSL_DHE_RSA_WITH_DES_CBC_SHA, SSL_DHE_DSS_WITH_DES_CBC_SHA, SSL_RSA_EXPORT_WITH_RC4_40_MD5, SSL_RSA_EXPORT_WITH_DES40_CBC_SHA, SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA, SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA, TLS_EMPTY_RENEGOTIATION_INFO_SCSV] [2013-03-20 15:30:38.956] [INFO ] data-service-pool-37 System.out Compression Methods: 0 [2013-03-20 15:30:38.956] [INFO ] data-service-pool-37 System.out *** [2013-03-20 15:30:38.956] [INFO ] data-service-pool-37 System.out data-service-pool-37, WRITE: TLSv1 Handshake, length = 81 [2013-03-20 15:30:38.956] [INFO ] data-service-pool-37 System.out data-service-pool-37, READ: TLSv1 Alert, length = 2 [2013-03-20 15:30:38.956] [INFO ] data-service-pool-37 System.out data-service-pool-37, RECV TLSv1 ALERT: fatal, handshake_failure [2013-03-20 15:30:38.972] [INFO ] data-service-pool-37 System.out data-service-pool-37, called closeSocket() [2013-03-20 15:30:38.972] [INFO ] data-service-pool-37 System.out data-service-pool-37, handling exception: javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure

当从 server.xml 中删除密码时,它会再次起作用。 我还要提到我正在同一台机器上测试服务器和客户端。我尝试将客户端请求中的服务器 ip 设置为“localhost”和实际机器 ip,但它没有在这两种情况下都行不通。此外,我让这台服务器在另一台 linux 机器上运行(密钥库当然是在具有 linux 路径的 linux 机器上生成的)并且仍然 - 它在没有密码的情况下工作并且停止使用密码。

【问题讨论】:

【参考方案1】:

好吧,我解决了这个问题。看来,通过使用 keytool 创建自签名证书,不提供 -keyalg 参数会使密钥对算法默认为 DSA。我的密码套件中没有一个包含 DSA 算法。在这种情况下,尽管客户端和服务器在他们的密码套件之间有交集,但都不适合密钥算法。

生成keystore时添加-keyalg RSA,问题解决。

【讨论】:

以上是关于SSLHandshakeException:收到致命警报:在 tomcat 7 服务器上设置密码时握手失败的主要内容,如果未能解决你的问题,请参考以下文章

当程序在 IntelliJ 中运行时,为啥我会收到 SSLHandshakeException 作为 JAR?

Apple Pay 证书更新 | SSLHandshakeException:收到致命警报:handshake_failure

ChangeCipherSpec - javax.net.ssl.SSLHandshakeException:收到致命警报:handshake_failure

无法下载站点地图:SSLHandshakeException:收到致命警报:handshake_failure

javax.net.ssl.SSLHandshakeException:收到致命警报:handshake_failure

获取 javax.net.ssl.SSLHandshakeException:收到致命警报:handshake_failure 错误