Android - SSL Handshake Exception on using Volley / java code - java

When trying to access web service from secured testing environment with SSL certificate getting the issue below.
com.android.volley.NoConnectionError: javax.net.ssl.SSLProtocolException: Read error: ssl=0xa35dad40: Failure in SSL library, usually a protocol error
error:100000d7:SSL routines:OPENSSL_internal:SSL_HANDSHAKE_FAILURE (external/boringssl/src/ssl/s3_pkt.c:402 0xa3630912:0x00000000)
I have tried with volley and basic java code, still getting the same issue. When I used the same code for secured development environment with different certificate its working fine. Whereas its not working in testing environment for specific bandwidths (Airtel 3G, 4G). It is working fine with all the environments(Testing & Dev) for 2G bandwidths.
Don't know where the problem occurs. Help me in sorting out this issue.
I have added the code snippet below,
Responsecallback responsecallback;
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
testing(mBase_Url);
}
public void testing(String urls) {
String result = "";
try {
URL url = new URL(urls);
HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
connection.setSSLSocketFactory(getSSLCertificate()); // Tell the URLConnection to use a SocketFactory from our SSLContext
connection.setRequestMethod("POST");
connection.setDoOutput(true);
connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
connection.setConnectTimeout(30000);
connection.setReadTimeout(30000);
connection.setHostnameVerifier(new HostnameVerifier() {
#Override
public boolean verify(String s, SSLSession sslSession) {
return true;
}
});
Uri.Builder builder = new Uri.Builder()
.appendQueryParameter("country", "IN");
String query = builder.build().getEncodedQuery();
PrintWriter out = new PrintWriter(connection.getOutputStream());
out.println(query);
out.close();
BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream())); //,8192
String inputLine;
while ((inputLine = in.readLine()) != null) {
result = result.concat(inputLine);
}
responsecallback.displayResponse(result);
in.close();
} catch (IOException e) {
result = e.toString();
Log.e(TAG, "HTTP Error Result=" + result);
responsecallback.displayResponse(result);
}
}
private SSLSocketFactory getSSLCertificate() {
try {
// Get an instance of the Bouncy Castle KeyStore format
KeyStore trusted = KeyStore.getInstance("PKCS12");
// your trusted certificates (root and any intermediate certs)
InputStream in = getResources().openRawResource(R.raw.xxxxxx); //SSL Certificate - P12 formate
String password = "XXXXXXX"; // Certificate password
char[] pwd = password.toCharArray();
try {
trusted.load(in, pwd);
} finally {
in.close();
}
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmf.init(trusted, pwd);
String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
tmf.init(trusted);
SSLContext context = SSLContext.getInstance("SSL");
context.init(kmf.getKeyManagers(), getWrappedTrustManagers(), new SecureRandom());
return context.getSocketFactory();
} catch (Exception e) {
Log.e(TAG, "Exception e=" + e.toString());
throw new AssertionError(e);
}
}
private TrustManager[] getWrappedTrustManagers() {
return new TrustManager[]{
new X509TrustManager() {
#Override
public void checkClientTrusted(java.security.cert.X509Certificate[] x509Certificates, String s)
throws java.security.cert.CertificateException {
}
#Override
public void checkServerTrusted(java.security.cert.X509Certificate[] x509Certificates, String s)
throws java.security.cert.CertificateException {
}
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
X509Certificate[] myTrustedAnchors = new X509Certificate[0];
return null;
}
}
};
}

Related

How to indicate to HttpsUrlConnection the cert (previously imported to cacert file) that it should use

I'm trying to consume a web service, in which we import a certificate to the JDK's cacert file. But I can't understand how to "set" the HttpsURLConnection object the cert that it must use to be able to execute the web service.
I read that once the certificate was installed in the cacert file, it was no longer necessary to indicate anything when connecting to the web service.
I am currently skipping the KeyStore and TrustStore validation to invoke the web service.
This is my code:
public void tokenFotoCteNOCHOProd(String url) throws IOException {
HttpsURLConnection conn = null;
String params = "";
String res = "";
try {
TrustManager[] trustAllCerts = new TrustManager[] {
new X509TrustManager() {
#Override
public void checkClientTrusted(java.security.cert.X509Certificate[] arg0, String arg1) throws CertificateException {
/**/
}
#Override
public void checkServerTrusted(java.security.cert.X509Certificate[] arg0, String arg1) throws CertificateException {
/**/
}
#Override
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return null;
}
}
};
HostnameVerifier hv = new HostnameVerifier() {
public boolean verify(String hostname, SSLSession session) {
return true;
}
};
SSLContext sc = SSLContext.getInstance("TLSv1.2");
sc.init(null, trustAllCerts, new SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
HttpsURLConnection.setDefaultHostnameVerifier(hv);
params = "someParams";
URL url = new URL(url);
conn = (HttpsURLConnection) url.openConnection();
conn.setDoOutput(true);
conn.setRequestMethod("POST");
conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
conn.setConnectTimeout(3000);
try (OutputStream os = conn.getOutputStream()) {
os.write(params.getBytes(StandardCharsets.UTF_8));
os.flush();
res = getRespWS(conn);//get resp..
}
} catch (KeyManagementException | NoSuchAlgorithmException | ClassCastException | JsonParseException e) {
LOGGER.info("error", e);
}
}

