I'm really trying to better understand how a CONNECT HTTP request process is handled. I'm stuck at this point in my HttpServer that I'm building and was hoping others can help give me incite on how I should approach these next challenges. A little info on my code thus far . I have a class HTTPServer listening on a socket on port 8080 (initially its a non SSL socket). I have a a class called DefaultHttpRequestHandler that holds an instance of HTTPClient that handles all requests needing to be made by the server and a worker thread inside HttpServer handles dispatching all requests sent by browser to port 8080.
My question is the following:
When the CONNECT request comes in and is sent to DefaultHttpRequestHandler it gets passed to the handle(HttpRequest request, HttpResponse response,HttpContext context) method. At this point I peek at the request and if I see it is a CONNECT what next? I was thinking I then establish the SSL socket connection on port 8080 which was before a normal socket? or do I always hold two sockets one as a standard socket and one as ssl than switch to the ssl one. This part is really frustrating me very confused how to code this sucker!
DefaultHttpServer.java - the server
public class DefaultHttpServer {
public static void main(String[] args) throws Exception {
Thread t = new RequestListenerThread(8080);
t.setDaemon(false);
t.start();
//send a request to proxy server for testing
testSendReqFromClient() ;
}
public static void testSendReqFromClient() throws Exception
{
SSLContext sslCtx = SSLContext.getInstance("TLS");
// sslCtx.init(null,new TrustManager[] { new EasyX509TrustManager() }, null);
sslCtx.init(null, new TrustManager[] { new X509TrustManager() {
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
System.out.println("getAcceptedIssuers =============");
return null;
}
public void checkClientTrusted(X509Certificate[] certs,
String authType) {
System.out.println("checkClientTrusted =============");
}
public void checkServerTrusted(X509Certificate[] certs,
String authType) {
System.out.println("checkServerTrusted =============");
}
#Override
public void checkClientTrusted(
java.security.cert.X509Certificate[] arg0,
String arg1) throws CertificateException {
// TODO Auto-generated method stub
}
#Override
public void checkServerTrusted(
java.security.cert.X509Certificate[] arg0,
String arg1) throws CertificateException {
// TODO Auto-generated method stub
}
} }, new SecureRandom());
Thread.sleep(5000);
SSLSocketFactory sf = new SSLSocketFactory(sslCtx, SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
Scheme https = new Scheme("https", 443, sf);
SchemeRegistry schemeRegistry = new SchemeRegistry();
schemeRegistry.register(https);
Scheme http = new Scheme("http", 80, PlainSocketFactory.getSocketFactory());
schemeRegistry.register(http);
BasicHttpRequest req = new BasicHttpRequest("GET","https://www.yahoo.com");
ThreadSafeClientConnManager tm = new ThreadSafeClientConnManager(schemeRegistry);
HttpClient httpClient = new DefaultHttpClient(tm);
ConnRouteParams.setDefaultProxy(req.getParams(), new HttpHost("localhost",8080,"http"));
httpClient.execute(new RequestWrapper(req));
}
}
DefaultRequestHandler.java - the client that sends requests to server from my proxy server
public class DefaultHttpRequestHandler implements HttpRequestHandler {
private static String sslType = "TLS";
private HttpClient httpClient = null;
private ThreadSafeClientConnManager tm;
public DefaultHttpRequestHandler() {
super();
init();
}
private void init() {
try {
SSLContext sslCtx = SSLContext.getInstance(sslType);
// sslCtx.init(null,new TrustManager[] { new EasyX509TrustManager() }, null);
sslCtx.init(null, new TrustManager[] { new X509TrustManager() {
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
System.out.println("getAcceptedIssuers =============");
return null;
}
public void checkClientTrusted(X509Certificate[] certs,
String authType) {
System.out.println("checkClientTrusted =============");
}
public void checkServerTrusted(X509Certificate[] certs,
String authType) {
System.out.println("checkServerTrusted =============");
}
#Override
public void checkClientTrusted(
java.security.cert.X509Certificate[] arg0,
String arg1) throws CertificateException {
// TODO Auto-generated method stub
}
#Override
public void checkServerTrusted(
java.security.cert.X509Certificate[] arg0,
String arg1) throws CertificateException {
// TODO Auto-generated method stub
}
} }, new SecureRandom());
SSLSocketFactory sf = new SSLSocketFactory(sslCtx, SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
Scheme https = new Scheme("https", 443, sf);
SchemeRegistry schemeRegistry = new SchemeRegistry();
schemeRegistry.register(https);
Scheme http = new Scheme("http", 80, PlainSocketFactory.getSocketFactory());
schemeRegistry.register(http);
tm = new ThreadSafeClientConnManager(schemeRegistry);
//httpClient = new ContentEncodingHttpClient(tm);
httpClient = new DefaultHttpClient(tm);
httpClient.getParams().setParameter(ClientPNames.ALLOW_CIRCULAR_REDIRECTS, true);
//httpClient.getConnectionManager().getSchemeRegistry() .register(https);
} catch (Exception e) {
System.err.println(e.getMessage());
e.printStackTrace();
}
}
public void handle(HttpRequest request, HttpResponse response,
HttpContext context) throws HttpException, IOException {
System.out.println(request);
RequestLine reqLine = request.getRequestLine();
if(reqLine.getMethod().equalsIgnoreCase("CONNECT"))
{
response.setEntity(new BufferedHttpEntity(new StringEntity("HTTP/1.0 200 Connection established\r\nProxy-agent: proxy client\r\n\r\n")));
//do i switch the socket to sslsocketconnection in defaulthttpserver here?
}
else
{
try {
HttpResponse clientResponse = null;
HttpEntity entity = null;
clientResponse = httpClient.execute(new RequestWrapper(request));
entity = clientResponse.getEntity();
if (entity != null) {
response.setEntity(new BufferedHttpEntity(entity));
}
} catch (Exception e) {
System.err.println(e.getMessage());
e.printStackTrace();
}
}
}
}
RequestListenerThread - This is run method inside my httpserver that handles dispatching requests
class RequestListenerThread extends Thread {
private static ServerSocket sslServersocket = null;
private static ServerSocket serversocket = null;
static ServerSocketFactory ssocketFactory = null;
private final HttpParams params;
private final HttpService httpService;
Selector selector ;
public RequestListenerThread(int port) throws Exception {
KeyStore ks = KeyStore.getInstance("JKS");
ks.load(new FileInputStream("privateKey2.store"), "whitehatsec123".toCharArray());
KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
kmf.init(ks, "whitehatsec123".toCharArray());
SSLContext context = SSLContext.getInstance("TLS");
context.init(kmf.getKeyManagers(), null, null);
ssocketFactory = context.getServerSocketFactory();
//serversocket = ssocketFactory.createServerSocket(port);
serversocket = new ServerSocket(port);
this.params = new SyncBasicHttpParams();
this.params.setBooleanParameter(ClientPNames.HANDLE_REDIRECTS, true).setIntParameter(CoreConnectionPNames.SO_TIMEOUT, 50000)
.setIntParameter(CoreConnectionPNames.SOCKET_BUFFER_SIZE,
8 * 1024)
.setBooleanParameter(
CoreConnectionPNames.STALE_CONNECTION_CHECK, false)
.setBooleanParameter(CoreConnectionPNames.TCP_NODELAY, true)
.setParameter(CoreProtocolPNames.ORIGIN_SERVER,
"HttpComponents/1.1");
// Set up the HTTP protocol processor
HttpProcessor httpproc = new ImmutableHttpProcessor(
new HttpResponseInterceptor[] { new ResponseDate(),
new ResponseServer(), new ResponseContent(),
new ResponseConnControl() });
// Set up request handlers
HttpRequestHandlerRegistry reqistry = new HttpRequestHandlerRegistry();
reqistry.register("*", new DefaultHttpRequestHandler());
// Set up the HTTP service
this.httpService = new HttpService(httpproc,
new DefaultConnectionReuseStrategy(),
new DefaultHttpResponseFactory(), reqistry, this.params);
}
public void run()
{
System.out.println("Listening on port "
+ serversocket.getLocalPort());
while (!Thread.interrupted())
{
try
{
// Set up HTTP connection
Socket socket = serversocket.accept();
DefaultHttpServerConnection conn = new DefaultHttpServerConnection();
System.out.println("Incoming connection from "
+ socket.getInetAddress());
conn.bind(socket, this.params);
// Start worker thread
Thread t = new WorkerThread(this.httpService, conn);
t.setDaemon(true);
t.start();
} catch (InterruptedIOException ex) {
break;
} catch (IOException ex) {
System.err
.println("I/O error initialising connection thread: "
+ ex.getMessage());
ex.printStackTrace();
break;
}
}
}
}
class WorkerThread extends Thread {
private final HttpService httpservice;
private final HttpServerConnection conn;
public WorkerThread(final HttpService httpservice,
final HttpServerConnection conn) {
super();
this.httpservice = httpservice;
this.conn = conn;
}
public void run() {
System.out.println("New connection thread");
HttpContext context = new BasicHttpContext(null);
try {
while (!Thread.interrupted() && this.conn.isOpen()) {
this.httpservice.handleRequest(this.conn, context);
}
} catch (ConnectionClosedException ex) {
System.err.println("Client closed connection");
} catch (IOException ex) {
System.err.println("I/O error: " + ex.getMessage());
ex.printStackTrace();
} catch (HttpException ex) {
System.err.println("Unrecoverable HTTP protocol violation: "
+ ex.getMessage());
} finally {
try {
this.conn.shutdown();
} catch (IOException ignore) {
}
}
}
}
A proxy receiving a CONNECT request (and accepting it) doesn't do any SSL/TLS initialisation or processing (if it did, it would be a potential MITM attacker). It merely relays all the traffic between the target HTTPS host and the initial client back and forth.
More detailed in these answers perhaps:
SSL (https) error on my custom proxy server
CONNECT request to a forward HTTP proxy over an SSL connection?
What you would need is to be able to get hold of the underlying socket (or input/output streams) and write every byte you read on the other side.
Related
I'm trying to replace the following deprecated (e.g. Apache DefaultHttpClient, SSLSocketFactory are deprecated) code:
public class HttpUtil {
public static DefaultHttpClient getDefaultHttpClient(IClientConfiguration configuration,
ExternalAPILogger logger) {
DefaultHttpClient client = new DefaultHttpClient();
if(configuration.isIgnoreSSLCertificate()) {
try {
SSLContext context = SSLContext.getInstance("TLS");
X509TrustManager trustManager = new X509TrustManager() {
public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
}
public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
}
public X509Certificate[] getAcceptedIssuers() {
return null;
}
};
context.init(null, new TrustManager[]{trustManager}, null);
SSLSocketFactory sslSocketFactory = new SSLSocketFactory(context);
sslSocketFactory.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
ClientConnectionManager connectionManager = client.getConnectionManager();
SchemeRegistry schemeRegistry = connectionManager.getSchemeRegistry();
schemeRegistry.register(new Scheme("https", sslSocketFactory, 443));
client = new DefaultHttpClient(connectionManager, client.getParams());
} catch (NoSuchAlgorithmException e) {
logger.log(HttpUtil.class.getName(), "TLS not available", e, ExternalAPILogger.DEBUG);
} catch(KeyManagementException e) {
logger.log(HttpUtil.class, "ssl context init failed", e, ExternalAPILogger.DEBUG);
}
}
if(configuration.isUseProxy()) {
HttpHost proxy = new HttpHost(configuration.getProxyHost(),
configuration.getProxyPort());
ConnRouteParams.setDefaultProxy(client.getParams(), proxy);
}
client.getParams().setParameter(CoreConnectionPNames.SO_TIMEOUT, 60 * 1000);
client.getParams().setParameter(CoreConnectionPNames.CONNECTION_TIMEOUT, 60 * 1000);
return client;
}
}
by this new code:
public static HttpClient getHttpClient(IClientConfiguration configuration,
ExternalAPILogger logger) {
HttpClientBuilder clientBuilder = HttpClientBuilder.create();
// clientBuilder.useSystemProperties();
if (configuration.isIgnoreSSLCertificate()) {
try {
SSLContext context = SSLContext.getInstance("TLS");
X509TrustManager trustManager = new X509TrustManager() {
public void checkClientTrusted(X509Certificate[] x509Certificates, String s)
throws CertificateException {
}
public void checkServerTrusted(X509Certificate[] x509Certificates, String s)
throws CertificateException {
}
public X509Certificate[] getAcceptedIssuers() {
return null;
}
};
context.init(null, new TrustManager[] { trustManager }, null);
// clientBuilder.setSSLContext(context);
// clientBuilder.setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE);
SSLConnectionSocketFactory sslSocketFactory =
new SSLConnectionSocketFactory(context, NoopHostnameVerifier.INSTANCE);
clientBuilder.setSSLSocketFactory(sslSocketFactory);
Registry<ConnectionSocketFactory> schemeRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
.register("https", sslSocketFactory).build();
HttpClientConnectionManager httpClientConnectionManager =
new BasicHttpClientConnectionManager(schemeRegistry);
clientBuilder.setConnectionManager(httpClientConnectionManager);
} catch (NoSuchAlgorithmException e) {
logger.log(HttpUtil.class.getName(), "TLS not available", e, ExternalAPILogger.DEBUG);
} catch (KeyManagementException e) {
logger.log(HttpUtil.class, "ssl context init failed", e, ExternalAPILogger.DEBUG);
}
}
RequestConfig.Builder requestConfigBuilder = RequestConfig.custom();
if (configuration.isUseProxy()) {
HttpHost proxy = new HttpHost(
configuration.getProxyHost(),
configuration.getProxyPort());
// clientBuilder
// .setRoutePlanner(new DefaultProxyRoutePlanner(proxy))
// .setProxy(proxy)
// .setProxyAuthenticationStrategy(new ProxyAuthenticationStrategy());
requestConfigBuilder.setProxy(proxy);
}
requestConfigBuilder
.setConnectTimeout(60 * 1000)
.setSocketTimeout(60 * 1000);
clientBuilder.setDefaultRequestConfig(requestConfigBuilder.build());
// SocketConfig.Builder socketConfig = SocketConfig.custom();
// socketConfig.setSoTimeout(60 * 1000);
// clientBuilder.setDefaultSocketConfig(socketConfig.build());
return clientBuilder.build();
}
but I have problems to get the code running using a http proxy. I get the following error message.:
Caused by: org.apache.http.conn.UnsupportedSchemeException: http protocol is not supported
I use RestTemplate config like this :
private RestTemplate createRestTemplate() throws Exception {
final String username = "admin";
final String password = "admin";
final String proxyUrl = "localhost";
final int port = 443;
CredentialsProvider credsProvider = new BasicCredentialsProvider();
credsProvider.setCredentials(new AuthScope(proxyUrl, port),
new UsernamePasswordCredentials(username, password));
HttpHost host = new HttpHost(proxyUrl, port, "https");
HttpClientBuilder clientBuilder = HttpClientBuilder.create();
clientBuilder.setProxy(host).setDefaultCredentialsProvider(credsProvider).disableCookieManagement();
HttpClient httpClient = clientBuilder.build();
HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory();
factory.setHttpClient(httpClient);
return new RestTemplate(factory);
}
And the this is how my method work:
public String receiveMessage(String message) {
try {
restTemplate = createRestTemplate();
ObjectMapper mapper = new ObjectMapper();
Class1 class1 = null;
String json2 = "";
class1= mapper.readValue(message, Class1.class);
Class1 class2 = restTemplate.getForObject(URL_SERVICE_1 + "/class1/findByName?name=" + class1.getName(),
Class1.class);
System.out.println("Server 1 : " + message);
json2 = mapper.writeValueAsString(class2);
return "Error - " + json2;
} catch (Exception e) {
// TODO Auto-generated catch block
return e.getMessage();
}
}
URL_SERVICE_1 contains https://localhost
When I tried to call function GET, I always get return like this :
I/O error on GET request for "https://localhost/class1/findByName?name=20-1P": Host name 'localhost' does not match the certificate subject provided by the peer (CN=*.webku-cool.com, OU=EssentialSSL Wildcard, OU=Domain Control Validated); nested exception is javax.net.ssl.SSLPeerUnverifiedException: Host name 'localhost' does not match the certificate subject provided by the peer (CN=*.webku-cool.com, OU=EssentialSSL Wildcard, OU=Domain Control Validated)
I don't know the correct setting for restTemplate with https. I already tried 23 references about SSL Settings and got same error.
1. Reference
2. Reference
3. Reference
As accepted answer has deprecated code, this is what I found helpful:
SSLContextBuilder sslcontext = new SSLContextBuilder();
sslcontext.loadTrustMaterial(null, new TrustSelfSignedStrategy());
httpclient = HttpAsyncClients.custom().setSSLContext(sslcontext.build()).setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE)
.build();
The correct solution for this problem is to correct the ssl certificate by adding localhost to the list of subjects. However, if your intent is to bypass ssl for development purpose, you would need to define a connection factory which always returns the result of hostname verification as true.
SSLClientHttpRequestFactory
public class SSLClientHttpRequestFactory extends SimpleClientHttpRequestFactory {
#Override
protected void prepareConnection(HttpURLConnection connection, String httpMethod) {
try {
if (!(connection instanceof HttpsURLConnection)) {
throw new RuntimeException("An instance of HttpsURLConnection is expected");
}
HttpsURLConnection httpsConnection = (HttpsURLConnection) connection;
TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() {
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return null;
}
public void checkClientTrusted(X509Certificate[] certs, String authType) {
}
public void checkServerTrusted(X509Certificate[] certs, String authType) {
}
} };
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, trustAllCerts, new java.security.SecureRandom());
httpsConnection.setSSLSocketFactory(new MyCustomSSLSocketFactory(sslContext.getSocketFactory()));
httpsConnection.setHostnameVerifier(new HostnameVerifier() {
public boolean verify(String hostname, SSLSession session) {
return true;
}
});
super.prepareConnection(httpsConnection, httpMethod);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* We need to invoke sslSocket.setEnabledProtocols(new String[] {"SSLv3"});
* see
* http://www.oracle.com/technetwork/java/javase/documentation/cve-2014-3566
* -2342133.html (Java 8 section)
*/
private static class MyCustomSSLSocketFactory extends SSLSocketFactory {
private final SSLSocketFactory delegate;
public MyCustomSSLSocketFactory(SSLSocketFactory delegate) {
this.delegate = delegate;
}
#Override
public String[] getDefaultCipherSuites() {
return delegate.getDefaultCipherSuites();
}
#Override
public String[] getSupportedCipherSuites() {
return delegate.getSupportedCipherSuites();
}
#Override
public Socket createSocket(final Socket socket, final String host, final int port, final boolean autoClose)
throws IOException {
final Socket underlyingSocket = delegate.createSocket(socket, host, port, autoClose);
return overrideProtocol(underlyingSocket);
}
#Override
public Socket createSocket(final String host, final int port) throws IOException {
final Socket underlyingSocket = delegate.createSocket(host, port);
return overrideProtocol(underlyingSocket);
}
#Override
public Socket createSocket(final String host, final int port, final InetAddress localAddress,
final int localPort) throws IOException {
final Socket underlyingSocket = delegate.createSocket(host, port, localAddress, localPort);
return overrideProtocol(underlyingSocket);
}
#Override
public Socket createSocket(final InetAddress host, final int port) throws IOException {
final Socket underlyingSocket = delegate.createSocket(host, port);
return overrideProtocol(underlyingSocket);
}
#Override
public Socket createSocket(final InetAddress host, final int port, final InetAddress localAddress,
final int localPort) throws IOException {
final Socket underlyingSocket = delegate.createSocket(host, port, localAddress, localPort);
return overrideProtocol(underlyingSocket);
}
private Socket overrideProtocol(final Socket socket) {
if (!(socket instanceof SSLSocket)) {
throw new RuntimeException("An instance of SSLSocket is expected");
}
((SSLSocket) socket).setEnabledProtocols(new String[] { "TLSv1" });
return socket;
}
}
}
And use the above mentioned connection factory as the constructor argument for RestTemplate. The part of the code which overrides the host name verification to always return true is as follows:
httpsConnection.setHostnameVerifier(new HostnameVerifier() {
public boolean verify(String hostname, SSLSession session) {
return true;
}
});
Happy coding!
Here is how I made it to work:
1. This bean ignores SSL check
2. It also ignores certificate mismatch
#Bean
public RestTemplate restTemplate()
throws KeyStoreException, NoSuchAlgorithmException, KeyManagementException {
TrustStrategy acceptingTrustStrategy = (X509Certificate[] chain, String authType) -> true;
SSLContextBuilder sslcontext = new SSLContextBuilder();
sslcontext.loadTrustMaterial(null, new TrustSelfSignedStrategy());
SSLContext sslContext = org.apache.http.ssl.SSLContexts.custom()
.loadTrustMaterial(null, acceptingTrustStrategy)
.build();
SSLConnectionSocketFactory csf = new SSLConnectionSocketFactory(sslContext);
CloseableHttpClient httpClient = HttpClients.custom().setSSLContext(sslcontext.build()).setSSLHostnameVerifier(
NoopHostnameVerifier.INSTANCE)
.build();
HttpComponentsClientHttpRequestFactory requestFactory =
new HttpComponentsClientHttpRequestFactory();
requestFactory.setHttpClient(httpClient);
RestTemplate restTemplate = new RestTemplate(requestFactory);
return restTemplate;
}
This option worked for me after trying lot of different options from online... Thanks a lot ...
SSLContextBuilder sslcontext = new SSLContextBuilder();
sslcontext.loadTrustMaterial(null, new TrustSelfSignedStrategy());
HttpClient httpClient = HttpAsyncClients.custom().setSSLContext(sslcontext.build()).setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE)
.build();
There are several examples out there [1][2] of how to configure HTTPS in Java/Groovy to ignore SSL certificate errors. In short they all create a custom TrustManager, add it to an SSLContext and then install the resulting SocketFactory as the default connection factory for HTTPS connections. And of course they comes with all the requisite warnings about MITM attacks and how dangerous this is.
Indeed in my situation where I am writing a groovy script to be run inside of a Jenkins job, setting the default socket factory is nuts. It would have affects well beyond that of my script. So my question is, how do you accomplish this for a specific connection or specific HTTP client and not for all connections/clients? In other words, how to I localize such a change to just my transient piece of code?
public class BasicHttpClientFactory implements HttpClientFactory {
private String proxyHost;
private Integer proxyPort;
private boolean isSocksProxy = false;
HttpClient httpClient;
final Integer maxConnections = new Integer(10);
private static final Log logger = LogFactory.getLog(BasicHttpClientFactory.class);
#Override
public HttpClient createNewClient() {
SSLConnectionSocketFactory sslsf = null;
try {
SSLContextBuilder builder = SSLContexts.custom();
builder.loadTrustMaterial(null, new TrustStrategy() {
#Override
public boolean isTrusted(X509Certificate[] chain, String authType)
throws CertificateException {
return true;
}
});
SSLContext sslContext = builder.build();
sslsf = new SSLConnectionSocketFactory(
sslContext, new X509HostnameVerifier() {
#Override
public void verify(String host, SSLSocket ssl)
throws IOException {
}
#Override
public void verify(String host, X509Certificate cert)
throws SSLException {
}
#Override
public void verify(String host, String[] cns,
String[] subjectAlts) throws SSLException {
}
#Override
public boolean verify(String s, SSLSession sslSession) {
return true;
}
});
} catch (KeyManagementException e) {
logger.error(e.getMessage(), e);
} catch (NoSuchAlgorithmException e) {
logger.error(e.getMessage(), e);
} catch (KeyStoreException e) {
logger.error(e.getMessage(), e);
}
Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create()
.register("http", new PlainConnectionSocketFactory())
.register("https", sslsf)
.build();
PoolingHttpClientConnectionManager poolingConnManager = new PoolingHttpClientConnectionManager(registry);
poolingConnManager.setMaxTotal(maxConnections);
poolingConnManager.setDefaultMaxPerRoute(maxConnections);
ConnectionKeepAliveStrategy keepAliveStrategy = new ConnectionKeepAliveStrategy() {
#Override
public long getKeepAliveDuration(HttpResponse response, HttpContext context) {
return 60 * 1000;
}
};
if (proxyHost != null) {
HttpHost proxy = new HttpHost(proxyHost, proxyPort);
httpClient = HttpClients.custom().setSSLSocketFactory(sslsf).setProxy(proxy).setConnectionManager(poolingConnManager).setKeepAliveStrategy(keepAliveStrategy).build();
}else {
httpClient = HttpClients.custom().setSSLSocketFactory(sslsf).setConnectionManager(poolingConnManager).setKeepAliveStrategy(keepAliveStrategy).build();
}
return httpClient;
}
public void setProxyHost(String proxyHost) {
this.proxyHost = proxyHost;
}
public void setProxyPort(Integer proxyPort) {
this.proxyPort = proxyPort;
}
public void setSocksProxy(boolean isSocksProxy) {
this.isSocksProxy = isSocksProxy;
}
}
And interface :
import org.apache.http.client.HttpClient;
public interface HttpClientFactory {
public HttpClient createNewClient();
}
After that You could use :
HttpClient httpClient = new BasicHttpClientFactory().createNewClient();
If You need any ideas how to merge it into Your project, just post some info - maybe i'll come up with some ideas ;)
I am trying to interact with a webservice which is a HTTPS call that works totally fine on different variants of 4.0(I havent checked it below 4.0 so I cant say about them) and its perfectly working. The issue I am facing is on Android 5.0 and the device I was able to grab was Nexus 5 and below is the exception i get when doing connectivity
javax.net.ssl.SSLPeerUnverifiedException: No peer certificate
at org.apache.harmony.xnet.provider.jsse.SSLSessionImpl.getPeerCertificates(SSLSessionImpl.java:146)
at org.apache.http.conn.ssl.AbstractVerifier.verify(AbstractVerifier.java:93)
After tonnes of searching and analyzing our production server SSL certificate i figured out that the server accept TLSv1 and the only cipher suite it supports is TLS_RSA_WITH_3DES_EDE_CBC_SHA. Though i understand that its not safe and it should be upgraded but right now i have to find out some way to get my Android app connected with the server.
I tried through the way suggested on this page
https://code.google.com/p/android-developer-preview/issues/attachmentText?id=1200&aid=12000009000&name=CompatSSLSocketFactory.java&token=ABZ6GAcWKpRZhuG6Skof32VtvF0Lzv3Z-A%3A1435550700632
And replaced my required algorithm i.e TLS_RSA_WITH_3DES_EDE_CBC_SHA but now the problem is that i am seeing this exception
Caused by: java.lang.IllegalArgumentException: cipherSuite
TLS_RSA_WITH_3DES_EDE_CBC_SHA is not supported.
at com.android.org.conscrypt.NativeCrypto.checkEnabledCipherSuites(NativeCrypto.java:1091)
at com.android.org.conscrypt.SSLParametersImpl.setEnabledCipherSuites(SSLParametersImpl.java:244)
at com.android.org.conscrypt.OpenSSLSocketImpl.setEnabledCipherSuites(OpenSSLSocketImpl.java:822)
So according to this exception the cipher suite i required is not supported by Android 5.0. But i got puzzled after seeing it in Android 5.0's supported list on this page
http://developer.android.com/reference/javax/net/ssl/SSLEngine.html
Anybody any idea whats this mystery?
I got the answer finally after working out on the issue for three days. Posting out the correct solution for people who gets stuck in a similar issue in future
First implement CustomTrustManager
public class CustomX509TrustManager implements X509TrustManager {
#Override
public void checkClientTrusted(X509Certificate[] chain, String authType)
throws CertificateException {
}
#Override
public void checkServerTrusted(X509Certificate[] certs,
String authType) throws CertificateException {
// Here you can verify the servers certificate. (e.g. against one which is stored on mobile device)
// InputStream inStream = null;
// try {
// inStream = MeaApplication.loadCertAsInputStream();
// CertificateFactory cf = CertificateFactory.getInstance("X.509");
// X509Certificate ca = (X509Certificate)
// cf.generateCertificate(inStream);
// inStream.close();
//
// for (X509Certificate cert : certs) {
// // Verifing by public key
// cert.verify(ca.getPublicKey());
// }
// } catch (Exception e) {
// throw new IllegalArgumentException("Untrusted Certificate!");
// } finally {
// try {
// inStream.close();
// } catch (IOException e) {
// e.printStackTrace();
// }
// }
}
public X509Certificate[] getAcceptedIssuers() {
return null;
}
}
Than implement your own Socket Factory
public class CustomSSLSocketFactory extends SSLSocketFactory {
SSLContext sslContext = SSLContext.getInstance("TLS");
public CustomSSLSocketFactory(KeyStore truststore)
throws NoSuchAlgorithmException, KeyManagementException,
KeyStoreException, UnrecoverableKeyException {
super(truststore);
TrustManager tm = new CustomX509TrustManager();
sslContext.init(null, new TrustManager[] { tm }, null);
}
public CustomSSLSocketFactory(SSLContext context)
throws KeyManagementException, NoSuchAlgorithmException,
KeyStoreException, UnrecoverableKeyException {
super(null);
sslContext = context;
}
#Override
public Socket createSocket(Socket socket, String host, int port,
boolean autoClose) throws IOException, UnknownHostException {
Socket newSocket = sslContext.getSocketFactory().createSocket(socket, host, port,
autoClose);
((SSLSocket) newSocket).setEnabledCipherSuites(((SSLSocket) newSocket).getSupportedCipherSuites());
AdjustSocket(newSocket);
return newSocket;
}
#Override
public Socket createSocket() throws IOException {
Socket socket = sslContext.getSocketFactory().createSocket();
((SSLSocket) socket).setEnabledCipherSuites(((SSLSocket) socket).getSupportedCipherSuites());
adjustSocket(socket);
return socket;
}
private void adjustSocket(Socket socket)
{
String[] cipherSuites = ((SSLSocket) socket).getSSLParameters().getCipherSuites();
ArrayList<String> cipherSuiteList = new ArrayList<String>(Arrays.asList(cipherSuites));
cipherSuiteList.add("TLS_RSA_WITH_3DES_EDE_CBC_SHA");
cipherSuites = cipherSuiteList.toArray(new String[cipherSuiteList.size()]);
((SSLSocket) socket).getSSLParameters().setCipherSuites(cipherSuites);
String[] protocols = ((SSLSocket) socket).getSSLParameters().getProtocols();
ArrayList<String> protocolList = new ArrayList<String>(Arrays.asList(protocols));
for (int ii = protocolList.size() - 1; ii >= 0; --ii )
{
if ((protocolList.get(ii).contains("SSLv3")) || (protocolList.get(ii).contains("TLSv1.1")) || (protocolList.get(ii).contains("TLSv1.2")))
protocolList.remove(ii);
}
protocols = protocolList.toArray(new String[protocolList.size()]);
((SSLSocket)socket).setEnabledProtocols(protocols);
}
}
Now add a function in the class to create a HttpClient
public HttpClient createHttpClient(){
try {
KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
trustStore.load(null, null);
CustomSSLSocketFactory sf = new CustomSSLSocketFactory(trustStore);
sf.setHostnameVerifier(CustomSSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
HttpParams params = new BasicHttpParams();
HttpConnectionParams.setConnectionTimeout(params, 15000);
HttpConnectionParams.setSoTimeout(params, 5000);
SchemeRegistry registry = new SchemeRegistry();
registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
registry.register(new Scheme("https", sf, 443));
ClientConnectionManager ccm = new ThreadSafeClientConnManager(params, registry);
return new DefaultHttpClient(ccm, params);
} catch (Exception e) {
return new DefaultHttpClient();
}
And now write below lines to call the server/webservice
HttpClient httpClient = createHttpClient();
HttpPost httpost = new HttpPost(url);
HttpResponse response = null;
try {
response = httpClient.execute(httpost);
StatusLine statusLine = response.getStatusLine();
} catch (IOException e) {
e.printStackTrace();
}
I'm trying to connect to a SharePoint url so I used the example code in httpcomponents-client-4.2.1\examples\org\apache\http\examples\client\ClientAuthentication.java but changed it to NTCredentials to ignore certificate problems. Apart from this the code is the same but outputs this:
executing requestGET url HTTP/1.1
----------------------------------------
HTTP/1.1 401 Unauthorized
Response content length: 0
Here is the full code
public class DocumentApprover {
static final String user = "user"; // your account name
static final String pass = "password"; // your password for the account
public static DefaultHttpClient wrapClient(DefaultHttpClient base) {
try {
SSLContext ctx = SSLContext.getInstance("TLS");
X509TrustManager tm = new X509TrustManager() {
public void checkClientTrusted(X509Certificate[] xcs, String string) throws CertificateException {
}
public void checkServerTrusted(X509Certificate[] xcs, String string) throws CertificateException {
}
public X509Certificate[] getAcceptedIssuers() {
return null;
}
};
ctx.init(null, new TrustManager[]{tm}, null);
SSLSocketFactory ssf = new SSLSocketFactory(ctx, SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
ClientConnectionManager ccm = base.getConnectionManager();
SchemeRegistry sr = ccm.getSchemeRegistry();
sr.register(new Scheme("https", 443, ssf));
return new DefaultHttpClient(ccm, base.getParams());
} catch (Exception ex) {
ex.printStackTrace();
return null;
}
}
/**
* #param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
try {
DefaultHttpClient httpclient = wrapClient(new DefaultHttpClient());
try {
httpclient.getCredentialsProvider().setCredentials(
new AuthScope("host", 443),
new NTCredentials(user, pass, "workstation", "domain"));
HttpGet httpget = new HttpGet("url");
System.out.println("executing request" + httpget.getRequestLine());
HttpResponse response = httpclient.execute(httpget);
HttpEntity entity = response.getEntity();
System.out.println("----------------------------------------");
System.out.println(response.getStatusLine());
if (entity != null) {
System.out.println("Response content length: " + entity.getContentLength());
}
EntityUtils.consume(entity);
} finally {
// When HttpClient instance is no longer needed,
// shut down the connection manager to ensure
// immediate deallocation of all system resources
httpclient.getConnectionManager().shutdown();
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
Why is it failing? If the credentials were wrong wouldn't I get a different error?