How to work with client certificates on Jetty SPDY with ALPN? - java

I have problem with client certifiacates when I use SPDY with Jetty.
It works when I work with NPN and start Jetty SPDY server with:
SSLconnector = new HTTPSPDYServerConnector(server, sslContextFactory);
As a baseRequest.getHttpChannel() it uses org.eclipse.jetty.spdy.server.http.HttpChannelOverSPDY and I can read SSL properties like SSL_SESSION_ID and client certificates with code like:
// ... HttpServletRequest request
java.security.cert.X509Certificate client_certs[] = (java.security.cert.X509Certificate[])request.getAttribute("javax.servlet.request.X509Certificate");
But NPN is not an option in Java8 (see my question How to run Jetty with SPDY using ALPN?). In Java8 I have to use ALPN protocol like:
sslContextFactory.setWantClientAuth(w3srv_config.want_client_auth);
// ...
HttpConfiguration httpConfig = new HttpConfiguration();
SslConnectionFactory ssl = new SslConnectionFactory(sslContextFactory, "alpn");
ALPNServerConnectionFactory alpn = new ALPNServerConnectionFactory("spdy/3", "http/1.1");
alpn.setDefaultProtocol("http/1.1");
HTTPSPDYServerConnectionFactory spdy = new HTTPSPDYServerConnectionFactory(SPDY.V3, httpConfig);
HttpConnectionFactory http = new HttpConnectionFactory(httpConfig);
SSLconnector = new ServerConnector(server, new ConnectionFactory[]{ssl, alpn, spdy, http});
//...
With this code I got null when I want to get any SSL related javax.servlet.request.*. Its baseRequest.getHttpChannel() is org.eclipse.jetty.server.HttpConnection$HttpChannelOverHttp.
What I have to change to work with client certificates?

The javax.servlet.request.* properties you are looking for are set by Jetty's SecureRequestCustomizer, which you need to add to the httpConfig object you create in your code example above.
I am guessing that your NPN configuration is slightly different, or you use some utility method in Jetty that does this for you with NPN but not with ALPN.
Just doing:
HttpConfiguration httpConfig = new HttpConfiguration();
httpConfig.addCustomizer(new SecureRequestCustomizer());
should be enough to fix your issue.

Related

CometD: Use SSL/TLS

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

How to setEnabledCipherSuites when using Apache HTTP Client?

