Android SSL SNI connection issues - java

I have an app that serves to consume and update data to a webserver and, recently, the app owner decided to switch to a secure connection due to personal information stored.
The server is already set up as SNI and I have checked it using digicert, the server is working fine and seems to be set up correctly, but does not include the path *.host.com on its alternate names (I am unsure if this is normal or not for SNI).
The iOS worked like a charm, however on Android I get this error:
java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.
My current connection method looks like this:
URL url = new URL(postURL);
HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
SSLContext sc;
sc = SSLContext.getInstance("TLS");
sc.init(null, null, new java.security.SecureRandom());
conn.setSSLSocketFactory(sc.getSocketFactory());
String userpass = "bob" + ":" + "12345678";
String basicAuth = "Basic " + Base64.encodeToString(userpass.getBytes(), Base64.DEFAULT);
conn.setRequestProperty("Authorization", basicAuth);
conn.setReadTimeout(7000);
conn.setConnectTimeout(7000);
conn.setRequestMethod("POST");
conn.setDoInput(true);
conn.connect();
InputStream instream = conn.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(instream));
StringBuilder everything = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
everything.append(line);
}
JSONObject jsonObject = new JSONObject(everything.toString());
return jsonObject;
I'm not quite sure what's the issue here, but trying to connect to https://sni.velox.ch/ gives me a long answer that seems like a success.
Also, I do have the pem key for the certificate here with me, but I do not know how I add that in this context.

Usually you get this error when using a self-signed certificate, in which case you would have to use the certificate while making the request.
Additionally, you might be getting this error because of not including the path *.host.com.
You could try the below code to pass your certificate while building the HttpsURLConnection. Please don't forget to copy the ca.pem file to assets folder.
private HttpsURLConnection buildSslServerConnection() {
HttpsURLConnection urlConnection = null;
try {
// Load CAs from an InputStream
CertificateFactory cf = CertificateFactory.getInstance("X.509");
InputStream caInput = new BufferedInputStream(context.getAssets().open("ca.pem"));
Certificate ca;
try {
ca = cf.generateCertificate(caInput);
} finally {
caInput.close();
}
// Create a KeyStore containing our trusted CAs
String keyStoreType = KeyStore.getDefaultType();
KeyStore keyStore = KeyStore.getInstance(keyStoreType);
keyStore.load(null, null);
keyStore.setCertificateEntry("ca", ca);
// Create a TrustManager that trusts the CAs in our KeyStore
String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
tmf.init(keyStore);
// Create an SSLContext that uses our TrustManager
SSLContext context = SSLContext.getInstance("TLS");
context.init(null, tmf.getTrustManagers(), null);
// Tell the URLConnection to use a SocketFactory from our SSLContext
urlConnection = (HttpsURLConnection) url.openConnection();
urlConnection.setSSLSocketFactory(context.getSocketFactory());
urlConnection.setRequestMethod("POST");
urlConnection.setRequestProperty("Authorization", "Basic" + Base64.encodeToString(userpass.getBytes(), Base64.DEFAULT));
urlConnection.setConnectTimeout(7000);
urlConnection.setReadTimeout(7000);
urlConnection.setInstanceFollowRedirects(false);
urlConnection.setUseCaches(false);
urlConnection.setAllowUserInteraction(false);
urlConnection.setDoOutput(false);
} catch (KeyManagementException e) {
LOG.error("Error while checking server connectivity: ", e);
} catch (CertificateException e) {
LOG.error("Error while checking server connectivity: ", e);
} catch (FileNotFoundException e) {
LOG.error("Error while checking server connectivity: ", e);
} catch (KeyStoreException e) {
LOG.error("Error while checking server connectivity: ", e);
} catch (NoSuchAlgorithmException e) {
LOG.error("Error while checking server connectivity: ", e);
} catch (MalformedURLException e) {
LOG.error("Error while checking server connectivity: ", e);
} catch (IOException e) {
LOG.error("Error while checking server connectivity: ", e);
}
return urlConnection;
}
Hope this helps.

Related

Load specific certificate in Java from worklight

