I have an http2 server at https://ec2-52-57-54-142.eu-central-1.compute.amazonaws.com/ with a self-signed cert. And I have a jetty http2 client that simply posts things to it. For some reason, I'm not getting ignoring self-signed cert to work. Here's a snippet of relevant code
SslContextFactory factory = new SslContextFactory(true);
factory.setTrustAll(true);
factory.setValidateCerts(false);
factory.setValidatePeerCerts(false);
factory.setEndpointIdentificationAlgorithm(null);
SSLContext sslContext = factory.getSslContext();
if(null == sslContext) {
sslContext = SSLContext.getInstance("TLS");
}
TrustManager[] verifiers = new TrustManager[] {...// some dummy trust manager that always passes};
sslContext.init(null, verifiers, null);
factory.setSslContext(sslContext);
HttpClientTransportOverHTTP2 httpClientTransportOverHTTP2
= new HttpClientTransportOverHTTP2(new HTTP2Client());
HttpClient httpClient = new HttpClient(httpClientTransportOverHTTP2, factory);
Request request = httpClient.POST(destination);
ContentProvider contentProvider = new InputStreamContentProvider(new StringInputStream(payload));
request.content(contentProvider);
ContentResponse response = request.send();
And I get these stacktrace
Caused by: java.util.concurrent.ExecutionException: java.nio.channels.ClosedChannelException
at org.eclipse.jetty.client.util.FutureResponseListener.getResult(FutureResponseListener.java:118)
at org.eclipse.jetty.client.util.FutureResponseListener.get(FutureResponseListener.java:101)
at org.eclipse.jetty.client.HttpRequest.send(HttpRequest.java:652)
at my code
... 34 more
Caused by: java.nio.channels.ClosedChannelException
at org.eclipse.jetty.io.WriteFlusher.onClose(WriteFlusher.java:498)
at org.eclipse.jetty.io.ssl.SslConnection$DecryptedEndPoint.onIncompleteFlush(SslConnection.java:409)
at org.eclipse.jetty.io.AbstractEndPoint$2.onIncompleteFlush(AbstractEndPoint.java:54)
at org.eclipse.jetty.io.WriteFlusher.write(WriteFlusher.java:322)
at org.eclipse.jetty.io.AbstractEndPoint.write(AbstractEndPoint.java:140)
at org.eclipse.jetty.http2.HTTP2Flusher.process(HTTP2Flusher.java:243)
at org.eclipse.jetty.util.IteratingCallback.processing(IteratingCallback.java:241)
at org.eclipse.jetty.util.IteratingCallback.succeeded(IteratingCallback.java:365)
at org.eclipse.jetty.http2.HTTP2Flusher.succeeded(HTTP2Flusher.java:258)
at org.eclipse.jetty.io.WriteFlusher$PendingState.complete(WriteFlusher.java:269)
at org.eclipse.jetty.io.WriteFlusher.completeWrite(WriteFlusher.java:394)
at org.eclipse.jetty.io.ssl.SslConnection$1.run(SslConnection.java:101)
at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:671)
at org.eclipse.jetty.util.thread.QueuedThreadPool$2.run(QueuedThreadPool.java:589)
... 1 more
When I step through things with a debugger, I see something about NOT_HANDSHAKE in one of the connection objects.
I really don't care about verifying anything. I just want to connect over HTTP2/TLS. I searched for many different terms, but they all end up with more or less the same thing (setTrustAll, custom TrustManager, etc)
Any help? Thanks!
P.S. Jetty version 9.3.12
Your client code is correct, although redundant.
It is enough to do:
SslContextFactory sslContextFactory = new SslContextFactory(true);
HTTP2Client http2Client = new HTTP2Client();
HttpClient httpClient = new HttpClient(new HttpClientTransportOverHTTP2(http2Client), sslContextFactory);
httpClient.start();
ContentResponse response = httpClient.GET("https://ec2-52-57-54-142.eu-central-1.compute.amazonaws.com/");
If you enable DEBUG logging on the Jetty HTTP/2 client, you will see that the client receives:
2016-10-05 09:20:33.102:DBUG:oejhp.Parser:qtp1897115967-15: Parsed GO_AWAY frame header from java.nio.HeapByteBuffer[pos=9 lim=35 cap=16384]
2016-10-05 09:20:33.103:DBUG:oejh.HTTP2Session:qtp1897115967-15: Received GoAwayFrame#3bc447d3,0/INADEQUATE_SECURITY_ERROR/Unknown error code
So the problem is that the server thinks that the security is inadequate (the GOAWAY frame arrives with error code INADEQUATE_SECURITY_ERROR).
At this point, the problem is on the server. You have to figure out why the server thinks the security is inadequate. Probably just a matter of configuration on the server.
Try
transport.setUseALPN(false);
Related
I have a java client which connects to an HTTPS server (the server written in Java also). Here is the HttpClient setting in the client:
SSLContext ctx = SSLContext.getInstance("TLSv1.2");
keyManagers = ...; // Created from a PKIX KeyManagerFactory
trustManagers = ...; // Created from a PKIX TrustManagerFactory
ctx.init(keyManagers, trustManagers, new SecureRandom());
SSLContext.setDefault(ctx);
RequestConfig defaultRequestConfig = RequestConfig.custom()//
.setSocketTimeout(5000)//
.setConnectTimeout(5000)//
.setConnectionRequestTimeout(5000)//
.build();
httpClient = HttpClients.custom()//
.setSSLContext(ctx)//
.setDefaultRequestConfig(defaultRequestConfig)//
.setSSLHostnameVerifier(new NoopHostnameVerifier())//
.build();
The client certificate and trusted certificates are stored in a PKI token.
The client sends some HTTP requests to the server continuously. All things work fine. Now I want to force client (or server) to restart handshaking. In other words, I want to refresh SSL connection which causes to check server certificate periodically. Is there any way to do this?
I know about SSLSessionContext.setSessionTimeout(). But this will not refresh the current connection(s). It will force only new connections to do handshaking again.
For future readers.
I ask a similar question on security.stackexchange.com without details about programming. I had thought that the question may be a security issue. However, that question now is migrated from security.stackexchange.com to stackoverflow.com and has a convincing answer for me. I suggest referring to that: https://stackoverflow.com/a/55004572/5538979
You can clear the ssl caches with the following code snippet:
SSLContext sslContext = ...; // your initialised SSLContext
SSLSessionContext sslSessionContext = sslContext.getClientSessionContext();
Collections.list(sslContext.getClientSessionContext().getIds()).stream()
.map(sslSessionContext::getSession)
.filter(Objects::nonNull)
.forEach(SSLSession::invalidate);
I'm attempting to implement a WebSocket Client in an application that supports secure transmissions through SSL. The application already supports standard SSL connections over HTTP, by implementing custom Key and Trust managers (these custom implementations are in place to prompt the user for a certificate when needed).
I'm having trouble getting a secure connection to our remote WebSocket endpoint. The failure is occurring during the handshake. I've tried two different implementations of the WebSocket API (both Tyrus and Jetty), and both fail in the same way, which, of course, leads me to point to our SSL implementation.
As I mentioned, the failure is occurring during the handshake. It seems that the connection cannot figure out that there are client certificates that are signed by the supported authorities returned from the server. I'm stumped to figure out if I haven't supplied the client certificates to the WebSocket API correctly, or if our custom Key/Trust managers are even getting used.
Here's a dump of the SSL Debug logs:
*** CertificateRequest
Cert Types: RSA, DSS
Cert Authorities:
(list of about 15 cert authorities supported by the server)
*** ServerHelloDone
Warning: no suitable certificate found - continuing without client authentication
*** CertificateChain
<empty>
***
I've set breakpoints in our TrustManager implementation, to determine if they are ever getting called, and it seems that they are not being called at this point.
I've been attempting to debug this for a few days now, and am running out of things to try.
Any suggestions would be greatly appreciated!
Here's a snippet of the Jetty Code:
SSLContext context = SSLContext.getInstance("TLS");
// getKeyManagers / getTrustManagers retrieves an
// array containing the custom key and trust manager
// instances:
KeyManager[] km = getKeyManagers();
TrustManager[] tm = getTrustManagers();
context.init(km, tm, null);
SslContextFactory contextFactory = new SslContextFactory();
contextFactory.setContext(context);
WebSocketClient client = new WebSocketClient(contextFactory);
SimpleEchoClient echoClient = new SimpleEchoClient();
try {
client.start();
ClientUpgradeRequest request = new ClientUpgradeRequest();
Future<Session> connection = client.connect(echoClient, uri, request);
Session session = connection.get();
// if everything works, do stuff here
session.close();
client.destroy();
} catch (Exception e) {
LOG.error(e);
}
can you try with rejectUnAuthorized:false so that your certificates for which your browser is unable to authorize will skip the authorization.
var ws = new WebSocket('wss://localhost:xxxx', {
protocolVersion: 8,
origin: 'https://localhost:xxxx',
rejectUnauthorized: false
});
I am using jolokia client to connect to my fuse server, which is using https for web. I am getting the below exception.
org.jolokia.client.exception.J4pException: IO-Error while contacting the server: javax.net.ssl.SSLException: hostname in certificate didn't match: <10.16.205.20> !=
at org.jolokia.client.J4pClient.mapException(J4pClient.java:333)
at org.jolokia.client.J4pClient.execute(J4pClient.java:198)
at org.jolokia.client.J4pClient.execute(J4pClient.java:168)
at org.jolokia.client.J4pClient.execute(J4pClient.java:117)
I have already imported the certificate of 10.16.205.20 to my local truststrore (cacerts) from where my client application is running jolokia client. I have also verified the hosts file have entry for the domain that is being used in the certificate on 10.16.205.20 server. I am using the below code to connect.
J4pClient client = J4pClient.url(baseUrl).user(user.getName()).password(user.getPassword()).authenticator(new BasicAuthenticator().preemptive()).build();
J4pExecRequest request = new J4pExecRequest("org.apache.karaf:type=bundles,name=root","list");
J4pExecResponse response = client.execute(request);
JSONObject obj = response.asJSONObject();
((CloseableHttpClient)client.getHttpClient()).close();
This code is running fine with the server deployed with http. Please let me know, if I am missing something.
You need to let your client use a ConnectionSocketFactory that bypasses this check.
For instance take a look at the following code (Code is Kotlin but you can easily translate it to java, I guess)
val sslCtx: SSLContext = SSLContexts
.custom()
.loadTrustMaterial(null, TrustSelfSignedStrategy())
.build()
val cf: ConnectionSocketFactory = SSLConnectionSocketFactory(sslCtx, SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER)
J4pClient.url(s.jolokiaUrl)
.sslConnectionSocketFactory(cf)
.connectionTimeout(timeout.toMillis().toInt())
.build()
My client implements Two-Way SSL in the following way:
private final static String KEYSTORE = "/security/client.jks";
private final static String KEYSTORE_PASSWORD = "secret";
private final static String KEYSTORE_TYPE = "JKS";
private final static String TRUSTSTORE = "/security/certificates.jks";
private final static String TRUSTSTORE_PASSWORD = "secret";
private final static String TRUSTSTORE_TYPE = "JKS";
...
KeyStore keystore = KeyStore.getInstance(KEYSTORE_TYPE);
FileInputStream keystoreInput = new FileInputStream(new File(KEYSTORE));
keystore.load(keystoreInput, KEYSTORE_PASSWORD.toCharArray());
KeyStore truststore = KeyStore.getInstance(TRUSTSTORE_TYPE);
FileInputStream truststoreIs = new FileInputStream(new File(TRUSTSTORE));
truststore.load(truststoreIs, TRUSTSTORE_PASSWORD.toCharArray());
SSLSocketFactory socketFactory = new SSLSocketFactory(keystore, KEYSTORE_PASSWORD, truststore);
Scheme scheme = new Scheme("https", 8543, socketFactory);
SchemeRegistry registry = new SchemeRegistry();
registry.register(scheme);
ClientConnectionManager ccm = new PoolingClientConnectionManager(registry);
httpclient = new DefaultHttpClient(ccm);
HttpResponse response = null;
HttpGet httpget = new HttpGet("https://mylocalhost.com:8543/test");
response = httpclient.execute(httpget);
...
And I try to retrieve the X.509 certificate on the server's side from the client via javax.servlet.http.HttpServletRequest.getAttribute("javax.servlet.request.X509Certificate") as it is decribed here: http://tomcat.apache.org/tomcat-5.5-doc/servletapi/javax/servlet/ServletRequest.html#getAttribute%28java.lang.String%29.
I get the HttpServletRequest on the server's side via:
HttpServletRequest servletRequest = (HttpServletRequest) msg.get("HTTP.REQUEST"); via the handleMessage(Message msg) method of my interceptor class which extends AbstractPhaseInterceptor<Message>. I have to use JAX-RS 1.1.1 on the server's side because of some Maven dependencies which I am not allowed to change and so I cannot use ContainerRequestFilter (supported from JAX-RS 2.0 on).
My problem is that getAttribute("javax.servlet.request.X509Certificate") on the server's side returns null all the time. If I verify the traffic between server and client, I can see that the certificate from the server is sent to the client, that handshake works. But I cannot see that the client certificate is sent to the server and I think it is the reason why getAttribute("javax.servlet.request.X509Certificate") returns null. Does someone know how I can solve that problem? I tried some other implementations on the client's side already, but with no change.
What am I doing wrong? Many thanks in advance!
Additional information: I have seen on the server's side that javax.servlet.request.ssl_session_id, javax.servlet.request.key_size and javax.servlet.request.cipher_suite are set, but the key javax.servlet.request.X509Certificate is not set. I'm using Jetty Server 8.1.15, Apache CXF 2.7.x and JAX-RS 1.1.1. I tried with Jetty configuration via http://cxf.apache.org/docs/jetty-configuration.html and http://cxf.apache.org/docs/secure-jax-rs-services.html#SecureJAX-RSServices-Configuringendpoints, the attribute still isn't set.
Problem is solved. It wasn't a problem in the code, it was a certificate problem only. My problem was that I was a beginner regarding X509 certificates as well, it was a handshake problem between server and client. In this case, only the SSL/Handshake debug helped me. The debug log told that the server only accepted client certificates from a specific CA, the server told the client the required CA in a certificate request during the ServerHello message. Since the Client didn't have a certificate from that CA, it didn't send something and the connection between client and server was closed then, with the result that javax.servlet.request.X509Certificate was not set.
For all others who might join the same problem sometime (which seems to be a common SSL configuration problem regarding to IBM as it is mentioned in the first link below), the following sources helped me a lot:
- http://www-01.ibm.com/support/docview.wss?uid=swg27038122&aid=1
(pages 16 and 17)
- http://java.dzone.com/articles/how-analyze-java-ssl-errors (shows as the handshake should look)
- need help Debugging SSL handshake in tomcat (shows how to debug ssl errors in Java)
- https://thomas-leister.de/internet/eigene-openssl-certificate-authority-ca-erstellen-und-zertifikate-signieren/ (in German, but maybe you can find an English equivalent)
- https://access.redhat.com/site/documentation/en-US/Red_Hat_JBoss_Fuse/6.0/html/Web_Services_Security_Guide/files/i382674.html (continuation of the German article)
- http://www.webfarmr.eu/2010/04/import-pkcs12-private-keys-into-jks-keystores-using-java-keytool/ (how to create keystore and truststore)
After creating an own CA, a server and client certificate and after creating the keystore and truststore for both, the attribute was set now:
- Here15_1: javax.servlet.request.X509Certificate
- Here16_2: class [Ljava.security.cert.X509Certificate;
- Here16_3: [Ljava.security.cert.X509Certificate;#43b8f002
The server code is able to extract the client certificate information now, too.
I was a bit envious of our Ruby on Rails team who needs to consume the same web services requiring HTTPS request be signed with a certificate. They grab a gem, sign the request with a single line of code, dishes are done...
Java, however, requires that we import the cert to our keystore (trying to avoid this), or create one in memory...that's fine. But, once I do that, it seems like the only option to actually signing requests is using the HttpUrlConnection class. HttpUrlConnection example
I'd like to be able to sign requests using my existing code, which uses apache's DefaultHttpClient client = new DefaultHttpClient(); - but I don't see a way to have the Http client sign requests using an in memory keystore.
Anyone faced this?
Have a look at AuthSSLProtocolSocketFactory.java. You should be able to perform SSL client authentication like this (untested code);
Protocol.registerProtocol("https",
new Protocol("https", new AuthSSLProtocolSocketFactory(keystoreUrl, keystorePassword, truststoreUrl, truststorePassword), 443));
HttpClient httpclient = new HttpClient();
GetMethod httpget = new GetMethod("https://www.whatever.com/");
try {
httpclient.executeMethod(httpget);
System.out.println(httpget.getStatusLine());
} finally {
httpget.releaseConnection();
}
Your client certificate goes into keystore pointed by keystoreUrl. Read more about this on HttpClient SSL Guide.