I have gone to almost all the post related to this exception. Actually my problem is I have an java application through which I am hitting an URL and getting response from it.
code to hit URL is :
HttpGet getRequest = new HttpGet("https://urlto.esb.com");
HttpResponse httpResponse = null;
DefaultHttpClient httpClient = new DefaultHttpClient();
httpResponse = httpClient.execute(getRequest);
Here I am getting javax.net.ssl.SSLPeerUnverifiedException: peer not authenticated
So after some Google search I come to know that I can import certificate in keystore of java where the application is running. so I imported certificate in keystore and this code is working. but i don't want this solution so after some more searching I come to know that I can use TrustManager for the same thing without importing certificate into keystore. So I have written code like:
#Test
public void withTrustManeger() throws Exception {
DefaultHttpClient httpclient = buildhttpClient();
HttpGet httpGet = new HttpGet("https://urlto.esb.com");
HttpResponse response = httpclient.execute( httpGet );
HttpEntity httpEntity = response.getEntity();
InputStream inputStream = httpEntity.getContent();
BufferedReader reader = new BufferedReader(new InputStreamReader(
inputStream));
StringBuilder sb = new StringBuilder();
String line = null;
while ((line = reader.readLine()) != null) {
sb.append(line + "\n");
}
inputStream.close();
String jsonText = sb.toString();
System.out.println(jsonText);
}
private DefaultHttpClient buildhttpClient() throws Exception {
DefaultHttpClient httpclient = new DefaultHttpClient();
SSLContext sc = SSLContext.getInstance("SSL");
sc.init(null, getTrustingManager(), new java.security.SecureRandom());
SSLSocketFactory socketFactory = new SSLSocketFactory(sc);
Scheme sch = new Scheme("https", 443, socketFactory);
httpclient.getConnectionManager().getSchemeRegistry().register(sch);
return httpclient;
}
private TrustManager[] getTrustingManager() {
TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() {
#Override
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return null;
}
#Override
public void checkClientTrusted(X509Certificate[] certs, String authType) {
// Do nothing
}
#Override
public void checkServerTrusted(X509Certificate[] certs, String authType) {
// Do nothing
}
} };
return trustAllCerts;
}
This code is also working but My question is I am not checking anything related to certificates then how connection is trusted. after debugging I come to know that only checkServerTrusted is hitting. So I have write something in checkServerTrusted to validate certificates that come in certs and the one which is in my application like some .cer or .crt file.
Every Help will be appreciated.
Update after #EpicPandaForce (Using Apache HttpClient 4.3)
try
{
keyStore = KeyStore.getInstance("JKS");
InputStream inputStream = new FileInputStream("E:\\Desktop\\esbcert\\keystore.jks");
keyStore.load(inputStream, "key".toCharArray());
SSLContextBuilder sslContextBuilder = SSLContexts.custom().loadTrustMaterial(keyStore, new TrustSelfSignedStrategy());
sslcontext = sslContextBuilder.build();
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext);
HttpClient httpclient = HttpClients.custom().setSSLSocketFactory(sslsf).build();
HttpGet httpGet = new HttpGet("https://url.esb.com");
HttpResponse response = httpclient.execute( httpGet );
HttpEntity httpEntity = response.getEntity();
InputStream httpStram = httpEntity.getContent();
BufferedReader reader = new BufferedReader(new InputStreamReader(
httpStram));
StringBuilder sb = new StringBuilder();
String line = null;
while ((line = reader.readLine()) != null) {
sb.append(line + "\n");
}
httpStram.close();
inputStream.close();
String jsonText = sb.toString();
System.out.println(jsonText);
}
catch(Exception e)
{
System.out.println("Loading keystore failed.");
e.printStackTrace();
}
Technically, seeing as you are using Apache HttpClient 4.x, a simpler solution would be the following:
SSLContext sslcontext = null;
try {
SSLContextBuilder sslContextBuilder = SSLContexts.custom()
.loadTrustMaterial(trustStore, new TrustSelfSignedStrategy());
sslcontext = sslContextBuilder.build();
Where trustStore is initialized like this
KeyStore keyStore = null;
try {
keyStore = KeyStore.getInstance("BKS", BouncyCastleProvider.PROVIDER_NAME); //you can use JKS if that is what you have
InputStream inputStream = new File("pathtoyourkeystore");
try {
keyStore.load(inputStream, "password".toCharArray());
} finally {
inputStream.close();
}
} catch(Exception e) {
System.out.println("Loading keystore failed.");
e.printStackTrace();
}
return keyStore;
}
And then create the HttpClient
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext);
httpclient = HttpClients
.custom()
.setSSLSocketFactory(sslsf).build();
EDIT: Exact code for me was this:
SSLContextBuilder sslContextBuilder = SSLContexts.custom()
.loadTrustMaterial(trustStore, new TrustSelfSignedStrategy());
sslcontext = sslContextBuilder.build();
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
sslcontext, new String[] {"TLSv1"}, null,
SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER
);
httpclient = HttpClients
.custom()
.setHostnameVerifier(SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER)
.setSSLSocketFactory(sslsf).build();
Related
A client developed in Java using JDK 1.6. I am consuming API in the java code. Whenever I hit this API from soapui or from JDK 1.7 it is working perfectly fine but when I tried to hit this API using JDK 1.6, it is returning the error.
com.sun.xml.internal.ws.client.ClientTransportException: HTTP transport error: java.net.SocketException: Connection reset
I have tried by developing client using WSDL and using HTTPSURLConnection, with both mechanism, I am getting the same error. It seems there is nothing wrong with the code. I am unable to find out the way for the resolution.
Wireshark Result:
When I ran the jar from JDK 1.7, I can see the result in Wireshark, the protocol is TSLv1 but when I tried to run the jar from 1.6, the protocol has been changed to SSLv2.
Is it possible to change protocol in the code or on the system where we are calling jar?
Here is my code:
public String myFun(String sender) throws IOException,
NoSuchAlgorithmException, KeyManagementException{
SSLContext sslContext = SSLContext.getInstance("SSL");
TrustManager[] trustManager = getTrustManager();
sslContext.init(null, trustManager, new SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory());
try{
String inquiryRequest = inquiryRequest = "<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:v5=\"http://xxxxxxx\">\n"
+"<soapenv:Header>\n"
+"</soapenv:Header>\n"
+"<soapenv:Body>\n"
+"<v5:single.smsReq>\n"
+"<sender>"+sender+"</sender>\n"
+"</v5:single.smsReq>\n"
+"</soapenv:Body>\n"
+"</soapenv:Envelope>";
URL url =new URL ("https://xxxx:xx/xx");
HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
conn.setRequestMethod("POST");
conn.setRequestProperty("content-type","application/xml");
conn.setRequestProperty("Authorization","Basic xxx");
conn.setDoOutput(true);
DataOutputStream wr = new DataOutputStream(conn.getOutputStream());
wr.writeBytes(inquiryRequest);
wr.flush();
wr.close();
BufferedReader in = new BufferedReader( new InputStreamReader(conn.getInputStream()));
String inputLine;
StringBuilder response = new StringBuilder();
while ((inputLine = in.readLine()) != null) {
response.append(inputLine);
}
in.close();
conn.disconnect();
return response.toString();
}catch(Exception e){
return null;
}
}
private TrustManager[] getTrustManager() {
TrustManager[] certs = new TrustManager[] { new X509TrustManager() {
public X509Certificate[] getAcceptedIssuers() {
return null;
}
public void checkClientTrusted(X509Certificate[] certs, String t) {
}
public void checkServerTrusted(X509Certificate[] certs, String t) {
}
}
};
return certs;
}
In SSLContext you can setup your own SSlContext TSLv1 or SSLv2 then call sslContext.init with trusted certificates. And, add it to your HttpsURLConnection as the DefaultSSLSocketFactory.
System.setProperty ("jsse.enableSNIExtension", "false");
SSLContext sslContext = SSLContext.getInstance("SSL");
TrustManager[] trustManager = getTrustManager();
sslContext.init(null, trustManager, new SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory());
HostnameVerifier hostNameVerifier = new HostnameVerifier() {
public boolean verify(String hostname, SSLSession session) {
return true;
}
};
HttpsURLConnection.setDefaultHostnameVerifier(hostNameVerifier);
I am creating an application where I want to POST xml data directly (without key value pair) to API. The API needs certification authentication which is done here in code. Now I want to POST the data to same URL.
Here is what I want to do :
My current code is :
#Override
public String demoAPI(String xmlData) {
StringBuilder sb = new StringBuilder();
String output="";
try {
KeyStore clientStore = KeyStore.getInstance("PKCS12");
clientStore.load(new FileInputStream(new File("path-to-pfx-file")),
"password".toCharArray());
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmf.init(clientStore, "password".toCharArray());
KeyManager[] kms = kmf.getKeyManagers();
KeyStore trustStore = KeyStore.getInstance("JKS");
trustStore.load(new FileInputStream("path-to-jks-file"), "password".toCharArray());
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(trustStore);
TrustManager[] tms = tmf.getTrustManagers();
final SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(kms, tms, new SecureRandom());
SSLContext.setDefault(sslContext);
HostnameVerifier hostnameVerifier = NoopHostnameVerifier.INSTANCE;
HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory());
HttpsURLConnection.setDefaultHostnameVerifier(hostnameVerifier);
URL url = new URL("URL");
HttpsURLConnection con = (HttpsURLConnection) url.openConnection();
con.setRequestMethod("GET");
con.setConnectTimeout(100000);
con.setSSLSocketFactory(sslContext.getSocketFactory());
con.connect();
BufferedReader br = new BufferedReader(new InputStreamReader(con.getInputStream()));
String line;
while ((line = br.readLine()) != null) {
sb.append(line + "\n");
}
br.close();
System.out.println(sb.toString());
output = sb.toString();
} catch (Exception e) {
e.printStackTrace();
output = e.getMessage();
}
return output;
}
Whereas , for same url , I want to POST xmlData from method.
How can I do that ? What should I change in the code after
con.setRequestMethod("POST"); ?
You have a url and you have some XML to post this url via HTTP. And your post request is a SOAP request, you can send this soap message in the body of this request. So nothing special or different than many developers' do. Post your soap data via http by using java.net or Apache or others. Here are the code samples to do this:
HttpsURLConnection con = null;
try{
URL url = new URL("URL");
con = (HttpsURLConnection) url.openConnection();
con.setRequestMethod("POST");
con.setRequestProperty("content-type", "application/x-www-form-urlencoded");
con.setRequestProperty("Content-Language", "application/soap+xml; charset=utf-8"); // request properties, set your needs
con.setDoOutput(true);
OutputStream os = con.getOutputStream();
os.write(xmlData.getBytes("utf-8"));
os.close();
}catch(Exception e){
e.printStackTrace();
}finally{
con.disconnect();
}
Another option would be to use Apache library. And the code:
HttpPost httpPost = new HttpPost("URL");
StringEntity strEntity = new StringEntity(xmlData, "text/xml", "UTF-8");
strEntity.setContentType("text/xml");
httpPost.setHeader("Content-Type","application/soap+xml;charset=UTF-8");
httpPost.setEntity(strEntity);
HttpClient httpclient = new DefaultHttpClient();
BasicHttpResponse httpResponse = (BasicHttpResponse) httpclient.execute(httpPost);
I want to add TLS 1.2 to the below code, Tried by creating socket but no luck. Can someone help on it ? Can we add it after creating a client ?
private static int executeSOAPRequest(String req, String targetURL)
throws Exception {
PostMethod post = new PostMethod(targetURL);
post.setRequestBody(req);
post.setRequestHeader("Content-type",
"text/xml; characterset=ISO-8859-1");
post.setRequestHeader("SOAPAction", "\"\"");
// prepare HTTP Client
HttpClient client = new HttpClient();
client.getParams().setParameter("SOAPAction", "\"\"");
// Post the request
int respCode = client.executeMethod(post);
System.out.println(post.getResponseBodyAsString());
// If response is not success
if (respCode != 200)
throw new Exception("Executing SOAP request has failed.");
// Convert the response into NOM XML
int resp = 0;
Document doc = nomDocPool.lendDocument();
try {
resp = doc.parseString(post.getResponseBodyAsString());
nomDocPool.returnDocument(doc);
} catch (XMLException e) {
nomDocPool.returnDocument(doc);
//logger.error("Exception while generating SAML : "
//+ e);
throw e;
}
System.out.println("resp: "+resp);
return resp;
}
HttpClient already handles TLS for you. This is documented:
http://hc.apache.org/httpclient-3.x/sslguide.html
HttpClient provides full support for HTTP over Secure Sockets Layer (SSL) or IETF Transport Layer Security (TLS) protocols by leveraging the Java Secure Socket Extension (JSSE). JSSE has been integrated into the Java 2 platform as of version 1.4 and works with HttpClient out of the box.
All you have to do is make sure your targetURL is using https:// instead of http://, then HttpClient handles the rest for you.
Forget HttpClient. Use javax.net.ssl.HttpsURLConnection.
String myResponse = null;
URL url = new URL(targetURL);
HttpsURLConnection con = (HttpsURLConnection) url.openConnection();
con.setRequestProperty("Content-Type", "text/xml; characterset=ISO-8859-1");
con.setRequestProperty("SOAPAction", "\"\"");
con.setRequestMethod("POST");
con.setDoInput(true);
con.setDoOutput(true);
con.setSSLSocketFactory(My_Lovely_CertificateHelper.getSslSocketFactory());
con.connect();
OutputStream os = con.getOutputStream();
OutputStreamWriter osw = new OutputStreamWriter(os, "ISO-8859-1");
osw.write(req);
osw.flush();
osw.close();
InputStream is = null;
if (con.getResponseCode() < HttpsURLConnection.HTTP_BAD_REQUEST) {
is = con.getInputStream();
} else {
is = con.getErrorStream();
}
InputStreamReader isr = new InputStreamReader(is, "ISO-8859-1");
int read = -1;
char[] buff = new char[1024];
StringBuilder sb = new StringBuilder();
while ((read = isr.read(buff)) != -1) {
sb.append(buff, 0, read);
}
myResponse = sb.toString();
return myResponse;
getSslSocketFactory()
public static SSLSocketFactory getSslSocketFactory() throws Exception {
SSLContext sc = SSLContext.getInstance("TLSv1.2");
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
String trustStorePath = getcertPath(); //"user.dir" + "\ohHappyDays.jks";
String password = "finallyFoundLoveIn2021";
KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
ks.load(new FileInputStream(trustStorePath), password.toCharArray());
kmf.init(ks, password.toCharArray());
sc.init(kmf.getKeyManagers(), (TrustManager[]) null, (SecureRandom) null);
return sc.getSocketFactory();
}
I am writing an Android application which has to connect to a server through HTTPS. The first solution I tried was this one:
(Don't mind the security flaws)
final static HostnameVerifier DO_NOT_VERIFY = new HostnameVerifier() {
public boolean verify(String hostname, SSLSession session) {
return true;
}
};
private static void trustAllHosts() {
// Create a trust manager that does not validate certificate chains
TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() {
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return new java.security.cert.X509Certificate[] {};
}
public void checkClientTrusted(X509Certificate[] chain,
String authType) throws CertificateException {
}
public void checkServerTrusted(X509Certificate[] chain,
String authType) throws CertificateException {
}
} };
// Install the all-trusting trust manager
try {
SSLContext sc = SSLContext.getInstance("TLS");
sc.init(null, trustAllCerts, new java.security.SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
} catch (Exception e) {
Log.d("USR_SSL", e.getMessage());
}
}
//...
#Override
protected String doInBackground(String... params) {
try {
URL url = new URL(params[0]);
//This is an HTTPS url
String jsonStr = "";
if(params.length > 1) {
jsonStr = params[1];
}
HttpsURLConnection urlConn = (HttpsURLConnection) url.openConnection();
trustAllHosts();
urlConn.setHostnameVerifier(DO_NOT_VERIFY);
urlConn.setRequestProperty("Content-Type", "application/json");
urlConn.setRequestProperty("Accept", "application/json");
urlConn.setRequestMethod("POST");
OutputStream os = urlConn.getOutputStream();
os.write(jsonStr.getBytes());
os.flush();
//...
All fine and dandy (almost), until I realised that I also have to use authentication, session, and all that good stuff. It should have been really fine using:
CookieManager cookieManager = new CookieManager();
CookieHandler.setDefault(cookieManager);
but unfortunately we have to support Android API level 8, which means the above two lines of code will not work. Given that, I've scoured the Internet for a few hours trying to build a solution using Apache classes, which seemingly support both HTTPS and Cookies.
This is the code I've managed to sew together:
public class ConnectionMediator {
public class MySSLSocketFactory extends SSLSocketFactory {
SSLContext sslContext = SSLContext.getInstance("TLS");
public MySSLSocketFactory(KeyStore truststore) throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException {
super(truststore);
TrustManager tm = new X509TrustManager() {
public void checkClientTrusted(X509Certificate[] chain, String authType) {
}
public void checkServerTrusted(X509Certificate[] chain, String authType) {
}
public X509Certificate[] getAcceptedIssuers() {
return null;
}
};
sslContext.init(null, new TrustManager[] { tm }, null);
}
#Override
public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException, UnknownHostException {
return sslContext.getSocketFactory().createSocket(socket, host, port, autoClose);
}
#Override
public Socket createSocket() throws IOException {
return sslContext.getSocketFactory().createSocket();
}
}
public void tryConnect(String url, String data) {
try {
//SSL Stuff
KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
trustStore.load(null, null);
SSLSocketFactory sf = new MySSLSocketFactory(trustStore);
sf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
HttpParams params = new BasicHttpParams();
HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
HttpProtocolParams.setContentCharset(params, HTTP.UTF_8);
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);
DefaultHttpClient httpClient = new DefaultHttpClient(ccm, params);
//Cookie stuff
HttpContext localContext = new BasicHttpContext();
HttpResponse response = null;
HttpPost httpPost = null;
StringEntity tmp = null;
httpClient.getParams().setParameter(ClientPNames.COOKIE_POLICY, CookiePolicy.RFC_2109);
httpPost = new HttpPost(url);
tmp = new StringEntity(data,"UTF-8");
httpPost.setEntity(tmp);
response = httpClient.execute(httpPost,localContext);
} catch(Exception e) {
Log.d("USR_DEBUG", e.getClass().toString() + ": " + e.getMessage());
}
}
}
At the time of writing this, I get a NetworkOnMainThreadException, but this is rather unimportant; what matters and what I wish to point out is that i have no idea what I'm doing, as in, to simply connect through by means of HTTPS and also use cookies, one has to use 13 different classes which I've never heard of. Obviously, my knowledge of HTTPS/Java net classes is bordering null, but in spite of this I would have expected something more intuitive. So rather than a "this doesn't work" type of question, my question is "what should I be doing", or even, "how do I learn what I have to do?".
Thank you very much,
a very confused coder
My initial question had two parts to it: first, how to use HTTPS and second, how to also use cookies along with it.
My question wasn't thorough enough, as I had partly answered it already - the code which I had initially posted worked with respect to HTTPS, and the NetworkOnMainThreadException occurred because I was not running the code in a separate thread, for example, using AsyncTask.
However, to also make proper use of cookies, one should make use of a solution similar to the following:
public class State {
private static HttpContext httpContext;
public static HttpContext getHttpContext() {
if(httpContext == null) {
httpContext = new BasicHttpContext();
CookieStore cookieStore = getCookieStore();
httpContext.setAttribute(ClientContext.COOKIE_STORE, cookieStore);
}
return httpContext;
}
private static CookieStore cookieStore;
public static CookieStore getCookieStore() {
if(cookieStore == null) {
cookieStore = new BasicCookieStore();
}
return cookieStore;
}
}
I am not sure if this is the 'Android' way of doing it (using a static class) but it works:
//...
//Connection objects
DefaultHttpClient httpClient = new DefaultHttpClient(ccm, params);
HttpPost httpPost = null;
//Cookie stuff
HttpContext httpContext = State.getHttpContext();
httpClient.getParams().setParameter(ClientPNames.COOKIE_POLICY, CookiePolicy.RFC_2109);
//....
It seems using another HttpClient every time preserves the session, provided that the same CookieStore and HttpContext are used.
While this answer makes 'things work' (which is what I needed right now), it is not a thorough answer as it does not explain at all why more than 10 classes are needed to connect everything together.
Here is my code
URL url = new URL(requestUrl);
HttpsURLConnection conn = (HttpsURLConnection)url.openConnection();
BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream()));
I am getting following exception
java.security.cert.CertificateException: No name matching www.sandbox.freelancer.com found
As per I know this site does not have proper Certificate. But any how I have to login into this site. Any suggestion will be appreciated. Thanks.
Create a trust manager that does not validate certificate chains
TrustManager[] trustAllCerts = new TrustManager[]{
new X509TrustManager() {
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return null;
}
public void checkClientTrusted(
java.security.cert.X509Certificate[] certs, String authType) {
}
public void checkServerTrusted(
java.security.cert.X509Certificate[] certs, String authType) {
}
}
};
Install the all-trusting trust manager
try {
SSLContext sc = SSLContext.getInstance("SSL");
sc.init(null, trustAllCerts, new java.security.SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
} catch (Exception e) {
}
Now access an https URL
try {
URL url = new URL("https://www.sandbox.freelancer.com");
HttpsURLConnection conn = (HttpsURLConnection)url.openConnection();
BufferedReader br =
new BufferedReader(new InputStreamReader(conn.getInputStream()));
} catch (MalformedURLException e) {
}
You can save the certificate (get it in firefox), and then add the certificate to the keystore, and then configure your app to use that keystore.