I have a worklight project that has an adapter which connects to the services to grab the response.
It uses the worklight keystore that we have created for our project which has the cert required to connect to the backend (cert name : *.company.com) and the keystore (myproject.p12) has the cert in it:
ssl.keystore.path = /was85/.../myproject.p12.
ssl.keystore.pass = Pass
ssl.keytore.type = PKCS12
once I get the response back from the adapter, inside it I have URI that I need to use to grab an image from the web services and convert it to base64.
I'm use a custom Java code to accomplish this:
package com.company.myProject;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.nio.charset.MalformedInputException;
import java.security.Security;
import java.util.logging.Logger;
public class ImageEncoder {
public final static Logger logger = Logger.getLogger(ImageEncoder.class.getName());
public static String getImage(String imageUrl)
throws MalformedURLException, IOException {
String imageDataString = "";
URL url = null;
URLConnection con = null;
try {
url = new URL(imageUrl);
logger.info("url "+url);
con = url.openConnection();
logger.info("con "+con);
InputStream input = con.getInputStream();
logger.info("input " + input);
byte[] bytes = org.apache.commons.io.IOUtils.toByteArray(input);
logger.info("bytes " + bytes);
input.close();
imageDataString = encodeImage(bytes);
logger.info("imageDataString " + imageDataString);
return imageDataString;
} catch (MalformedInputException malformedInputException) {
malformedInputException.printStackTrace();
imageDataString = malformedInputException.toString();
logger.info("MalformedInputException malformedInputException " + imageDataString);
return ("exception while reading the imag <" + imageDataString + ">");
} catch (IOException ioException) {
ioException.printStackTrace();
imageDataString = ioException.toString();
logger.info("IOException ioException " + imageDataString);
return ("exception while reading the imag <" + imageDataString + ">");
}
}
public static String encodeImage(byte[] imageData) {
// TODO Auto-generated method stub
org.apache.commons.codec.binary.Base64 base = new org.apache.commons.codec.binary.Base64(
false);
return base.encodeToString(imageData);
// return
// org.apache.commons.codec.binary.Base64.encodeBase64URLSafeString(imageData);
}
}
However, the Java code once it opens the connection it complains about the certification (*.company.com) and gives this error:
The signer may need to be added to local trust store "/was85/profiles/node1/config/cells/cell_was/ecommerce_trust.p12" located in SSL configuration alias "DefaultSystemProperties" loaded from SSL configuration file "System Properties". The extended error message from the SSL handshake exception is: "PKIX path building failed: java.security.cert.CertPathBuilderException: PKIXCertPathBuilderImpl could not build a valid CertPath.; internal cause is:
java.security.cert.CertPathValidatorException: The certificate issued by CN=AddTrust External CA Root, OU=AddTrust External TTP Network, O=AddTrust AB, C=SE is not trusted; internal cause is:
java.security.cert.CertPathValidatorException: Certificate chaining error".
And after investigating it is checking the JVM trust store and not our project trust store.
To resolve this issue I have three options:
Add the root cert which is (AddTrust) to myProject.p12 and not my
leaf cert (*.company.com) which is not accepted.
Add the leaf cert (*.company.com) to the JVM keystore
(ecommerce_trust.p12) which is not acceptable as we have another app
running on the same JVM and it will get access to the leaf cert.
Make a trustManager in my java code to get the project p12 and not
the JVM one which has the following code:
try {
url = new URL(imageUrl);
logger.info("url "+url);
KeyStore trustStore = KeyStore.getInstance("PKCS12");
trustStore.load(new FileInputStream("/was85/resources/security/ecommerce_gr_mobile.p12"), "Pass".toCharArray());
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(trustStore);
TrustManager[] tms = tmf.getTrustManagers();
SSLContext sslContext = null;
sslContext = SSLContext.getInstance("SSL");
sslContext.init(null, tms, new SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory());
HttpsURLConnection con = (HttpsURLConnection)url.openConnection();
logger.info("con "+con);
//con.setSSLSocketFactory(sslFactory);
InputStream input = con.getInputStream();
logger.info("input " + input);
byte[] bytes = org.apache.commons.io.IOUtils.toByteArray(input);
logger.info("bytes " + bytes);
input.close();
imageDataString = encodeImage(bytes);
logger.info("imageDataString " + imageDataString);
//return imageDataString;
} catch (MalformedInputException malformedInputException) {
malformedInputException.printStackTrace();
imageDataString = malformedInputException.toString();
logger.info("MalformedInputException malformedInputException " + imageDataString);
return ("exception while reading the imag <" + imageDataString + ">");
} catch (IOException ioException) {
ioException.printStackTrace();
imageDataString = ioException.toString();
logger.info("IOException ioException " + imageDataString);
return ("exception while reading the imag <" + imageDataString + ">");
} catch (KeyStoreException keyStoreException) {
// TODO Auto-generated catch block
keyStoreException.printStackTrace();
imageDataString = keyStoreException.toString();
logger.info("keyStoreException " + imageDataString);
} catch (NoSuchAlgorithmException noSuchAlgorithmException) {
// TODO Auto-generated catch block
noSuchAlgorithmException.printStackTrace();
imageDataString = noSuchAlgorithmException.toString();
logger.info("noSuchAlgorithmException " + imageDataString);
} catch (CertificateException certificateExceptione) {
// TODO Auto-generated catch block
certificateExceptione.printStackTrace();
imageDataString = certificateExceptione.toString();
logger.info("certificateExceptione " + imageDataString);
} catch (KeyManagementException keyManagementException) {
// TODO Auto-generated catch block
keyManagementException.printStackTrace();
imageDataString = keyManagementException.toString();
logger.info("keyManagementException " + imageDataString);
}
return imageDataString;
}
which is not working and I'm getting this error:
[9/2/15 13:40:09:512 EDT] 0000021d ImageEncoder I >>>>>>>>>>>>>>>trustStore loaded <<<<<<<<<<java.security.KeyStore#f1c4b946
[9/2/15 13:40:09:512 EDT] 0000021d ImageEncoder I >>>>>>>>>>>>>>>tmf init <<<<<<<<<<javax.net.ssl.TrustManagerFactory#4d3fb9ab
[9/2/15 13:40:09:513 EDT] 0000021d ImageEncoder I >>>>>>>>>>>>>>>tms init <<<<<<<<<<[Ljavax.net.ssl.TrustManager;#c76fa980
[9/2/15 13:40:09:513 EDT] 0000021d ImageEncoder I >>>>>>>>>>>>>>>sslContext <<<<<<<<<<
[9/2/15 13:40:09:570 EDT] 0000021d ImageEncoder I con com.ibm.net.ssl.www2.protocol.https.e:https://domain.company.com/wps/wcm/connect/e77f32e8-906f-445f-b198-e3b77cb0e786/logo90x40.gif?MOD=AJPERES&CACHEID=e77f32e8-906f-445f-b198-e3b77cb0e786
[9/2/15 13:40:09:676 EDT] 0000021d ImageEncoder I IOException ioException javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure
Worklight server uses the keystore defined by "ssl.keystore.*" to create SSL connection between adapter and backend server configured in adapter's XML file, so if you have custom java code with your own connection you should to set your own SSL context as you do in your example above. It is the right way.
The reason of the error might be the keystore "/was85/resources/security/ecommerce_gr_mobile.p12" doesn't contain the cert of the images server.
I suggest to create separate kestore and use it from adapter's java code. Put into it all certs of the servers you want to reach from adapter's java code.
I was able to resolve the issue by adding:
con.connect();
The complete code will be:
KeyStore trustStore = KeyStore.getInstance("PKCS12");
File key = new File ("/was85/resources/security/ecommerce_gr_mobile.p12");
trustStore.load(new FileInputStream(key), "Pass".toCharArray());
logger.info(">>>>>>>>>>>>>>>trustStore loaded <<<<<<<<<<" + String.valueOf(trustStore) );
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(trustStore);
logger.info(">>>>>>>>>>>>>>>tmf init <<<<<<<<<<" + String.valueOf(tmf));
TrustManager[] tms = tmf.getTrustManagers();
logger.info(">>>>>>>>>>>>>>>tms init <<<<<<<<<<" + String.valueOf(tms));
SSLContext sslContext = null;
sslContext = SSLContext.getInstance("TLS");
logger.info(">>>>>>>>>>>>>>>sslContext <<<<<<<<<<");
sslContext.init(null, tms, new SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory());
HttpsURLConnection con = (HttpsURLConnection)url.openConnection();
con.connect();
logger.info("con "+con);
//con.setSSLSocketFactory(sslFactory);
InputStream input = con.getInputStream();
logger.info("input " + input);
byte[] bytes = org.apache.commons.io.IOUtils.toByteArray(input);
logger.info("bytes " + bytes);
input.close();

Android connect to server with selfsigned certificate

EDITTED: "The code below works fine, no errors, no exceptions"
I'm aware of the grand amount of questions in regards to this topic, as well as the many blogs that google conjures up. I have read through them and have managed to come up with what I'm about to explain. My doubt lies in "is my approach correct? Does it have any side-effects?" and another question that is better asked as I explain my method.
I based this approach following this Android.Developres tutorial.
System.setProperty("jsse.enableSNIExtension", "false");
//Java 7 introduced SNI (enabled by default). The server I use is
// misconfigured I suppose and
// it sends an "Unrecognized Name" warning in the SSL handshake
// which breaks my web service.
// Load CA from an InputStream (CA would be saved in Raw file,
// and loaded as a raw resource)
CertificateFactory cf = CertificateFactory.getInstance("X.509");
InputStream caInput = new BufferedInputStream(new FileInputStream("PATH_TO_CERT.crt"));
Certificate ca;
try {
ca = cf.generateCertificate(caInput);
} finally {
caInput.close();
}
// Create a KeyStore containing our trusted CAs
String keyStoreType = KeyStore.getDefaultType();
KeyStore keyStore = KeyStore.getInstance(keyStoreType);
keyStore.load(null, null);
keyStore.setCertificateEntry("ca", ca);
// Create a TrustManager that trusts the CAs in our KeyStore
String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
tmf.init(keyStore);
// Create an SSLContext that uses our TrustManager
SSLContext context = SSLContext.getInstance("TLS");
context.init(null, tmf.getTrustManagers(), null);
// Create all-trusting host name verifier
// to avoid the following :
// java.security.cert.CertificateException: No name matching
// This is because Java by default verifies that the certificate CN (Common Name) is
// the same as host name in the URL. If they are not, the web service client fails.
HostnameVerifier allHostsValid = new HostnameVerifier() {
#Override
public boolean verify(String arg0, SSLSession arg1) {
return true;
}
};
//Install it
HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid);
// Tell the URLConnection to use a SocketFactory from our SSLContext
URL url = new URL("https....");
urlConnection.setSSLSocketFactory(context.getSocketFactory());
try {
HttpsURLConnection urlConnection = (HttpsURLConnection) url.openConnection();
urlConnection.setRequestMethod("GET");
urlConnection.connect();
switch(urlConnection.getResponseCode()){
case 401:
BufferedReader br = new BufferedReader(new InputStreamReader(urlConnection.getErrorStream()));
StringBuilder sb = new StringBuilder();
String line;
while ((line = br.readLine()) != null) {
sb.append(line+"\n");
}
br.close();
System.out.println( sb.toString());
}
} catch (MalformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ProtocolException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
Here is my other question, in the following line:
InputStream caInput = new BufferedInputStream(new FileInputStream("PATH_TO_CERT.crt"));
You see that the method forces me to have the certificate.crt preloaded onto raw file inside res folder. Is there a way (I have looked but have found 0 answers) to connect to the server and download said certificate.crt and save it on a private folder not accessible by the user?

Android to JAVA SSL

i am a relative novice at Android and have had issues getting a simple SSL connection working to a java server.
I know the java server works as I tested it using the same keystore as the server and a java client.
The Android client does send something as the java server accepts a connection and displays a null value for a readline variable, and no error message.
I have my keystore, and truststore in bks format, and added to the res/raw folder.
Android Client:
SSLContext sslContext = SSLContext.getDefault();
KeyStore trustSt = KeyStore.getInstance("BKS");
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
InputStream trustStoreStream = this.getResources().openRawResource(R.raw.truststore);
trustSt.load(trustStoreStream, "password".toCharArray());
trustManagerFactory.init(trustSt);
KeyStore keyStore = KeyStore.getInstance("BKS");
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
InputStream keyStoreStream = this.getResources().openRawResource(R.raw.keystore);
keyStore.load(keyStoreStream, "password".toCharArray());
keyManagerFactory.init(keyStore, "password".toCharArray());
sslContext.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), null);
}
catch (NoSuchAlgorithmException nsae){Log.d("SSL", nsae.getMessage());}
catch (KeyStoreException kse){Log.d("SSL", kse.getMessage());}
catch (IOException ioe){Log.d("SSL", ioe.getMessage());}
catch (CertificateException ce){Log.d("SSL", ce.getMessage());}
catch (KeyManagementException kme){Log.d("SSL", kme.getMessage());}
catch(AccessControlException ace){Log.d("SSL", ace.getMessage());}
catch(UnrecoverableKeyException uke){Log.d("SSL", uke.getMessage());}
try{
//error catch
String error = "test";
sslsocketfactory = (SSLSocketFactory) SSLSocketFactory.getDefault();
s = (SSLSocket) sslsocketfactory.createSocket("192.168.2.101", port);
outStream = s.getOutputStream();
outStreamWriter = new OutputStreamWriter(outStream);
bufferedWriter = new BufferedWriter(outStreamWriter);
bufferedWriter.write(error + "\n");
bufferedWriter.flush();
} //end try
catch (UnknownHostException e) {e.printStackTrace();}
catch (IOException e) {e.printStackTrace();}
finally{
if (s != null){
try {s.close();}
catch (IOException e) {e.printStackTrace();}
}
}//end finally
}
I have specified a truststore but do not not how to initialise the truststore on the socket.
Any help would be appreciated.
I think changing this line
sslsocketfactory = (SSLSocketFactory) SSLSocketFactory.getDefault();
to use the SSLSocketFactory(KeyStore truststore) constructor may solve your problem.
sslsocketfactory = new SSLSocketFactory(trustSt);

