支持多种 TLS 协议的 HttpClient

Posted

技术标签:

【中文标题】支持多种 TLS 协议的 HttpClient【英文标题】:HttpClient supporting multiple TLS protocols 【发布时间】:2013-08-14 16:44:50 【问题描述】:

我们正在编写一个应用程序,该应用程序必须使用 HTTPS 与一些服务器进行通信。 它需要与 AWS(使用 AWS 库)以及我们使用 TLS 1.2 的一些内部服务进行通信。

我首先将 HttpClient 更改为使用 TLS 1.2 SSLContext:

public static SchemeRegistry buildSchemeRegistry() throws Exception 
    final SSLContext sslContext = SSLContext.getInstance("TLSv1.2");
    sslContext.init(createKeyManager(), createTrustManager(), new SecureRandom());
    final SchemeRegistry schemeRegistry = new SchemeRegistry();
    schemeRegistry.register(new Scheme("https", 443, new SSLSocketFactory(sslContext)));
    return schemeRegistry;

并将此 SchemeRegistry 注入 DefaultHttpClient 对象(通过 spring),但这样做我从 AWS 收到错误,因此我假设(我可能错了)AWS 不支持 TLS 1.2(我没有收到此消息如果我只使用普通的 DefaultHttpClient):

AmazonServiceException: Status Code: 403, AWS Service: AmazonSimpleDB, AWS Request ID: 5d91d65f-7158-91b6-431d-56e1c76a844c, AWS Error Code: InvalidClientTokenId, AWS Error Message: The AWS Access Key Id you provided does not exist in our records.

如果我尝试在 spring 中定义两个 HttpClient,一个使用 TLS 1.2,一个是默认值,我会收到以下错误,我认为这意味着 Spring 不喜欢实例化和自动装配两个 HttpClient 对象:

SEVERE: Servlet /my-refsvc threw load() exception
java.lang.NullPointerException
at com.company.project.refsvc.base.HttpsClientFactory.<clinit>(BentoHttpsClientFactory.java:25)
...
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1031)
at 
...
org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:223)

我在 java 中使用 HTTPS 的次数不多,请问各位好心人能给我一些建议吗? 1) 我如何让 Spring 允许两个 HttpClient 对象,一个连接到 AWS 东西 bean,另一个连接到其他 bean 以访问 TLS1.2 服务 2) 或者是否可以更改一个 HttpClient 对象以尝试 TLS1.2(通过 SSLContext 或 SchemeRegistry 或其他方式),如果失败则尝试 TLS1.1 或 1.0? 3)如果两者都可能,那么“更好”的方法是什么?

【问题讨论】:

在使用SSLContext.getInstance("TLSv1.1")SSLContext.getInstance("TLS") 时是否会出现同样的错误? 是的,我在那里遇到了同样的错误。我想知道是不是我的客户端证书搞砸了,而不是 TLS 版本。 尝试使用默认的SSLContext以防万一(SSLContext sslContext = SSLContext.getDefault(),已经初始化)。否则,请尝试减少自定义:sslContext.init(createKeyManager(), null, null) 应使用 TM 和 SecureRandom 的默认值。密钥管理器没有默认值,因此您的密钥管理器代码中可能仍然存在问题(仅在服务器请求客户端证书时有用)。 我创建了一篇关于如何为 android 4.1+ 启用它的小博文blog.dev-area.net/2015/08/13/… 【参考方案1】:

TLS 有一个内置机制来协商要使用的协议版本。来自RFC 5246 (Appendix E):

TLS 版本 1.0、1.1 和 1.2 与 SSL 3.0 非常相似,并且 使用兼容的 ClientHello 消息;因此,支持所有这些 相对容易。同样,服务器可以轻松处理客户端 尝试使用未来版本的 TLS,只要 ClientHello 格式保持兼容,客户端支持最高 服务器中可用的协议版本。

希望与此类旧服务器进行协商的 TLS 1.2 客户端 将发送一个普通的 TLS 1.2 ClientHello,包含 3, 3 (TLS 1.2) 在 ClientHello.client_version 中。如果服务器不支持这个版本,它会响应一个 ServerHello 包含一个 旧版本号。如果客户同意使用此版本, 谈判将酌情进行 协议。

此外,更改SSLContext.getInstance(...) 中的版本号只会更改默认启用的协议。使用SSLSocket.setEnabledProtocols(...) 设置实际的协议版本(请参阅this question)。我不确定您正在使用的其他库,但它可能在某处设置了启用的协议。

有几种可能:

您在 createKeyManager() 中所做的操作与默认行为不同。如果服务使用的是客户端证书认证,那么错误的配置肯定会导致 403 错误。

(我猜不太可能,但如果没有看到你的createKeyManager()createTrustManager() 就很难说)。也许您使用的服务器与 TLS 1.2 和版本协商机制不兼容。 sun.security.ssl.SSLContextImpl中有这样的评论:

SSL/TLS 协议指定前向兼容性和版本 回滚攻击保护,然而,一些 SSL/TLS 服务器 供应商没有正确实施这些方面,一些当前 SSL/TLS 服务器可能拒绝与 TLS 1.1 或更高版本的客户端通信。

【讨论】:

我们正在使用客户端证书身份验证,但我不知道在他们不期望的情况下尝试使用客户端证书对 AWS 服务器进行身份验证是否会导致错误。 AWS 使用 HMAC 签名进行身份验证,但我真的不知道更多。 createKeyManager 和 createTrustManager 只是加载一个 .jks 文件并从中返回对象(不能在此评论中发布代码) @agentgonzo,如果您想发布更多代码,可以编辑您的问题。您的应用程序(默认情况下)是否尝试使用密钥库的其他设置(javax.net.ssl.* 属性)?如果您获得其他上下文(TLS 和 TLSv1.1)会发生什么?

以上是关于支持多种 TLS 协议的 HttpClient的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 TLS/SSL 确保 WebSocket 连接的安全

如何使用 TLS/SSL 确保 WebSocket 连接的安全

如何阻止 SSL 协议以支持 TLS?

又拍云 CDN 正式支持 TLS 1.3 加密协议,一键开启极速 HTTPS 体验

netty系列之:让TLS支持http2

jdk 啥版本开始支持 tls1.2协议