Java HttpClient 支持多种 TLS 协议
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/18237569/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
HttpClient supporting multiple TLS protocols
提问by agentgonzo
We're writing an app that must communicate with a few servers using HTTPS. It needs to communicate with AWS (using the AWS libraries) and also with some of our internal services that use TLS 1.2.
我们正在编写一个应用程序,它必须使用 HTTPS 与几个服务器进行通信。它需要与 AWS(使用 AWS 库)以及我们使用 TLS 1.2 的一些内部服务进行通信。
I started off by changing my HttpClient to use a TLS 1.2 SSLContext:
我首先将 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;
}
and injecting this SchemeRegistry into the DefaultHttpClient object (via spring), but doing that I get errors from AWS and so I assume (I may be wrong) that AWS doesn't support TLS 1.2 (I don't get this message if I just use the normal DefaultHttpClient):
并将这个 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.
If I try to have two HttpClients defined in spring, one that uses TLS 1.2 and one that is the default, I get the following error, which I assume means that Spring doesn't like instantiating and autowiring two HttpClient objects:
如果我尝试在 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)
I haven't used HTTPS much in java so could you kind people give me some advice please? 1) How would I get Spring to allow two HttpClient objects and for one to be wired to the AWS stuff beans and the other to be wired to the other beans for accessing the TLS1.2 services 2) Or is it possible to change the one HttpClient object to be able to try TLS1.2 (via SSLContext, or the SchemeRegistry or something) and if that fails then try TLS1.1 or 1.0? 3) If both are possible, what would be the 'better' way of doing it?
我在 Java 中使用 HTTPS 的次数不多,所以请各位好心人给我一些建议吗?1) 我如何让 Spring 允许两个 HttpClient 对象,一个连接到 AWS 东西 bean,另一个连接到其他 bean 以访问 TLS1.2 服务 2) 或者是否可以更改一个HttpClient 对象能够尝试 TLS1.2(通过 SSLContext,或 SchemeRegistry 或其他东西),如果失败,则尝试 TLS1.1 或 1.0?3)如果两者都有可能,那么“更好”的方法是什么?
回答by Bruno
TLS has an in-built mechanism to negotiate which version of the protocol is to be used. From RFC 5246 (Appendix E):
TLS 有一个内置机制来协商使用哪个版本的协议。来自RFC 5246(附录 E):
TLS versions 1.0, 1.1, and 1.2, and SSL 3.0 are very similar, and use compatible ClientHello messages; thus, supporting all of them is relatively easy. Similarly, servers can easily handle clients trying to use future versions of TLS as long as the ClientHello format remains compatible, and the client supports the highest protocol version available in the server.
A TLS 1.2 client who wishes to negotiate with such older servers will send a normal TLS 1.2 ClientHello, containing { 3, 3 } (TLS 1.2) in ClientHello.client_version. If the server does not support this version, it will respond with a ServerHello containing an older version number. If the client agrees to use this version, the negotiation will proceed as appropriate for the negotiated protocol.
TLS 1.0、1.1 和 1.2 版本与 SSL 3.0 非常相似,并且使用兼容的 ClientHello 消息;因此,支持所有这些相对容易。同样,只要 ClientHello 格式保持兼容,服务器就可以轻松处理尝试使用未来版本 TLS 的客户端,并且客户端支持服务器中可用的最高协议版本。
希望与此类旧服务器协商的 TLS 1.2 客户端将发送一个普通的 TLS 1.2 ClientHello,在 ClientHello.client_version 中包含 { 3, 3 } (TLS 1.2)。如果服务器不支持这个版本,它会响应一个包含旧版本号的 ServerHello。如果客户端同意使用此版本,则协商将根据协商的协议进行。
In addition, changing the version number in SSLContext.getInstance(...)
only changes which protocols are enabled by default. Setting the actual protocol versions is done with SSLSocket.setEnabledProtocols(...)
(see this question). I'm not sure about the rest of the libraries you're using, but it's possible that it sets the enabled protocols somewhere.
此外,更改版本号SSLContext.getInstance(...)
只会更改默认启用的协议。设置实际协议版本已完成SSLSocket.setEnabledProtocols(...)
(请参阅此问题)。我不确定您正在使用的其他库,但它可能在某处设置了启用的协议。
There are a few possibilities:
有几种可能:
What you're doing in your
createKeyManager()
differs from the default behaviour. If the service is using client-certificate authentication, bad configuration there would certainly lead to a 403 error.(Less likely, I guess, but hard to say without seeing your
createKeyManager()
andcreateTrustManager()
). Perhaps the server you're using isn't compatible with TLS 1.2 and the version negotiation mechanism. There is this comment insun.security.ssl.SSLContextImpl
:SSL/TLS protocols specify the forward compatibility and version roll-back attack protections, however, a number of SSL/TLS server vendors did not implement these aspects properly, and some current SSL/TLS servers may refuse to talk to a TLS 1.1 or later client.
您在做什么
createKeyManager()
与默认行为不同。如果服务使用客户端证书身份验证,那么错误的配置肯定会导致 403 错误。(我猜不太可能,但如果没有看到您的
createKeyManager()
和 就很难说createTrustManager()
)。也许您使用的服务器与 TLS 1.2 和版本协商机制不兼容。有这样的评论sun.security.ssl.SSLContextImpl
:SSL/TLS 协议指定了前向兼容性和版本回滚攻击保护,但是,许多 SSL/TLS 服务器供应商没有正确实现这些方面,并且当前的一些 SSL/TLS 服务器可能拒绝与 TLS 1.1 或更高版本通信客户。