Intercept SSL/TLS requests in HTTPS Grizzly server - ssl

I have set up an HTTPS server using grizzly 2.3.30 and jersey 2.25.1, which can be found here.
The server works well and I can curl to it with certificate-authority, certificate and key:
curl -v --cacert $CERTS/myCA.pem --key $CERTS/grizzly.key --cert $CERTS/grizzly.crt https://localhost:9999/hello
I want to intercept TLS/SSL requests, so I can log which ones fail like for example:
curl -v https://localhost:9999/hello
I am using Grizzly Http Server Framework with Jersey in this fashion:
public class MyGrizzlyServer {
public static void main(String[] args) throws Exception {
System.out.println("Hello main!");
String uriStr = "https://0.0.0.0:9999/";
URI uri = URI.create(uriStr);
final ResourceConfig rc = new ResourceConfig().packages("org");
HttpServer server = GrizzlyHttpServerFactory.createHttpServer(uri, rc, false);
SSLEngineConfigurator engineConfig = getSslEngineConfig();
for (NetworkListener listener : server.getListeners()) {
listener.setSecure(true);
listener.setSSLEngineConfig(engineConfig);
}
HttpHandler handler = server.getHttpHandler();
System.out.println("Http server start...");
server.start();
System.out.println("Hit enter to stop it...");
System.in.read();
server.shutdownNow();
}
private static SSLEngineConfigurator getSslEngineConfig() {
SSLContextConfigurator sslConfigurator = new SSLContextConfigurator();
sslConfigurator.setKeyStoreFile("./mycerts/grizzly.jks");
sslConfigurator.setKeyStorePass("awesome");
sslConfigurator.setTrustStoreFile("./mycerts/myCA.jks");
sslConfigurator.setTrustStorePass("mycapass");
sslConfigurator.setSecurityProtocol("TLS");
SSLContext context = sslConfigurator.createSSLContext(true);
SSLEngineConfigurator sslEngineConfigurator = new SSLEngineConfigurator(context);
sslEngineConfigurator.setNeedClientAuth(true);
sslEngineConfigurator.setClientMode(false);
return sslEngineConfigurator;
}
}
I have been reading Grizzly documentation to get familiarized with its internals.
Grizzly seems to pile filter chains for transport, ssl, http, etc.
I am experimenting with this, but haven't figured out how to achieve it yet.
Any hint will be appreciated.

After playing a bit with filter chains, I was able to remove default SSLBaseFilter and add a custom SSL Filter inherited from SSLBaseFilter.
That way I could captured exceptions thrown by failed TLS/SSL requests.
In MyGrizzlyServer server:
server.start();
NetworkListener listener = server.getListener("grizzly");
FilterChain filterChain = listener.getFilterChain();
int sslBaseFilterIndex = filterChain.indexOfType(SSLBaseFilter.class);
filterChain.remove(sslBaseFilterIndex);
MySslFilter sslFilter = new MySslFilter(sslEngineConfig);
filterChain.add(sslBaseFilterIndex, sslFilter);
With custom SSL filter:
public class MySslFilter extends SSLBaseFilter {
MySslFilter(SSLEngineConfigurator configurator) {
super(configurator);
}
#Override
public NextAction handleRead(FilterChainContext ctx) throws IOException {
NextAction nextAction = null;
try {
System.out.println(" *** MySslFilter handleRead ***" );
nextAction = super.handleRead(ctx);
} catch (IOException e) {
System.out.println(" *** MySslFilter Exception ***" );
e.printStackTrace();
}
return nextAction;
}
}

Related

Problem using git token with jgit sshFactory (JschConfigSessionFactory)