SSL connection failure between java server and android client

I am trying to setup mutual authentication SSL connection between java host and android client. Don't know why its not getting connected. Below are the code of Android client app and Java server.
Client code:
private SSLContext createSSLContext(final Context cont){
SSLContext ssl_cont = null;
try {
Log.d(TAG, "TrustStore - Initializing");
KeyStore trustStore = KeyStore.getInstance("BKS");
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
InputStream trustStoreStream = cont.getResources().openRawResource(R.raw.myclienttruststore);
trustStore.load(trustStoreStream, "client".toCharArray());
trustManagerFactory.init(trustStore);
Log.d(TAG, "TrustStore - Initialized");
// Setup keystore
Log.d(TAG, "KeyStore - Initializing");
KeyStore keyStore = KeyStore.getInstance("BKS");
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
InputStream keyStoreStream = cont.getResources().openRawResource(R.raw.myclient);
keyStore.load(keyStoreStream, "client".toCharArray());
keyManagerFactory.init(keyStore, "client".toCharArray());
Log.d(TAG, "KeyStore - Initialized");
ssl_cont = SSLContext.getInstance("TLS");
ssl_cont.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), null);
} catch (Exception e) {
// TODO Auto-generated catch block
alertbox("SSLClient", "ERROR: " + e.getMessage());
Log.d(TAG, "ERROR: " + e.getMessage());
}
return ssl_cont;
}
OnClickListener onConnClick = new OnClickListener() {
public void onClick(View arg0) {
// TODO Auto-generated method stub
try {
// Setup the SSL context to use the truststore and keystore
Log.d(TAG, "Started..");
SSLContext ssl_context = createSSLContext(cont);
Log.d(TAG,"here 1...");
SSLSocketFactory socketFactory = (SSLSocketFactory) ssl_context.getSocketFactory();
Log.d(TAG,"here 2...");
socket = (SSLSocket) socketFactory.createSocket(ipadd.getText().toString().trim(), Integer.parseInt(port.getText().toString().trim()));
Log.d(TAG,"here 3...");
dataOut = new DataOutputStream(socket.getOutputStream());
dataIn = new DataInputStream(socket.getInputStream());
dataOut.writeUTF("Hello !!");
msgin.setText("Connected");
Log.d(TAG, "Completed..");
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
msgin.setText("Not connected");
alertbox("Main", "ERROR: " + e.getMessage());
Log.d(TAG, "ERROR: " + e.getMessage());
}
}
};
Server code:
try {
mySSLServerFac = (SSLServerSocketFactory) SSLServerSocketFactory.getDefault();
mySSLServerSocket = (SSLServerSocket) mySSLServerFac.createServerSocket(9999);
System.out.println("Listening on 9999\n");
mySSLSocket = (SSLSocket) mySSLServerSocket.accept();
DataInputStream input = new DataInputStream(mySSLSocket.getInputStream());
DataOutputStream output = new DataOutputStream(mySSLSocket.getOutputStream());
do{
System.out.println("Remote IP Address : " + mySSLSocket.getInetAddress());
msg = input.readUTF().toString();
System.out.println(msg);
java.util.Scanner sc = new java.util.Scanner(System.in);
output.writeUTF(sc.nextLine());
}while(msg != "exit");
System.out.println(msg);
} catch (Exception e) {
e.printStackTrace();
}
I am stuck with "No cipher suites in common" error at server. Since i am nowhere in SSL connection setup. Let me help if you find out the bug or major problem.
Here is the link i followed to create certificate and truststore. Truststore and kestore i have created are here
I am using Android 2.2 and BKSProvider 1.46, please let know where i am going wrong. I have to wind up this project as soon as possible.
Thanks in advance.
From the stack trace it looks like exception you caught does not contain a message.
Log.d(TAG, e.getMessage());
It has nothing to do with SSL.
It's solved ! Problem was with the truststore of java host, followed this post.
The trustStore needs to be specified for client/server as they are using the default trustStore, causing failure. Using -Djavax.net.ssl.trustStore=servertruststore.jks -Djavax.net.ssl.trustStorePassword=server on the server and creating own keystore & truststore at client allows the session to complete. It was the -Djavax.net.debug=ssl,handshake which helped lot.
The entire command is : java -Djavax.net.ssl.keyStore=server.jks -Djavax.net.ssl.keyStorePassword=server -Djavax.net.ssl.trustStore=servertruststore.jks -Djavax.net.ssl.trustStorePassword=server SSLServer
Now i am on to creating sslsession and multi-threaded programming.