Android - How to store certificate in keystore programmatically?

I am making a financial transaction android app. It requires SSL authentication and I successfully able to complete it(handshake between Android and Tomcat). I used keytool and openSSL to generate Server and client certificates. Tomcat certifcate format is JKS and android formate is BKS. I stored this BKS file in Raw folder and use this as follows:
public class NetworkCallSecure extends AsyncTask<String, Void, String> {
ResponseListener responseListener;
Activity activity;
ResultCodes code;
public NetworkCallSecure(Activity activity, ResponseListener responseListener, ResultCodes code) {
this.responseListener = responseListener;
this.activity = activity;
this.code = code;
}
#Override
protected String doInBackground(String... params) {
try{
System.setProperty("http.keepAlive", "false");
HttpsURLConnection .setDefaultHostnameVerifier(new HostnameVerifier() {
public boolean verify(String hostname,
SSLSession session) {
Log.d("HTTPS",hostname+":"+session);
return true;
}
});
char[] passwKey = "mypass".toCharArray();
KeyStore ks = KeyStore.getInstance("BKS");
InputStream in = activity.getResources().openRawResource(
R.raw.client);
InputStream is = activity.getResources().openRawResource(
R.raw.client);
ks.load(in, passwKey);
KeyManagerFactory kmf = KeyManagerFactory.getInstance("X509");
kmf.init(ks, passwKey);
SSLContext context = SSLContext.getInstance("TLS");
context.init(kmf.getKeyManagers(),
new X509TrustManager[] { new MyX509TrustManager(is,
passwKey) }, new SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(context
.getSocketFactory());
URL url = new URL(params[0]);
HttpsURLConnection connection = (HttpsURLConnection) url
.openConnection();
connection.setRequestMethod("POST");
connection.setRequestProperty("Content-Type", "application/json");
connection.setRequestProperty("Content-Length", "" + Integer.toString(params[1].getBytes().length));
connection.setDoOutput(true);
byte[] outputInBytes = params[1].getBytes("UTF-8");
OutputStream os = connection.getOutputStream();
os.write( outputInBytes );
os.close();
BufferedReader bin = new BufferedReader(new InputStreamReader(
connection.getInputStream()));
StringBuffer sb = new StringBuffer();
String line;
while ((line = bin.readLine()) != null) {
sb.append(line);
}
in.close();
is.close();
return sb.toString();
} catch (Exception e) { // should never happen
e.printStackTrace();
Log.d("Err", e.toString());
}
return "no result";
}
#Override
protected void onPostExecute(String result) {
responseListener.getResponse(result,code);
}
}
My Trustmanager class is:
public class MyX509TrustManager implements X509TrustManager {
X509TrustManager pkixTrustManager;
public MyX509TrustManager(InputStream trustStore, char[] password)
throws Exception {
// create a "default" JSSE X509TrustManager.
KeyStore ks = KeyStore.getInstance("BKS");
ks.load(trustStore, password);
TrustManagerFactory tmf = TrustManagerFactory.getInstance("X509");
tmf.init(ks);
TrustManager tms[] = tmf.getTrustManagers();
/*
* Iterate over the returned trustmanagers, look for an instance of
* X509TrustManager. If found, use that as our "default" trust manager.
*/
for (int i = 0; i < tms.length; i++) {
if (tms[i] instanceof X509TrustManager) {
pkixTrustManager = (X509TrustManager) tms[i];
return;
}
}
/*
* Find some other way to initialize, or else we have to fail the
* constructor.
*/
throw new Exception("Couldn't initialize");
}
public void checkClientTrusted(X509Certificate[] arg0, String arg1)
throws CertificateException {
// TODO Auto-generated method stub
try {
pkixTrustManager.checkClientTrusted(arg0, arg1);
} catch (CertificateException excep) {
// do any special handling here, or rethrow exception.
}
}
public void checkServerTrusted(X509Certificate[] arg0, String arg1)
throws CertificateException {
// TODO Auto-generated method stub
try {
pkixTrustManager.checkServerTrusted(arg0, arg1);
} catch (CertificateException excep) {
/*
* Possibly pop up a dialog box asking whether to trust the cert
* chain.
*/
}
}
public X509Certificate[] getAcceptedIssuers() {
// TODO Auto-generated method stub
return pkixTrustManager.getAcceptedIssuers();
}
}
Now I want to register user using this HTTPS connection. The process is get details from user and send it to server. Server will verify these details and send confirmation PIN on user mobile (got this MSISDN in user details). User will enter this PIN and server will verify that PIN is same. After user is verified client app (user mobile) will generate a CSR and send it to server. Server will generate Certificate using this CSRand send it to client (mobile app).
Now my problem is I want to store this certificate where only my App can access this certificate. I am trying to save this in my BKS file in raw folder using this:
private boolean storeCertInKeystore(byte[] cert) {
try {
InputStream is = getResources().openRawResource(
R.raw.client);
CertificateFactory cf = CertificateFactory.getInstance("X.509");
InputStream certstream = new ByteArrayInputStream(cert);
X509Certificate certificate = (X509Certificate) cf.generateCertificate(certstream);
KeyStore keyStore = KeyStore.getInstance("BKS");
keyStore.load(is, "mypass".toCharArray());
keyStore.setCertificateEntry("mycert", certificate);
Log.d("My App Cert: ", "true");
return true;
} catch(Exception e) {
e.printStackTrace();
}
return false;
}
This code runs successfully but could not store cert in BKS file. I tried another way describe here but could not succeed. (I want to use this certificate later in my app for client authentication)
My Question is Q. How can I store this certificate so it can be only accessible by my app? And also I can delete this certificate when user registration expires.
Please help and thanks in advance.
Your issue is not with the keystore itself, but rather with the
location of the file where you're trying to store the new client
certificate!
The "RAW-folder" is part of your installed application package. So
you can "virtually" access it, and only READ, not WRITE!
Your best option, if you want your keystore to be private, is your
application sandboxed-private-folder(Internal storage).
You can't write in the RAW-folder, but you can write in your application private folder.
In the link that you provided, the storage/write location is in
fact the private folder. So it didn't work for you because you're
trying to "write in the Raw-Folder"
You probably know that already, but you can copy your file(R.raw.client)
from the "Raw-folder" to your application private folder. That way, you only use one keystore file(readable and writable).

Android SSL HostName Was Not Verified

I have a self signed server hardcoded port 52428. My client app keeps getting "Hostname Was Not Verified" even when I override the HostNameVerifier to always return true. When I changed the hostname from IP Address to DNS, another error pops up that says "Unable to resolve host: No Address associated with hostname"
Here's my code:
private class SSLConnect extends AsyncTask<Void, Void, String> {
#Override
protected String doInBackground(Void... values) {
//String https_url = "https://www.google.com/";
//String https_url = "https://192.168.0.106:52428/webserveradmin/preferences";
String https_url = "https://home-pc:52428/webserveradmin/preferences/";
String response;
try {
TrustManager[] tm = new TrustManager[]{
new X509TrustManager() {
#Override
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
}
#Override
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
}
#Override
public X509Certificate[] getAcceptedIssuers() {
//return new X509Certificate[0];
return null;
}
}
};
URL url;
try {
url = new URL(https_url);
}
catch (MalformedURLException e) {
return "Error URL: " + e.getMessage();
}
HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
try {
conn.setDefaultHostnameVerifier(new NullHostNameVerifier());
SSLContext sc = SSLContext.getInstance("SSL");
sc.init(null, tm, new SecureRandom());
conn.setSSLSocketFactory(sc.getSocketFactory());
conn.setRequestMethod("GET");
conn.setRequestProperty("Authorization", "Basic " + Base64.encode("sa:sa".getBytes(), Base64.DEFAULT));
conn.connect();
InputStream in = conn.getInputStream();
BufferedReader r = new BufferedReader(new InputStreamReader(in));
StringBuilder sb = new StringBuilder();
String line;
while ((line = r.readLine()) != null) {
sb.append(line);
}
response = sb.toString();
} catch (GeneralSecurityException e) {
return "Error Security: " + e.getMessage();
}
}
catch(Exception e){
return "Error SSL: " + e.getMessage();
}
return response;
}
#Override
protected void onProgressUpdate(Void... values) {
}
#Override
protected void onPostExecute(String result) {
Toast.makeText(ctxt, result, Toast.LENGTH_LONG).show();
}
}
public class NullHostNameVerifier implements HostnameVerifier{
#Override
public boolean verify(String hostname, SSLSession session) {
return true;
}
}
The hostname verifier cares about verifying the hostname only, not the trust chain. But with self-signed certificates you don't have a trust chain leading to a locally trusted certificate.
Apart from that, just disabling the certificate checking is a very bad idea, because this way you will not only accept your self-signed certificate but instead any certificates and thus you will be open to man-in-the-middle attacks. See also SSL Vulnerability in ******** VU#582497.
To do it correctly use instead certificate/public key pinning. For a more detailed explanation and also sample code see OWSAP.