I have the following code running with JGIT and when I set the repo to the origin to HTTPS protocol I'm able to fetch using JGIT with no issues. However, when I change the repo's origin to an SSH protocol I get an AUTH failed error. I've looked through stack overflow and the JGIT documentation and am wondering if someone can take a look at this code and advise on how to use a git token to auth through SSH. is this even possible? Thanks
FetchResult result = git.fetch().setTransportConfigCallback(transport -> {
if (transport instanceof SshTransport) {
SshTransport sshTransport = (SshTransport) transport;
SshSessionFactory sshSessionFactory = new JschConfigSessionFactory() {
#Override
protected void configure(OpenSshConfig.Host host, Session session) {
session.setPassword("<TOKEN>");
session.setConfig("StrictHostKeyChecking", "no");
}
#Override
protected JSch getJSch(final OpenSshConfig.Host hc, FS fs) throws JSchException {
JSch jsch = super.getJSch(hc, fs);
jsch.removeAllIdentity();
return jsch;
}
};
sshTransport.setSshSessionFactory(sshSessionFactory);
} else if (transport instanceof HttpTransport) {
HttpTransport httpTransport = (HttpTransport) transport;
httpTransport.setCredentialsProvider(new UsernamePasswordCredentialsProvider("<SERVICE_NAME", "<TOKEN>"));
}
}).setCheckFetchedObjects(true).call();
}

How to connect to FTPS server with data connection using same TLS session from Apache Camel using custom FTPSClient?