Android: HTTPS (SSL) connection using HttpsURLConnection

I have 2 apps, one is a Servlet/Tomcat Server, and the other is an Android app.
I want to use HttpURLConnection to send and receive XML between both.
Code:
private String sendPostRequest(String requeststring) {
DataInputStream dis = null;
StringBuffer messagebuffer = new StringBuffer();
HttpURLConnection urlConnection = null;
try {
URL url = new URL(this.getServerURL());
urlConnection = (HttpURLConnection) url.openConnection();
urlConnection.setDoOutput(true);
urlConnection.setRequestMethod("POST");
OutputStream out = new BufferedOutputStream(urlConnection.getOutputStream());
out.write(requeststring.getBytes());
out.flush();
InputStream in = new BufferedInputStream(urlConnection.getInputStream());
dis = new DataInputStream(in);
int ch;
long len = urlConnection.getContentLength();
if (len != -1) {
for (int i = 0; i < len; i++)
if ((ch = dis.read()) != -1) {
messagebuffer.append((char) ch);
}
} else {
while ((ch = dis.read()) != -1)
messagebuffer.append((char) ch);
}
dis.close();
} catch (Exception e) {
e.printStackTrace();
} finally {
urlConnection.disconnect();
}
return messagebuffer.toString();
}
Now, I need to use SSL to send the XMLs for security.
First, I use Java Keytool to generate the .keystore file.
Keytool -keygen -alias tomcat -keyalg RSA
Then I put the XML Code on server.xml file of Tomcat to use SSL
<Connector
port="8443" protocol="HTTP/1.1" SSLEnabled="true"
maxThreads="150" scheme="https" secure="true"
keystoreFile="c:/Documents and Settings/MyUser/.keystore"
keystorePass="password"
clientAuth="false" sslProtocol="TLS"
/>
Then, I change it the HttpURLConnection for HttpsURLConnection
private String sendPostRequest(String requeststring) {
DataInputStream dis = null;
StringBuffer messagebuffer = new StringBuffer();
HttpURLConnection urlConnection = null;
//Conexion por HTTPS
HttpsURLConnection urlHttpsConnection = null;
try {
URL url = new URL(this.getServerURL());
//urlConnection = (HttpURLConnection) url.openConnection();
//Si necesito usar HTTPS
if (url.getProtocol().toLowerCase().equals("https")) {
trustAllHosts();
//Creo la Conexion
urlHttpsConnection = (HttpsURLConnection) url.openConnection();
//Seteo la verificacion para que NO verifique nada!!
urlHttpsConnection.setHostnameVerifier(DO_NOT_VERIFY);
//Asigno a la otra variable para usar simpre la mism
urlConnection = urlHttpsConnection;
} else {
urlConnection = (HttpURLConnection) url.openConnection();
}
//Do the same like up
and add a trustAllHosts method to Trust every server (dont check for any certificate)
private static void trustAllHosts() {
X509TrustManager easyTrustManager = new X509TrustManager() {
public void checkClientTrusted(
X509Certificate[] chain,
String authType) throws CertificateException {
// Oh, I am easy!
}
public void checkServerTrusted(
X509Certificate[] chain,
String authType) throws CertificateException {
// Oh, I am easy!
}
public X509Certificate[] getAcceptedIssuers() {
return null;
}
};
// Create a trust manager that does not validate certificate chains
TrustManager[] trustAllCerts = new TrustManager[] {easyTrustManager};
// 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) {
e.printStackTrace();
}
}
Those changes worked very good, but I don´t want to Trust every server. I want to use my keystore file to validate the connection and use SSL in the right way.
I read a lot on the internet and made a lot of tests, but I can´t understand what I have to do and how to do it.
Can somebody help me?
Thank you very much
Sorry for my poor english
-------------------------UPDATE 2011/08/24-------------------------------------------------
Well, I'm still working on this. I made a new method to set the KeyStore, InputStream, etc
The method looks like this:
private static void trustIFNetServer() {
try {
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
KeyStore ks = KeyStore.getInstance("BKS");
InputStream in = context.getResources().openRawResource(R.raw.mykeystore);
String keyPassword = "password";
ks.load(in, keyPassword.toCharArray());
in.close();
tmf.init(ks);
TrustManager[] tms = tmf.getTrustManagers();
SSLContext sc = SSLContext.getInstance("TLS");
sc.init(null, tms, new java.security.SecureRandom());
} catch (Exception e) {
e.printStackTrace();
}
}
First I had a lot of problems with the Key and the Certificate, but now it is working (I think so)
My problem right now is a TimeOut Exception. I don´t know why it is generated. I'm think it's something with the data write, but I can't solve yet.
Any Idea?
You need to create a trust store file for your self-signed certificate as described here.
Use it on the client side to connect with your server. It doesn't really matter if you use JKS or another format, I'll assume JKS for now.
To accomplish what you have in mind you need a different TrustManager, obviously. You can use TrustManagerFactory and feed its trust settings with your newly created trust store.
TrustManagerFactory tmf = TrustManagerFactory.getInstance("PKIX");
KeyStore ks = KeyStore.getInstance("JKS");
FileInputStream in = new FileInputStream("<path to your key store>");
ks.load(in, "password".toCharArray());
in.close();
tmf.init(ks);
TrustManager[] tms = tmf.getTrustManagers();
Use tms to init your SSLContextinstead for the new trust settings to be used for your SSL/TLS connection.
Also you should make sure that the CN part of the server TLS certificate is equal to the FQDN (fully qualified domain name) of your server, e.g. if your server base URL is 'https://www.example.com', then the CN of the certificate should be 'www.example.com'. This is needed for host name verification, a feature that prevents man-in-the-middle-attacks. You could disable this, but only when using this your connection will be really secure.
Create your trust store, store at as an asset and use it initialize this SocketFactory. Then use the factory instead of your own 'trust everybody' one.
If you want to ignore all the certificate, ignore the handshake, then this works:
HttpsURLConnection and intermittent connections

Categories