是否可以为javax.ws.rs(Resteasy实现)重用Java可信证书? [英] Is it possible to reuse Java trusted certificates for javax.ws.rs (Resteasy implementation)?
问题描述
假设我们需要信任一个自签名SSL证书.例如,我们使用 https://self-signed.badssl.com/.
Suppose we need to trust a self-signed SSL certificate. As an example, let's use https://self-signed.badssl.com/.
由于签名者不是适当的"授权者,因此Java不信任它,因此拒绝连接到该服务器.但是,
Since the signer is not a "proper" authority, Java doesn't trust it and refuses to connect to that server. However, after
$ cd $JAVA_HOME/jre/lib/security
$ keytool -import -trustcacerts -alias ... -file ... -keystore cacerts
并重新启动应用程序,以下代码有效:
and restart of the application, the following code works:
new URL ("https://self-signed.badssl.com/").openConnection ().getResponseCode ()
并返回200(确定),而不会引发异常. IE.由于可以信任证书,因此现在可以使用一种基本的Java打开HTTPS连接的方式.
and returns 200 (OK), without throwing an exception. I.e. basic Java way of opening an HTTPS connection now works, since the certificate is now trusted.
但是,这对javax.ws.rs Client(至少在Resteasy中实现)没有任何可见的影响,并且我仍然遇到异常:
However, this doesn't have any visible effect on javax.ws.rs Client (as implemented in Resteasy, at least) and I still get an exception:
javax.ws.rs.ProcessingException: Unable to invoke request
at org.jboss.resteasy.client.jaxrs.engines.ApacheHttpClient4Engine.invoke(ApacheHttpClient4Engine.java:287)
at org.jboss.resteasy.client.jaxrs.internal.ClientInvocation.invoke(ClientInvocation.java:407)
at org.jboss.resteasy.client.jaxrs.internal.ClientInvocationBuilder.method(ClientInvocationBuilder.java:273)
[...]
Caused by: javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at sun.security.ssl.Alerts.getSSLException(Alerts.java:192)
at sun.security.ssl.SSLSocketImpl.fatal(SSLSocketImpl.java:1949)
at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:302)
at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:296)
at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1506)
at sun.security.ssl.ClientHandshaker.processMessage(ClientHandshaker.java:216)
at sun.security.ssl.Handshaker.processLoop(Handshaker.java:979)
at sun.security.ssl.Handshaker.process_record(Handshaker.java:914)
at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:1062)
at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1375)
at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1403)
at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1387)
at org.apache.http.conn.ssl.SSLSocketFactory.connectSocket(SSLSocketFactory.java:535)
at org.apache.http.conn.ssl.SSLSocketFactory.connectSocket(SSLSocketFactory.java:403)
at org.apache.http.impl.conn.DefaultClientConnectionOperator.openConnection(DefaultClientConnectionOperator.java:177)
at org.apache.http.impl.conn.ManagedClientConnectionImpl.open(ManagedClientConnectionImpl.java:304)
at org.apache.http.impl.client.DefaultRequestDirector.tryConnect(DefaultRequestDirector.java:611)
at org.apache.http.impl.client.DefaultRequestDirector.execute(DefaultRequestDirector.java:446)
at org.apache.http.impl.client.AbstractHttpClient.doExecute(AbstractHttpClient.java:863)
at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:82)
at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:57)
at org.jboss.resteasy.client.jaxrs.engines.ApacheHttpClient4Engine.invoke(ApacheHttpClient4Engine.java:283)
... 90 more
Caused by: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:387)
at sun.security.validator.PKIXValidator.engineValidate(PKIXValidator.java:292)
at sun.security.validator.Validator.validate(Validator.java:260)
at sun.security.ssl.X509TrustManagerImpl.validate(X509TrustManagerImpl.java:324)
at sun.security.ssl.X509TrustManagerImpl.checkTrusted(X509TrustManagerImpl.java:229)
at sun.security.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:124)
at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1488)
... 107 more
Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at sun.security.provider.certpath.SunCertPathBuilder.build(SunCertPathBuilder.java:146)
at sun.security.provider.certpath.SunCertPathBuilder.engineBuild(SunCertPathBuilder.java:131)
at java.security.cert.CertPathBuilder.build(CertPathBuilder.java:280)
at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:382)
... 113 more
Resteasy似乎没有考虑标准"密钥库.但是我想在中央(特定于机器)放置其他受信任的密钥,而不想通过URL.openConnection
或javax.ws.rs来烦扰应用程序使用它们的确切方式.
It seems as Resteasy doesn't take "standard" keystore into account. But I would rather like to have a central (machine-specific) place for additional trusted keys and not bother how exactly application uses them, with URL.openConnection
or javax.ws.rs.
问题
可以使javax.ws.rs Client
使用与正常" Java HTTPS连接机制相同的 密钥库吗?
Question
Is it possible to make javax.ws.rs Client
use the same keystore as "normal" Java HTTPS connection mechanism?
推荐答案
在创建Client
实例时设置SSL上下文
在 ClientBuilder
API中有一个方法,可让您设置 SSLContext
:
Setting the SSL context when creating the Client
instance
In the ClientBuilder
API there's a method that allows you to set the SSLContext
:
设置从使用此SSL上下文的客户端实例创建的Web目标创建到服务器端点的安全传输连接时将使用的SSL上下文.预计SSL上下文将初始化所有安全基础结构,包括密钥和信任管理器.
Set the SSL context that will be used when creating secured transport connections to server endpoints from web targets created by the client instance that is using this SSL context. The SSL context is expected to have all the security infrastructure initialized, including the key and trust managers.
设置SSL上下文实例将重置先前指定的任何密钥存储或信任存储值.
Setting a SSL context instance resets any key store or trust store values previously specified.
参数:
sslContext
-安全套接字协议实现,充当安全套接字工厂或SSL引擎的工厂.不得为null
.
sslContext
- secure socket protocol implementation which acts as a factory for secure socket factories or SSL engines. Must not benull
.返回:
更新的客户端构建器实例.
an updated client builder instance.
抛出:
NullPointerException
-如果sslContext
参数为null
.假设您已将证书添加到
cacerts
信任库,则可以使用默认的Client
实例.Assuming you have added the certificate to
cacerts
trust store, you could use the defaultSSLContext
when creating yourClient
instance.
Client client = ClientBuilder.newBuilder().sslContext(SSLContext.getDefault()).build();
应该就足够了.但是,由于某些原因,以上代码不适用于RESTEasy,但适用于Jersey.很有可能是RESTEasy错误.
It should be enough. However, for some reason, the above piece of code does not work with RESTEasy, but does work with Jersey. It's very likely it's a RESTEasy bug.
RESTEasy 文档指出以下内容:
The RESTEasy documentation states the following:
客户端和服务器之间的网络通信默认情况下由Apache HttpComponents项目中的
HttpClient
(4.x)在RESTEasy中进行处理. [...]Network communication between the client and server is handled in RESTEasy, by default, by
HttpClient
(4.x) from the Apache HttpComponents project. [...]RESTEasy和
HttpClient
做出合理的默认决定,以便可以使用客户端框架而无需引用HttpClient
,但是对于某些应用程序,可能有必要深入了解HttpClient
详细信息. [...]RESTEasy and
HttpClient
make reasonable default decisions so that it is possible to use the client framework without ever referencingHttpClient
, but for some applications it may be necessary to drill down into theHttpClient
details. [...]自定义 RESTEeasy使用的
HttpClient
,请执行以下操作:To customize the
HttpClient
used by RESTEeasy, do the following:
HttpClient httpClient = HttpClientBuilder.create() .setSslcontext(SSLContext.getDefault()) .build(); ApacheHttpClient4Engine engine = new ApacheHttpClient4Engine(httpClient); Client client = new ResteasyClientBuilder().httpEngine(engine).build();
然后您可以执行请求:
Response response = client.target("https://self-signed.badssl.com/").request().get(); System.out.println(response.getStatus());
SSL上下文是否有替代方案?
代替使用
SSLContext
在创建您的Client
,您可以加载KeyStore
.要加载cacerts
信任库,您可以执行以下操作:
Are there any alternatives to the SSL context?
Instead of using the
SSLContext
when creating yourClient
, you could load aKeyStore
. To loadcacerts
trust store, you can do the following:
String filename = System.getProperty("java.home") + "/lib/security/cacerts".replace('/', File.separatorChar); FileInputStream is = new FileInputStream(filename); KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType()); String password = "changeit"; keystore.load(is, password.toCharArray());
cacerts
'的默认密码为changeit
.然后创建您的
Client
实例使用以下方法之一:Then create your
Client
instance using one of the following approaches:
Client client = ClientBuilder.newBuilder().trustStore(keystore).build();
Client client = ClientBuilder.newBuilder().keyStore(keystore, password).build();
问题在于它不适用于RESTEasy,但适用于Jersey.
The issue is that it doesn't work with RESTEasy, but does work with Jersey.
上述解决方案已针对以下JAX-RS客户端API实现进行了测试:
The solutions mentioned above were tested against the following JAX-RS Client API implementations:
jersey-client
(版本2.23.1)resteasy-client
(版本3.0.18.Final)这篇关于是否可以为javax.ws.rs(Resteasy实现)重用Java可信证书?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!