是否可以为javax.ws.rs(Resteasy实现)重用Java可信证书? [英] Is it possible to reuse Java trusted certificates for javax.ws.rs (Resteasy implementation)?

查看:117
本文介绍了是否可以为javax.ws.rs(Resteasy实现)重用Java可信证书?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设我们需要信任一个自签名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 be null.

返回:

更新的客户端构建器实例.

an updated client builder instance.

抛出:

NullPointerException-如果sslContext参数为null.

假设您已将证书添加到cacerts信任库,则可以使用默认的

Assuming you have added the certificate to cacerts trust store, you could use the default SSLContext when creating your Client 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 referencing HttpClient, but for some applications it may be necessary to drill down into the HttpClient 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 your Client, you could load a KeyStore. To load cacerts 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:

这篇关于是否可以为javax.ws.rs(Resteasy实现)重用Java可信证书?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