I have written a test client that sends requests over TLS using the Jersey library. I am having a hard time trying to figure out how to configure/code the Jersey client such that it reuses the SSL sessions so that I could make my tests faster. The Jersey client by default uses HTTP keepAlive; meaning it keeps the TCP connections open and reuses them, but it doesn't seem to do the same with SSL sessions.
If anyone had any experience with this, please let me know.
Here is the code snippet with which I am setting up the Jersey Client and also the code with which I am sending a request:
SSLContext sslContext = sslConfig.createSSLContext().getInstance(tlsVersion);
sslContext.init(keyManagerFactory.getKeyManagers(), trustAllCerts, new SecureRandom());
SSLEngine sslEngine = sslContext.createSSLEngine("qa.p.uvvu.com", 7001);
Client client = ClientBuilder.newBuilder().sslContext(sslContext)
.hostnameVerifier(new HostnameVerifier() {
#Override
public boolean verify(String hostname, SSLSession session) {
return true;
}
}).build();
response = client.target(uri).request()
.headers(multivaluedMap).post(Entity.entity(object, MediaType.APPLICATION_XML_TYPE));
I use the Apache connector for this:
HttpClientConnectionManager connManager = PoolingHttpClientConnectionManager();
ClientConfig clientConfig = new ClientConfig();
clientConfig.connectorProvider(new ApacheConnectorProvider());
clientConfig.property(ApacheClientProperties.CONNECTION_MANAGER, connManager);
Client client = ClientBuilder.newClient(clientConfig);
You can configure the HttpClientConnectionManager to your needs depending on how many connections you want to keep in the pool, and for how long you want to keep them in the pool.
Disclaimer: I don't have the code with me, so the code above might not work 100%.
Related
Is there any example of how make a HTTPS call with a hapi fhir client ?
FhirContext ctx = new FhirContext();
IGenericClient client = ctx.newRestfulGenericClient("https://fhirtest.uhn.ca/base");
By default the above code will not work as the server will require SSL authentication.
how do I add SSL authentication to the hapi client ??
The next example shows how to connect to a FHIR server using https while using the HAPI FHIR client. Please be aware that this example accepts all certificates. To make it secure you should specify a truststore and a different hostname verifier.
FhirContext ctx = new FhirContext();
KeyStore truststore = null;
SSLContext sslContext = SSLContexts.custom().loadTrustMaterial(truststore, new TrustSelfSignedStrategy()).build();
HostnameVerifier hostnameVerifier = NoopHostnameVerifier.INSTANCE;
SSLConnectionSocketFactory sslFactory = new SSLConnectionSocketFactory(sslContext, hostnameVerifier);
CloseableHttpClient httpClient = HttpClients.custom().setSSLSocketFactory(sslFactory).build();
ctx.getRestfulClientFactory().setHttpClient(httpClient);
IGenericClient client = ctx.newRestfulGenericClient("https://fhirtest.uhn.ca/base");
I want to make a HTTP call to server that uses TLS to authenticate. Moreover server needs my IP to be whitelisted, so with AWS Lambda I need to use proxy. What I want to achieve is HTTP POST request with TLS that goes through proxy.
To achieve TLS protocol I use KeyStore with loaded certs and private key.
Making a call without proxy (locally from whitelisted IP) works, so I assume keyStore is configured correctly.
Here is how I build httpClient (it's java.net.http.HttpClient):
var keyManagerFactory = KeyManagerFactory.getInstance("PKIX");
keyManagerFactory.init(keyStore, null);
var trustManagerFactory = TrustManagerFactory.getInstance("PKIX");
trustManagerFactory.init(keyStore, null);
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), null);
URI proxyUri = config.getProxyUri(); // this is injected object with preloaded config parameters
HttpClient httpClient = HttpClient.newBuilder()
.sslContext(sslContext)
.proxy(
ProxySelector.of(
InetSocketAddress.createUnresolved(proxyUri.getHost(), proxyUri.getPort())))
.build();
Now making a request:
String body = createRequestBody(); // creates string with JSON
HttpRequest request = HttpRequest.newBuilder()
.uri(config.getServiceUri()) // same config as in example above
.header("Content-Type", "application/json")
.POST(BodyPublishers.ofString(body))
.build();
HttpResponse<String> response = httpClient.send(request, BodyHandlers.ofString());
Calling .send(...) causes
java.io.IOException: Tunnel failed, got: 403
# java.net.http/jdk.internal.net.http.HttpClientImpl.send(Unknown Source)
# java.net.http/jdk.internal.net.http.HttpClientFacade.send(Unknown Source)
# (method we are in above example)
Proxy doesn't need any authentication and in other AWS Lambda I've seen this proxy working with builder using only .proxy(...) method just like in the example above. So the only thing that is different is this .sslContext(...).
Do I need some more sslContext configuration? I've been searching for some examples with TLS through proxy, but I've not managed to find anything.
HttpClient.Builder Docs doesn't say anything about proxy with sslContext either.
Thanks for help!
As daniel wrote in a comment
It would seem that you have insufficient permission to access the service you're trying to use
It turned out to be proxy config that was blocking traffic to that specific host and port.
There is nothing wrong with the code above. After a change in proxy settings to it works as expected.
Thanks for help!
How do I enable secure connections with CometD?
I have an app that is working when I use an "http" protocol for the BayeuxServer. If I switch to "https", I get failed handshakes.
What is the correct way to use a secure connection in CometD?
This is via the Java Client.
Here is the error:
{failure={exception=java.lang.NullPointerException, message={ext={ack=true}, supportedConnectionTypes=[long-polling], channel=/meta/handshake, id=4, version=1.0}, connectionType=long-polling}, channel=/meta/handshake, id=4, subscription=null, successful=false}
I do not see any exceptions on the server (ie, the null pointer is not in our code), and if I use HTTP, it works fine.
I've pieced together the following for the Java client side:
SslContextFactory sslContextFactory = new SslContextFactory();
sslContextFactory.setTrustAll(true); // only interacting with our backend, so accept self-signed certs
WebSocketClient webSocketClient = new WebSocketClient(sslContextFactory);
webSocketClient.start();
ClientTransport wsTransport = new JettyWebSocketTransport(null, null, webSocketClient);
HttpClient httpClient = new HttpClient(sslContextFactory);
httpClient.start();
ClientTransport httpTransport = new LongPollingTransport(null, httpClient);
I believe that will do it.
I still need to figure out how to configure the server side cometd to accept the secure connections. I am using the Spring setup.
The answer to the server side is: Its a pain in the ass.
Here is how you can get it working with the jetty maven plugin:
http://juplo.de/configure-https-for-jetty-maven-plugin-9-0-x/#comment-53352
I'm trying to create simple web server using java sockets which should support both http & https. But i can acheive only one at a time. I need to logic which supports both http # port 80 & https # port 443 at same time.
This is the sample code for HTTPS Server using sslsocket. We can acheive HTTP Server using simple ServerSocket.
public class HttpsServer {
public static void main(String[] args) {
try {
KeyStore ks = KeyStore.getInstance("PKCS12");
ks.load(new FileInputStream("/opt/p12file.p12"), "p12pass".toCharArray());
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmf.init(ks, "p12pass".toCharArray());
SSLContext sc = SSLContext.getInstance("TLS");
sc.init(kmf.getKeyManagers(), null, null);
SSLServerSocketFactory ssf = sc.getServerSocketFactory();
SSLServerSocket s = (SSLServerSocket) ssf.createServerSocket(8080);
while (true) {
SSLSocket c = (SSLSocket) s.accept();
BufferedWriter w = new BufferedWriter(new OutputStreamWriter(c.getOutputStream()));
w.write("HTTP/1.0 200 OK");
w.newLine();
w.write("Content-Type: text/html");
w.newLine();
w.newLine();
w.write("<html><body><h1>Https Server Works</h1></body></html>");
w.newLine();
w.flush();
w.close();
c.close();
}
}
catch (Exception e) {
e.printStackTrace();
}
}
}
Can anyone help me please??
How make SSL server socket support both http & https in java?
You can't. HTTP is plaintext, which SSLServerSocket cannot support.
I'm trying to create simple web server using java sockets which should support both http & https. But I can achieve only one at a time. I need to logic which supports both http # port 80 & https # port 443 at same time.
You need:
a plaintext ServerSocket listening at 80
an SSLServerSocket listening at 443
an accept-loop thread for each of these
a connection thread per accepted socket.
You will never ever get it done inside a static main() method. I suggest you read the 'Custom Networking' section of the Java Tutorial, and then the JSSE Reference Guide.
You also of course need to take a really good look at RFC 2616 HTTP 1.1. It is extremely non-trivial to implement correctly.
As suggested in comments, you should really use something off-the-shelf.
You have two options:
Use two different ports, one for http and one for https.
SSL Hello detection / Port unification:
In HTTP and HTTPS the client is expected to talk first. So the server can use this to detect the protocol the client is expecting:
if the client sends a TLS ClientHello, then proceed with a TLS handshake;
if a plain HTTP request is sent instead, then handle the request as it is.
More information:
Can a Java server accept both SSL and plaintext connections on one port?
Is it possible to change plain socket to SSLSocket?
I am developing a SSL/TLS enabled server using the Java SimpleFramework. I am wondering how to validate client authentications on the server.
On the server side, I am extending org.simpleframework.http.core.ContainerServer and overriding the process() method as follows:
#Override
public void process(Socket socket) throws IOException {
// Ensures client authentication is required
socket.getEngine().setNeedClientAuth(true);
super.process(socket);
}
This is to make sure that clients authenticate. Note that if I remove the call to setNeedClientAuth(), my program works perfectly.
On the client side, the following code is used:
HttpClient client = new HttpClient();
Credentials defaultcreds = new UsernamePasswordCredentials("username", "password");
client.getState().setCredentials(AuthScope.ANY, defaultcreds);
GetMethod get = new GetMethod("https://url.to.server");
get.setDoAuthentication(true);
client.executeMethod(get);
When enabling authentication requirement, I get the following exception:
javax.net.ssl.SSLHandshakeException: Remote host closed connection during handshake
I am guessing this relates to the fact that the passed credentials is never validated.
To summarize my question, how should I proceed to validate clients on the server?