I would like to send files to FTPS server using Apache Camel. The problem is that this FTPS server requires that the TLS/SSL session is to be reused for the data connection. And I can't set 'TLSOptions NoSessionReuseRequired' option for security reason to solve the issue.
As far as I know, Apache Camel uses Apache Common Net class FTPSClient internally to communicate to FTPS servers and Apache Common Net doesn't support this feature as described here
So I has implemented this workaround. Here is code of my custom FTPSClient:
public class SSLSessionReuseFTPSClient extends FTPSClient {
// adapted from: https://trac.cyberduck.io/changeset/10760
#Override
protected void _prepareDataSocket_(final Socket socket) throws IOException {
if (socket instanceof SSLSocket) {
final SSLSession session = ((SSLSocket) _socket_).getSession();
final SSLSessionContext context = session.getSessionContext();
try {
final Field sessionHostPortCache = context.getClass().getDeclaredField("sessionHostPortCache");
sessionHostPortCache.setAccessible(true);
final Object cache = sessionHostPortCache.get(context);
final Method putMethod = cache.getClass().getDeclaredMethod("put", Object.class, Object.class);
putMethod.setAccessible(true);
// final Method getHostMethod = socket.getClass().getDeclaredMethod("getHost");
Method getHostMethod;
try {
getHostMethod = socket.getClass().getDeclaredMethod("getPeerHost");
} catch (NoSuchMethodException e) {
getHostMethod = socket.getClass().getDeclaredMethod("getHost");
}
getHostMethod.setAccessible(true);
Object host = getHostMethod.invoke(socket);
final String key = String.format("%s:%s", host, String.valueOf(socket.getPort()))
.toLowerCase(Locale.ROOT);
putMethod.invoke(cache, key, session);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
}
It works brilliantly as standalone FTPS client in JDK 8 and JDK 11 as shown:
public class FTPSDemoClient {
public static void main(String[] args) throws IOException {
System.out.println("Java version is: " + System.getProperty("java.version"));
System.out.println("Java vendor is: " + System.getProperty("java.vendor"));
final SSLSessionReuseFTPSClient ftps = new SSLSessionReuseFTPSClient();
System.setProperty("jdk.tls.useExtendedMasterSecret", "false");
System.setProperty("jdk.tls.client.enableSessionTicketExtension", "false");
System.setProperty("jdk.tls.client.protocols", "TLSv1,TLSv1.1,TLSv1.2");
System.setProperty("https.protocols", "TLSv1,TLSv1.1,TLSv1.2");
//System.setProperty("javax.net.debug", "all");
ftps.setTrustManager(TrustManagerUtils.getAcceptAllTrustManager());
ftps.addProtocolCommandListener(new PrintCommandListener(new PrintWriter(System.out), true));
ftps.connect("my_ftps_server");
System.out.println("Connected to server");
ftps.login("user", "password");
System.out.println("Loggeded to server");
ftps.setFileType(FTP.BINARY_FILE_TYPE);
// Use passive mode as default because most of us are
// behind firewalls these days.
ftps.enterLocalPassiveMode();
ftps.setUseEPSVwithIPv4(true);
// Set data channel protection to private
ftps.execPROT("P");
for (final String s : ftps.listNames("directory1/directory2")) {
System.out.println(s);
}
// send file
try (final InputStream input = new FileInputStream("C:\\testdata\\olympus2.jpg")) {
ftps.storeFile("directory1/directory2/olympus2.jpg", input);
}
// receive file
try (final OutputStream output = new FileOutputStream("C:\\testdata\\ddd.txt")) {
ftps.retrieveFile(""directory1/directory2/ddd.txt", output);
}
ftps.logout();
if (ftps.isConnected()) {
try {
ftps.disconnect();
} catch (final IOException f) {
// do nothing
}
}
}
}
Now I am ready to use this custom FTPSClient in my Apache Camel route, first I create custom FTPSClient instance and make it available for Apache Camel:
public final class MyFtpClient {
public static void main(String[] args) {
RouteBuilder routeBuilder = new MyFtpClientRouteBuilder();
System.out.println("Java version is: " + System.getProperty("java.version"));
System.out.println("Java vendor is: " + System.getProperty("java.vendor"));
System.setProperty("jdk.tls.useExtendedMasterSecret", "false");
System.setProperty("jdk.tls.client.enableSessionTicketExtension", String.valueOf(false));
System.setProperty("jdk.tls.client.protocols", "TLSv1,TLSv1.1,TLSv1.2");
System.setProperty("https.protocols", "TLSv1,TLSv1.1,TLSv1.2");
SSLSessionReuseFTPSClient ftps = new SSLSessionReuseFTPSClient();
ftps.setTrustManager(TrustManagerUtils.getAcceptAllTrustManager());
// ftps.addProtocolCommandListener(new PrintCommandListener(new PrintWriter(System.out), true));
ftps.setRemoteVerificationEnabled(false);
ftps.setUseEPSVwithIPv4(true);
SimpleRegistry registry = new SimpleRegistry();
registry.bind("FTPClient", ftps);
// tell Camel to use our SimpleRegistry
CamelContext ctx = new DefaultCamelContext(registry);
try {
ctx.addRoutes(routeBuilder);
ctx.start();
Thread.sleep(5 * 60 * 1000);
ctx.stop();
}
catch (Exception e) {
e.printStackTrace();
}
}
}
And use it in Apache Camel Route:
public class MyFtpClientRouteBuilder extends RouteBuilder {
#Override
public void configure() throws Exception {
// lets shutdown faster in case of in-flight messages stack up
getContext().getShutdownStrategy().setTimeout(10);
from("ftps://my_ftps_server:21/directory1/directory2?username=user&password=RAW(password)"
+ "&localWorkDirectory=/tmp&autoCreate=false&passiveMode=true&binary=true&noop=true&resumeDownload=true"
+ "&bridgeErrorHandler=true&throwExceptionOnConnectFailed=true&maximumReconnectAttempts=0&transferLoggingLevel=OFF"
+ "&readLock=changed&disconnect=true&ftpClient=#FTPClient") // #FTPClient
.to("file://c:/testdata?noop=true&readLock=changed")
.log("Downloaded file ${file:name} complete.");
// use system out so it stand out
System.out.println("*********************************************************************************");
System.out.println("Use ctrl + c to stop this application.");
System.out.println("*********************************************************************************");
}
}
And it works!
But, when I add another route in the same java code by adding second from clause like this:
from("ftps://my_ftps_server/directory1/directory2?username=user&password=RAW(password)"
+ "&localWorkDirectory=/tmp&autoCreate=false&passiveMode=true&binary=true&noop=true&resumeDownload=true"
+ "&bridgeErrorHandler=true&throwExceptionOnConnectFailed=true&maximumReconnectAttempts=0&transferLoggingLevel=OFF"
+ "&readLock=changed&disconnect=true&ftpClient=#FTPClient") // #FTPClient
.to("file://c:/testdata?noop=true&readLock=changed")
.log("Downloaded file ${file:name} complete.");
from("file://c:/testdata?noop=true&readLock=changed&delay=30s")
.to("ftps://my_ftps_server/directory1/directory2?username=user&password=RAW(password)"
+ "&localWorkDirectory=/tmp&autoCreate=false&passiveMode=true&binary=true&noop=true&resumeDownload=true"
+ "&bridgeErrorHandler=true&throwExceptionOnConnectFailed=true&maximumReconnectAttempts=0&transferLoggingLevel=OFF"
+ "&readLock=changed&disconnect=true&stepwise=false&ftpClient=#FTPClient") // changed from FTPClient to FTPClient1
.log("Upload file ${file:name} complete.");
it ruins my code, it throws exception:
org.apache.camel.component.file.GenericFileOperationFailedException: File operation failed: null Socket is closed. Code: 226
...
Caused by: java.net.SocketException: Socket is closed
at java.net.Socket.setSoTimeout(Socket.java:1155) ~[?:?]
at sun.security.ssl.BaseSSLSocketImpl.setSoTimeout(BaseSSLSocketImpl.java:637) ~[?:?]
at sun.security.ssl.SSLSocketImpl.setSoTimeout(SSLSocketImpl.java:74) ~[?:?]
at org.apache.commons.net.ftp.FTP._connectAction_(FTP.java:426) ~[commons-net-3.8.0.jar:3.8.0]
at org.apache.commons.net.ftp.FTPClient._connectAction_(FTPClient.java:668) ~[commons-net-3.8.0.jar:3.8.0]
at org.apache.commons.net.ftp.FTPClient._connectAction_(FTPClient.java:658) ~[commons-net-3.8.0.jar:3.8.0]
at org.apache.commons.net.ftp.FTPSClient._connectAction_(FTPSClient.java:221) ~[commons-net-3.8.0.jar:3.8.0]
at org.apache.commons.net.SocketClient._connect(SocketClient.java:254) ~[commons-net-3.8.0.jar:3.8.0]
at org.apache.commons.net.SocketClient.connect(SocketClient.java:212) ~[commons-net-3.8.0.jar:3.8.0]
at org.apache.camel.component.file.remote.FtpOperations.doConnect(FtpOperations.java:125) ~[camel-ftp-3.4.1.jar:3.4.1]
Files, anyway are transferred to and from FTPS server by Apache Camel.
Interesting thing, when I don't share my custom FTPSClient and use one instance exactly for one route like this:
SSLSessionReuseFTPSClient ftps = new SSLSessionReuseFTPSClient();
...
SSLSessionReuseFTPSClient ftps1 = new SSLSessionReuseFTPSClient();
...
SimpleRegistry registry = new SimpleRegistry();
registry.bind("FTPClient", ftps);
registry.bind("FTPClient1", ftps1);
from("ftps://my_ftps_server/directory1/directory2?username=user&password=RAW(password)"
+ "&localWorkDirectory=/tmp&autoCreate=false&passiveMode=true&binary=true&noop=true&resumeDownload=true"
+ "&bridgeErrorHandler=true&throwExceptionOnConnectFailed=true&maximumReconnectAttempts=0&transferLoggingLevel=OFF"
+ "&readLock=changed&disconnect=true&ftpClient=#FTPClient") // #FTPClient
.to("file://c:/testdata?noop=true&readLock=changed")
.log("Downloaded file ${file:name} complete.");
from("file://c:/testdata?noop=true&readLock=changed&delay=30s")
.to("ftps://my_ftps_server/directory1/directory2?username=user&password=RAW(password)"
+ "&localWorkDirectory=/tmp&autoCreate=false&passiveMode=true&binary=true&noop=true&resumeDownload=true"
+ "&bridgeErrorHandler=true&throwExceptionOnConnectFailed=true&maximumReconnectAttempts=0&transferLoggingLevel=OFF"
+ "&readLock=changed&disconnect=true&stepwise=false&ftpClient=#FTPClient1")
.log("Upload file ${file:name} complete.");
it works perfectly!
So, I have couple of questions:
Why does Apache Camel (I mean Apache Common Net) developers refuse (or can't) to add usage of same TLS session functionality to FTPSClient class since 2011?
Am I the only person who uses Apache Camel to work with FTPS server with data connection using same TLS session? I haven't managed to find solution anywhere.
Is it possible to force Apache Camel not to share custom FTPSClient instance what, I suppose is the root of the problem, but to create new instance of FTPSClient every time then route are processed? My solution doesn't seem elegant.
What is wrong in my custom FTPSClient implementation that leads to this error then I use instance of this class in Apache Camel? Standard FTPClient hasn't this issue, of course.

Java, Apache HttpClient, TLSv1.2 & OpenJDK 7

We have a small group of Tomcat servers running OpenJDK v1.7.0_111. We have plans to upgrade them and migrate them this summer but we've found that a client API we interact with is moving to require TLSv1.2 in the near term. My ultimate desire is to find a configuration change to allow for this.
The application hosted there creates it's SSL context in a pretty straight forward way:
SSLContext sslContext = SSLContexts.createDefault()
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext);
SSLContexts is from Apache's httpclient library (version 4.4.1) and is also pretty straight forward with how it creates the SSL context:
public static SSLContext createDefault() throws SSLInitializationException {
try {
SSLContext ex = SSLContext.getInstance("TLS");
ex.init((KeyManager[])null, (TrustManager[])null, (SecureRandom)null);
return ex;
} catch (NoSuchAlgorithmException var1) {
throw new SSLInitializationException(var1.getMessage(), var1);
} catch (KeyManagementException var2) {
throw new SSLInitializationException(var2.getMessage(), var2);
}
}
And digging through the SSLConnectionSocketFactory class, it appears that it's simply using the SSLSocket.getEnabledProtocols() method to determine which protocols are available for use. Note that this.supportedProtocols is null in my case.
public Socket createLayeredSocket(Socket socket, String target, int port, HttpContext context) throws IOException {
SSLSocket sslsock = (SSLSocket)this.socketfactory.createSocket(socket, target, port, true);
if(this.supportedProtocols != null) {
sslsock.setEnabledProtocols(this.supportedProtocols);
} else {
String[] allProtocols = sslsock.getEnabledProtocols();
ArrayList enabledProtocols = new ArrayList(allProtocols.length);
String[] arr$ = allProtocols;
int len$ = allProtocols.length;
for(int i$ = 0; i$ < len$; ++i$) {
String protocol = arr$[i$];
if(!protocol.startsWith("SSL")) {
enabledProtocols.add(protocol);
}
}
if(!enabledProtocols.isEmpty()) {
sslsock.setEnabledProtocols((String[])enabledProtocols.toArray(new String[enabledProtocols.size()]));
}
}
The problem I'm having is that while running a few preliminary tests I'm unable to get these clients to connect to an API requiring TLSv1.2.
In the following example I can get the URLConnection code to complete by including the -Dhttps.protocols=TLSv1.2 parameter, but I cannot get the Apache connection to connect.
public static void main(String[] args) throws Exception{
String testURL = "https://testapi.com";
SSLContext sslcontext = SSLContext.getInstance("TLS");
sslcontext.init(null, null, null);
try {
SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(sslcontext);
CloseableHttpClient client = HttpClients.custom().setSSLSocketFactory(socketFactory).build();
HttpGet httpget = new HttpGet(testURL);
CloseableHttpResponse response = client.execute(httpget);
System.out.println("Response Code (Apache): " + response.getStatusLine().getStatusCode());
}
catch (Exception e){
System.err.println("Apache HTTP Client Failed");
e.printStackTrace();
}
try {
HttpsURLConnection urlConnection = (HttpsURLConnection) new URL(testURL).openConnection();
urlConnection.setSSLSocketFactory(sslcontext.getSocketFactory());
urlConnection.connect();
System.out.println("Response Code (URLConnection): " + urlConnection.getResponseCode());
}
catch (Exception e){
System.err.println("HttpsURLConnection Failed");
e.printStackTrace();
}
}
Along with the -Dhttps.protocols=TLSv1.2 I've tried the -Djdk.tls.client.protocols=TLSv1.2 and the -Ddeployment.security.TLSv1.2=true JVM parameters without any luck.
Does anyone have thoughts to how to enable TLSv1.2 in this configuration without upgrading to v8 or changing the application to specifically request an instance of TLSv1.2?
jdk.tls.client.protocols only works on Java 8 (and presumably 9) which you aren't using.
https.protocols only works by default in HttpsURLConnection which httpclient doesn't use.
deployment.* only applies to JNLP and applets (if any browser still permits applets) which you aren't using.
An answer to your Q as stated, at least for 4.5, assuming you use HttpClientBuilder or HttpClients (which you didn't say), is to use .useSystemProperties() or .createSystem(), respectively; these do use the same system properties as *URLConnection -- or at least many of them including https.protocols. You should check none of the other properties included in this set is configured to do something you don't want. This does require changing the apps, but not changing them 'to specifically request ... TLSv1.2'.
Other than that you can configure the SSLConnectionSocketFactory to specify the exact protocols allowed as in the Q linked by #pvg, or SSLContexts.custom().useProtocol(String).build() to specify the upper bound -- which is enough for your case because offering the range 'up to 1.2' to a server that requires 1.2 will select 1.2.
Here is the recommended way of configuring Apache HttpClient 4.x to use a specific TLS/SSL version
CloseableHttpClient client = HttpClientBuilder.create()
.setSSLSocketFactory(new SSLConnectionSocketFactory(SSLContext.getDefault(), new String[] { "TLSv1.2" }, null, SSLConnectionSocketFactory.getDefaultHostnameVerifier()))
.build();
Vote up to dave_thompson_085's answer

Netty client does not send client certificate during SSL handshake that requires mutual authentication

I'm new to Netty and I try to write an echo server and client that uses mutual authentication. Unfortunately, it's not working, the client doesn't send its client certificate and the server disconnects as expected. Below an overview of what I've done so far and the client side code - that probably contains some bug or I missed something important. Thanks for going through all this!
That is what I have:
Netty version 4.1.0.CR1
Valid keystores, truststores and CRL for download on server
A complete implementation of echo server and client using JSSE directly (that is working as expected)
A working implementation of the echo server using Netty (it's working fine when used with the JSSE based client)
A client based on Netty that does not send a client certificate
Client code:
The channel handler:
package info.junius.tutorial.echo.netty.tls;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandler.Sharable;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.util.CharsetUtil;
public class EchoClientHandler extends SimpleChannelInboundHandler<ByteBuf>
{
#Override
public void channelRead0(ChannelHandlerContext ctx, ByteBuf in)
{
System.out.println("CLIENT: Received echo from server:\n" + in.toString(CharsetUtil.UTF_8));
}
#Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
{
cause.printStackTrace();
ctx.close();
}
}
The channel initialiser:
package info.junius.tutorial.echo.netty.tls;
import io.netty.channel.Channel;
import io.netty.channel.ChannelInitializer;
import io.netty.handler.ssl.SslContext;
public class ClientChannelInitializer extends ChannelInitializer<Channel>
{
private final SslContext context;
private final String peerHost;
private final int peerPort;
public ClientChannelInitializer(SslContext context, String peerHost, int peerPort)
{
this.context = context;
this.peerHost = peerHost;
this.peerPort = peerPort;
}
#Override
protected void initChannel(Channel channel) throws Exception
{
// Add SSL handler first to encrypt and decrypt everything.
channel.pipeline().addLast(this.context.newHandler(channel.alloc(), this.peerHost, this.peerPort));
// and then business logic.
channel.pipeline().addLast(new EchoClientHandler());
}
}
The echo client:
package info.junius.tutorial.echo.netty.tls;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
public class EchoClient
{
private final String host;
private final int port;
public EchoClient(String host, int port)
{
super();
this.host = host;
this.port = port;
}
public static void main(String[] args) throws Exception
{
if (args.length != 2)
{
System.err.println("Usage: " + EchoClient.class.getSimpleName() + " <host> <port>");
}
else
{
// Security.addProvider(new BouncyCastleProvider());
String host = args[0];
int port = Integer.parseInt(args[1]);
new EchoClient(host, port).start();
}
}
public void start() throws Exception
{
TlsContextUtil tlsContextUtil = new TlsContextUtil();
ChannelInitializer<Channel> channelInitializer = new ClientChannelInitializer(tlsContextUtil.getClientContext(), this.host, this.port);
EventLoopGroup group = new NioEventLoopGroup();
try
{
Bootstrap b = new Bootstrap();
b.group(group).channel(NioSocketChannel.class).handler(channelInitializer);
Channel channel = b.connect(this.host, this.port).sync().channel();
ChannelFuture writeFuture = channel.writeAndFlush("Hello from netty client!\n");
// channel.closeFuture().sync();
writeFuture.sync();
}
finally
{
group.shutdownGracefully().sync();
}
}
}
And a utility class that returns an SslContext:
...
public SslContext getClientContext() throws IOException
{
SslContext sslContext = null;
try
{
// truststore
TrustManagerFactory tmf = TrustManagerFactory.getInstance("PKIX", "SunJSSE");
tmf.init(this.getKeystore(TRUSTSTORE));
// keystore holding client certificate
KeyManagerFactory kmf = KeyManagerFactory.getInstance("PKIX", "SunJSSE");
kmf.init(this.getKeystore(CLIENT_KEYSTORE), KEYSTORE_PW);
SslContextBuilder builder = SslContextBuilder.forClient().keyManager(kmf).trustManager(tmf).ciphers(PFS_CIPHERS);
// build context
sslContext = builder.build();
}
catch (NoSuchAlgorithmException
| NoSuchProviderException
| KeyStoreException
| IllegalStateException
| UnrecoverableKeyException e)
{
throw new IOException("Unable to create client TLS context", e);
}
return sslContext;
}
...
VM arguments:
-Djavax.net.debug=all -Djava.security.debug="certpath crl" -Dcom.sun.net.ssl.checkRevocation=true -Dcom.sun.security.enableCRLDP=true
I'm quite confident that my mistake must be in the Netty client code, because the system works fine when using JSSE only. Any help is highly appreciated!
Cheers,
Andy
OK, I've got it to work. It was actually my client code that was wrong (the code was based on the secure chat example that comes with Netty). So I changed it to the version used in the echo example:
EchoClientHandler:
#Override
public void channelActive(ChannelHandlerContext ctx)
{
// When notified that the channel is active send a message.
System.out.println("CLIENT: Sending request to server...");
ctx.writeAndFlush(Unpooled.copiedBuffer("Mein Schnitzel ist kaputt!\n", CharsetUtil.UTF_8));
}
and the EchoClient:
try
{
Bootstrap b = new Bootstrap();
b.group(group).channel(NioSocketChannel.class).handler(channelInitializer);
ChannelFuture f = b.connect(this.host, this.port).sync();
f.channel().closeFuture().sync();
}
finally
{
group.shutdownGracefully().sync();
}
The previous code just disconnected too early, so that the handshake never completed.

Unable to tunnel through proxy. Proxy returns "HTTP/1.1 407" via https

I try to connect to a server via https that requires authentication.Moreover, I have an http proxy in the middle that also requires authentication. I use ProxyAuthSecurityHandler to authenticate with the proxy and BasicAuthSecurityHandler to authenticate with the server.
Receiving java.io.IOException: Unable to tunnel through proxy.
Proxy returns "HTTP/1.1 407 Proxy Auth Required"
at sun.net.www.protocol.http.HttpURLConnection.doTunneling(HttpURLConnection.java:1525)
at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect (AbstractDelegateHttpsURLConnection.java:164)
at sun.net.www.protocol.https.HttpsURLConnectionImpl.connect(HttpsURLConnectionImpl.java:133)
at org.apache.wink.client.internal.handlers.HttpURLConnectionHandler.processRequest(HttpURLConnectionHandler.java:97)
I noticed that the implementation of ProxyAuthSecurityHandler is expecting response code 407 however, during debug we never get to the second part due to the IOException thrown.
Code snap:
ClientConfig configuration = new ClientConfig();
configuration.connectTimeout(timeout);
MyBasicAuthenticationSecurityHandler basicAuthProps = new MyBasicAuthenticationSecurityHandler();
basicAuthProps.setUserName(user);
basicAuthProps.setPassword(password);
configuration.handlers(basicAuthProps);
if ("true".equals(System.getProperty("setProxy"))) {
configuration.proxyHost(proxyHost);
if ((proxyPort != null) && !proxyPort.equals("")) {
configuration.proxyPort(Integer.parseInt(proxyPort));
}
MyProxyAuthSecurityHandler proxyAuthSecHandler =
new MyProxyAuthSecurityHandler();
proxyAuthSecHandler.setUserName(proxyUser);
proxyAuthSecHandler.setPassword(proxyPass);
configuration.handlers(proxyAuthSecHandler);
}
restClient = new RestClient(configuration);
// create the createResourceWithSessionCookies instance to interact with
Resource resource = getResource(loginUrl);
// Request body is empty
ClientResponse response = resource.post(null);
Tried using wink client versions 1.1.2 and also 1.2.1. the issue repeats in both.
What I found out is that when trying to pass through a proxy using https url we first send CONNECT and only then try to send the request. The proxy server cannot read any headrs we attach to the request, cause it doesn't have the key to decrypt the traffic.
This means that the CONNECT should already have the user/pass to the proxy to pass this stage.
here is a code snap I used - that works for me:
import sun.misc.BASE64Encoder;
import java.io.*;
import java.net.*;
public class ProxyPass {
public ProxyPass(String proxyHost, int proxyPort, final String userid, final String password, String url) {
try {
/* Create a HttpURLConnection Object and set the properties */
URL u = new URL(url);
Proxy proxy =
new Proxy(Proxy.Type.HTTP, new InetSocketAddress(proxyHost, proxyPort));
HttpURLConnection uc = (HttpURLConnection)u.openConnection(proxy);
Authenticator.setDefault(new Authenticator() {
#Override
protected PasswordAuthentication getPasswordAuthentication() {
if (getRequestorType().equals(RequestorType.PROXY)) {
return new PasswordAuthentication(userid, password.toCharArray());
}
return super.getPasswordAuthentication();
}
});
uc.connect();
/* Print the content of the url to the console. */
showContent(uc);
} catch (IOException e) {
e.printStackTrace();
}
}
private void showContent(HttpURLConnection uc) throws IOException {
InputStream i = uc.getInputStream();
char c;
InputStreamReader isr = new InputStreamReader(i);
BufferedReader br = new BufferedReader(isr);
String line;
while ((line = br.readLine()) != null) {
System.out.println(line);
}
}
public static void main(String[] args) {
String proxyhost = "proxy host";
int proxyport = port;
String proxylogin = "proxy username";
String proxypass = "proxy password";
String url = "https://....";
new ProxyPass(proxyhost, proxyport, proxylogin, proxypass, url);
}
}
if you are using wink - like I do, you need to set the proxy in the ClientConfig and before passing it to the RestClient set the default authenticator.
ClientConfig configuration = new ClientConfig();
configuration.connectTimeout(timeout);
BasicAuthenticationSecurityHandler basicAuthProps = new BasicAuthenticationSecurityHandler();
basicAuthProps.setUserName(user);
basicAuthProps.setPassword(password);
configuration.handlers(basicAuthProps);
if (proxySet()) {
configuration.proxyHost(proxyHost);
if ((proxyPort != null) && !proxyPort.equals("")) {
configuration.proxyPort(Integer.parseInt(proxyPort));
}
Authenticator.setDefault(new Authenticator() {
#Override
protected PasswordAuthentication getPasswordAuthentication() {
if (getRequestorType().equals(RequestorType.PROXY)) {
return new PasswordAuthentication(proxyUser), proxyPass.toCharArray());
}
return super.getPasswordAuthentication();
}
});
}
restClient = new RestClient(configuration);
Resource resource = getResource(loginUrl);
// Request body is empty
ClientResponse response = resource.post(null);
if (response.getStatusCode() != Response.Status.OK.getStatusCode()) {
throw new RestClientException("Authentication failed for user " + user);
}
If Ilana Platonov's answer doesn't work, try editing the variables :
jdk.http.auth.tunneling.disabledSchemes
jdk.http.auth.proxying.disabledSchemes