Return code 403 while reading URL with HttpsURLConnection

I am trying to read the content of the URL with the code down below, but get an 403 error:
https://api.kraken.com/0/public/Time
The URL is reachable in the Problem or via Rest-Connection-Test-Pages like https://apigee.com without a problem. The code works for other HTTPS-Urls just fine. I have no clue what could be wrong here:
public class HttpsClient{
public static void main(String[] args)
{
new HttpsClient().testIt();
}
private void testIt(){
// Install the all-trusting trust manager
try {
SSLContext sc = SSLContext.getInstance("TLS");
sc.init(null, trustAllCerts, new SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
} catch (Exception e) {
;
}
String https_url = "https://api.kraken.com/0/public/Time";
URL url;
HttpsURLConnection con = null;
try {
url = new URL(https_url);
con = (HttpsURLConnection)url.openConnection();
//dumpl all cert info
print_https_cert(con);
try{
System.out.println("****** Content of the URL ********");
BufferedReader br =
new BufferedReader(
new InputStreamReader(con.getInputStream()));
String input;
while ((input = br.readLine()) != null){
System.out.println(input);
}
br.close();
} catch (IOException e) {
e.printStackTrace();
}
} catch (Exception e) {
e.printStackTrace();
}
}
// Create a trust manager that does not validate certificate chains
TrustManager[] trustAllCerts = new TrustManager[]{new X509TrustManager(){
#Override
public X509Certificate[] getAcceptedIssuers(){return null;}
#Override
public void checkClientTrusted(X509Certificate[] certs, String authType){}
#Override
public void checkServerTrusted(X509Certificate[] certs, String authType){}
}};
}

Android HttpsUrlConnection validate SSL

I have a question about validating SSL in Android using HttpsUrlConnection class. I need to connect to a web server using secure connection and validate the ssl. I have to check if it has expired or not, and also if the name of the certificate matches to a custom one. Optionally - it will be great if the SSL Certificate thumbnail can also be validated (to a predefined one). Here is the code which I'm using for now to connect to the server :
public void UseHttpsConnection(String url, String charset, String query) {
try {
final TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() {
#Override
public void checkClientTrusted( final X509Certificate[] chain, final String authType ) {
}
#Override
public void checkServerTrusted( final X509Certificate[] chain, final String authType ) {
}
#Override
public X509Certificate[] getAcceptedIssuers() {
return null;
}
} };
// Install the all-trusting trust manager
final SSLContext sslContext = SSLContext.getInstance( "TLS" );
sslContext.init( null, trustAllCerts, new java.security.SecureRandom() );
// Create an ssl socket factory with our all-trusting manager
final SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
System.setProperty("http.keepAlive", "false");
HttpsURLConnection connection = (HttpsURLConnection) new URL(url)
.openConnection();
connection.setSSLSocketFactory( sslSocketFactory );
connection.setDoOutput(true);
connection.setRequestMethod("POST");
connection.setRequestProperty("Charset", charset);
connection.setRequestProperty("Content-Type",
"application/x-www-form-urlencoded;charset=" + charset);
OutputStream output = null;
try {
output = connection.getOutputStream();
output.write(query.getBytes(charset));
} catch (IOException e) {
e.printStackTrace();
} finally {
if (output != null)
try {
output.close();
} catch (IOException logOrIgnore) {
logOrIgnore.printStackTrace();
}
}
int status = ((HttpsURLConnection) connection).getResponseCode();
Log.i("", "Status : " + status);
for (Entry<String, List<String>> header : connection
.getHeaderFields().entrySet()) {
Log.i("Headers",
"Headers : " + header.getKey() + "="
+ header.getValue());
}
InputStream response = new BufferedInputStream(
connection.getInputStream());
int bytesRead = -1;
byte[] buffer = new byte[30 * 1024];
while ((bytesRead = response.read(buffer)) > 0) {
byte[] buffer2 = new byte[bytesRead];
System.arraycopy(buffer, 0, buffer2, 0, bytesRead);
handleDataFromSync(buffer2);
}
connection.disconnect();
} catch (Exception e) {
e.printStackTrace();
}
}
I need a little help here, because I'm new with SSL validation and the basic things to do with it. Thanks for any kind of help!
If you want to validate the certificate the first thing you have to do is throw away that insecure TrustManager that trusts anything at all. Instead write one that checks the certificate received in the manner you require.

Categories