支持多个TLS协议的HttpClient

5
我们正在编写一款应用程序,必须使用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 stuff beans,而另一个则将被连线到其他服务TLS1.2的beans。 2)或者是有可能更改一个HttpClient对象来尝试TLS1.2(通过SSLContext或SchemeRegistry等),如果失败,则尝试TLS1.1或1.0吗? 3)如果两者都可行,哪种方法会更好呢?


当使用 SSLContext.getInstance("TLSv1.1")SSLContext.getInstance("TLS") 时,您是否会收到相同的错误? - Bruno
是的,我在那里也遇到了相同的错误。我想知道是否是我的客户端证书搞砸了而不是TLS版本的问题。 - agentgonzo
尝试在必要时使用默认的SSLContextSSLContext sslContext = SSLContext.getDefault()已初始化)。否则,尝试减少自定义:sslContext.init(createKeyManager(), null, null)应该使用TM和SecureRandom的默认值。对于keymanager没有默认值,因此如果服务器请求客户端证书,则您的keymanager代码可能仍然存在问题(仅在这种情况下有用)。 - Bruno
我写了一篇关于如何在 Android 4.1+ 上启用 TLS 1.1 和 TLS 1.2 的小博客文章。http://blog.dev-area.net/2015/08/13/android-4-1-enable-tls-1-1-and-tls-1-2/ - fkrauthan
1个回答

5
TLS具有内置机制来协商使用哪个版本的协议。来自RFC 5246(附录E)
TLS版本1.0、1.1和1.2以及SSL 3.0非常相似,并且使用兼容的ClientHello消息;因此,支持它们都相对容易。同样,只要ClientHello格式保持兼容性,并且客户端支持服务器可用的最高协议版本,服务器就可以轻松处理尝试使用未来版本的TLS的客户端。
希望与这些旧服务器进行协商的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
@agentgonzo,如果您想发布更多的代码,可以编辑您的问题。您的应用程序(默认情况下)是否尝试使用密钥库的其他设置(javax.net.ssl.*属性)?如果您获得其他上下文(TLS和TLSv1.1),会发生什么? - Bruno

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接