Since I need to work with some legacy server, and since RC4 was removed from the Java 8, I need to re-enable some RC4 based ciphers. As described in the release note we have to use SSLSocket/SSLEngine.setEnabledCipherSuites(). Since I'm using Apache HTTP Client I was not able to find a way to do this. Thanks in advance! (I also found quite semitrailer problem with out an answer so thought of posting a new one)
I was facing the same problem and I was able to figure this out.
SecureProtocolSocketFactoryImpl protFactory = new SecureProtocolSocketFactoryImpl();
httpsClient.getHostConfiguration().setHost(host, port, httpsProtocol);
In the "SecureProtocolSocketFactoryImpl" class you have to override the method public Socket createSocket() for SecureProtocolSocketFactory class.
In that method you will get a socket like this
SSLSocket soc = (SSLSocket) getSSLContext().getSocketFactory().createSocket(
socket,
host,
port,
autoClose
);
So there you will be able to do something like below.
ciphersToBeEnabled[0] = "TLS_ECDHE_ECDSA_WITH_RC4_128_SHA";
soc.setEnabledCipherSuites(ciphersToBeEnabled);
hope you get the idea. If you have any problems please comment below. Note that doing this only will not enable RC4 related ciphers. You will need to modify java "java.security" file in jre/lib/security/ file and remove CR4 form the disabled algorithm list.
The recommended way to get the HttpClient is by using HttpClientBuilder. In this builder, you can set the HttpClientConnectionManager which in turn can take a Registry<ConnectionSocketFactory>. In this ConnectionSocketFactory, you can configure ciphers and protocols that the client want to restrict.
Sample Code:
Registry<ConnectionSocketFactory> socketFactoryRegistry;
{
SSLContext sslcontext = <your SSLContext>;
socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
.register("http", new PlainConnectionSocketFactory())
.register("https", new SSLConnectionSocketFactory(sslcontext,
<your supported protocols, could be null>,
<your supported ciphers, could be null>,
<your HostnameVerifier>
.build();
}
HttpClientBuilder b = HttpClientBuilder.create()
.setConnectionManager(new BasicHttpClientConnectionManager(socketFactoryRegistry))
.set<anything else you want>(<with what you want>);
HttpClient client = b.build();

jersey-client 2.22.2 - How to set SunPKCS11 keystore on SslConfigurator properly?

I have been attempting to have my jersey client do a ssl client authentication with my Jersey/Grizzly Rest api. Other clients are successful handshaking with this server, but I am having trouble with my java client using Jersey client. When I run the code below, the keystore is successfully loaded and when the SslConfigurator's createSSLContext() is called, the ssl debug output shows this keystore properly being accessed and my private keys found.
However, when the Client's WebTarget is used, the ssl debug output shows the handshake is happening with the default keystore JKS. Why isn't the ClientBuilder using this keystore from the SSLContext?
File tmpConfigFile = File.createTempFile("pkcs11-", "conf");
tmpConfigFile.deleteOnExit();
PrintWriter configWriter = new PrintWriter(new FileOutputStream(tmpConfigFile), true);
configWriter.println("name=ActiveClient");
configWriter.println("library=\"C:\\\\Program Files\\\\ActivIdentity\\\\ActivClient\\\\acpkcs211.dll\"");
configWriter.println("slotListIndex=0");
SunPKCS11 provider = new SunPKCS11(tmpConfigFile.getAbsolutePath());
Security.addProvider(provider);
// KeyStore keyStore = KeyStore.getInstance("PKCS11", provider);
KeyStore keyStore = KeyStore.getInstance("PKCS11");
keyStore.load(null, null);
ClientConfig config = new ClientConfig();
SslConfigurator sslConfig = SslConfigurator.newInstance()
.keyStore(keyStore)
.keyStorePassword("mypin")
.keyStoreType("PKCS11")
.trustStoreFile(TRUSTORE_CLIENT_FILE)
.trustStorePassword(TRUSTSTORE_CLIENT_PWD)
.securityProtocol("TLS");
final SSLContext sslContext = sslConfig.createSSLContext();
Client client = ClientBuilder
.newBuilder().hostnameVerifier(new MyHostnNameVerifier())
.sslContext(sslContext)
.build();
WebTarget target = client.target("https://localhost:8443/appname/resources/employees?qparam=something");
Response res = target.request().accept(MediaType.APPLICATION_JSON).get();
This code actually worked. The problem was that my server's trust certificate wasn't available for the smart card cert that it needed to trust. I added the correct certs to the truststore on the server and then it worked. The ssl debug messages weren't very clear.
I've run into many issues this time and I found a way to achieve my goals. In your example I can not see any use of ClientConfig config instance. This worked for me:
ClientConfig config = new ClientConfig();
config.connectorProvider(new ApacheConnectorProvider());
Client client = ClientBuilder.newBuilder().hostnameVerifier(new MyHostnNameVerifier())
.sslContext(sslContext).withConfig(config).build();
I found ApacheConnectorProvider more suitable for connections using secure layers or proxies (witch was another huge problem I solved).

Ignore certificate validation - Tomcat8 WebSocket (JSR-356)

SslContextFactory sec = new SslContextFactory();
sec.setValidateCerts(false);
WebSocketClient client = new WebSocketClient(sec);
The above code is implemented for Jetty WebSockets, to tell the java client to disable certificate validation. Is there any way I can achieve the same in Java API for Tomcat8 WebSockets (JSR-356)?
PS: I have tried this method. It didn't work for Secure WebSocket connection of Tomcat WebSockets
Did you generate self signed certificate and trying to use it?
Then import your self signed certificate to new keystore and use that keystore as a trust store on your client side.
For a tyrus websocket client, I use like this:
String keyStorePath = StompClientTest.class.getResource("/myapp.keystore").getPath();
System.getProperties().put("javax.net.debug", "all"); // debug your certificate checking
System.getProperties().put(SslContextConfigurator.KEY_STORE_FILE, keyStorePath);
System.getProperties().put(SslContextConfigurator.TRUST_STORE_FILE, keyStorePath);
System.getProperties().put(SslContextConfigurator.KEY_STORE_PASSWORD, "secret");
System.getProperties().put(SslContextConfigurator.TRUST_STORE_PASSWORD, "secret");
final SslContextConfigurator defaultConfig = new SslContextConfigurator();
defaultConfig.retrieve(System.getProperties());
SslEngineConfigurator sslEngineConfigurator = new SslEngineConfigurator(defaultConfig);
sslEngineConfigurator.setHostVerificationEnabled(false);
StandardWebSocketClient webSocketClient = new StandardWebSocketClient();
webSocketClient.getUserProperties().put(ClientProperties.SSL_ENGINE_CONFIGURATOR, sslEngineConfigurator);
For tomcat read answer in following question: https://stackoverflow.com/a/32205864/386213

How to use Apache Httpclient with Jersey 2.3 client

I would like to use an Apache connector with Jersey 2.3 client for HTTPS connections.
I tried the following:
ClientConfig clientConfig = new ClientConfig();
clientConfig.property(ApacheClientProperties.CONNECTION_MANAGER, new PoolingClientConnectionManager());
ApacheConnector connector = new ApacheConnector(clientConfig);
clientConfig.connector(connector);
Client client = ClientBuilder.newBuilder()
.withConfig(clientConfig)
.sslContext(sslContext)
.hostnameVerifier(getHostnameVerifier())
.build();
However, it seems the sslContext is ignored as the certificate of the server is rejected as untrusted (sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target)
If I remove the ".withConfig(clientConfig)" part, the SSL connection works fine, but evidently without the Apache connector. Is there a way to use my own ClientConfig with the Apache connector as well as my own SSLContext?
you need config SSL for apache connector.
ClientConfig clientConfig = new ClientConfig();
clientConfig.property(ApacheClientProperties.CONNECTION_MANAGER, new PoolingClientConnectionManager());
//config your ssl for apache connector
SslConfigurator sslConfig = SslConfigurator.newInstance();
clientConfig.property(ApacheClientProperties.SSL_CONFIG, sslConfig);
ApacheConnector connector = new ApacheConnector(clientConfig);
clientConfig.connector(connector);
Client client = ClientBuilder.newBuilder()
.withConfig(clientConfig)
.hostnameVerifier(getHostnameVerifier())
.build();
Not sure whay are you using apache connector. But you can make a client connection using jersey and https like this:
ClientConfig config = new DefaultClientConfig();
SSLContext ctx = SSLContext.getInstance("SSL");
ctx.init(null, myTrustManager, null);
config.getProperties().put(HTTPSProperties.PROPERTY_HTTPS_PROPERTIES, new HTTPSProperties(hostnameVerifier, ctx));
Client client = Client.create(config);

Categories

Resources