SSLHandshakeException CertPathValidatorException on Android 2.3 but not on Android 4 - android

I have the problem that I get a SSLHandshakeException that only comes up on any Android 2.3 phone but not on newer (Android 4+) ones.
To accept the server's certificate I read the saved certificate from my ressources and connect it with the TrustManager used for this connection.
This practice is recommended by Google (9th April 2014): http://developer.android.com/training/articles/security-ssl.html
Exception stack trace output:
04-09 07:25:15.739: W/ShopLoader(2079):
javax.net.ssl.SSLHandshakeException:
java.security.cert.CertPathValidatorException:
Trust anchor for certification path not found.:
java.security.cert.CertPathValidatorException:
Trust anchor for certification path not found.
My code:
private static InputStream getSecureStream(String urlString) throws KeyStoreException, KeyManagementException, NoSuchAlgorithmException, IOException, CertificateException {
Context appCtx = MyBackgroundService.instance.context;
Resources res = appCtx.getResources();
InputStream is = res.openRawResource(res.getIdentifier("de_ssl_2014", "raw", appCtx.getPackageName()));
CertificateFactory cf = CertificateFactory.getInstance("X.509");
InputStream caInput = new BufferedInputStream(is);
Certificate ca;
try {
ca = cf.generateCertificate(caInput);
System.out.println("ca=" + ((X509Certificate) ca).getSubjectDN());
} 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
URL url = new URL(urlString);
HttpsURLConnection urlConnection =
(HttpsURLConnection)url.openConnection();
urlConnection.setSSLSocketFactory(context.getSocketFactory());
InputStream in = urlConnection.getInputStream();
return in;
}
Output before Exception:
04-09 07:58:46.869: I/System.out(3282): ca=CN=api.xxxxxxxx.de, OU=Domain Control Validated - RapidSSL(R), OU=See www.rapidssl.com/resources/cps (c)13, OU=GT40709841, OID.2.5.4.5=0JfY9NNUOGmytnt1WE//sOqJj7JzTtCg
OpenSSL connection returns this:
openssl s_client -connect api.xxxxxxxx.de:443
CONNECTED(00000003)
depth=1 /C=US/O=GeoTrust, Inc./CN=RapidSSL CA
verify error:num=20:unable to get local issuer certificate
verify return:0
---
Certificate chain
0 s:/serialNumber=0JfY9NNUOGmytnt1WE//sOqJj7JzTtCg/OU=GT40709841/OU=See www.rapidssl.com/resources/cps (c)13/OU=Domain Control Validated - RapidSSL(R)/CN=api.xxxxxxxx.de
i:/C=US/O=GeoTrust, Inc./CN=RapidSSL CA
1 s:/C=US/O=GeoTrust, Inc./CN=RapidSSL CA
i:/C=US/O=GeoTrust Inc./CN=GeoTrust Global CA
---
Server certificate
-----BEGIN CERTIFICATE-----
MIIFJjCCBA6gAwIBAgIDD2BUMA0GCSqGSIb3DQEBCwUAMDwxCzAJBgNVBAYTAlVT
[...]
jIhiyCMrPZ9VU6QqWQ7tslmtR54SpINwCzFVE6ySWC9CY8m8+PtWyfDDPwWzuJLO
UlxESqqQXD7iZJequBUoiLYCQTc7kofp/LU=
-----END CERTIFICATE-----
subject=/serialNumber=0JfY9NNUOGmytnt1WE//sOqJj7JzTtCg/OU=GT40709841/OU=See www.rapidssl.com/resources/cps (c)13/OU=Domain Control Validated - RapidSSL(R)/CN=api.xxxxxxxx.de
issuer=/C=US/O=GeoTrust, Inc./CN=RapidSSL CA
---
No client certificate CA names sent
---
SSL handshake has read 3129 bytes and written 316 bytes
---
New, TLSv1/SSLv3, Cipher is DHE-RSA-AES256-SHA
Server public key is 2048 bit
Compression: NONE
Expansion: NONE
SSL-Session:
Protocol : TLSv1
Cipher : DHE-RSA-AES256-SHA
Session-ID: C037859AE2DDF571DCC6D7C0C6C7D22CE34E7A3DC1BE6BB5E286B66A3EAA5492
Session-ID-ctx:
Master-Key: FE598D2380B14A0C73B6FBFBFB51C977579AE12CB37077769922D0E90C4AF5487B43EBC02433F1CAA6134CF60F4EBB34
Key-Arg : None
Start Time: 1397029699
Timeout : 300 (sec)
Verify return code: 20 (unable to get local issuer certificate)
---
closed

First copy your keystore and trustore to assets folder then try with this code (Get method):
KeyStore keyStore_manager = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore_manager.load(_context.getAssets().open(your_keystore),password.toCharArray());
KeyStore keyStore_trust = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore_trust.load(_context.getAssets().open(your_trustore),password.toCharArray());
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
keyManagerFactory.init(keyStore_manager, password.toCharArray());
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(keyStore_trust);
HttpsURLConnection.setDefaultHostnameVerifier(new NullHostNameVerifier());
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(keyManagerFactory.getKeyManagers(),tmf.getTrustManagers(), null);
HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory());
URL url = new URL(your_url);
HttpsURLConnection urlConnection = (HttpsURLConnection) requestedUrl.openConnection();
if (urlConnection instanceof HttpsURLConnection)
{
((HttpsURLConnection) urlConnection).setSSLSocketFactory(sslContext
.getSocketFactory());
}
urlConnection.setRequestMethod("GET");
urlConnection.setConnectTimeout(1500);
urlConnection.connect();
String data = "";
int http_status = urlConnection.getResponseCode();
if (http_status == 200)
{
//read data sent from server
InputStream response = new BufferedInputStream(
urlConnection.getInputStream());
int bytesRead = -1;
byte[] buffer = new byte[30 * 1024];
while ((bytesRead = response.read(buffer)) > 0)
{
data = new String(buffer, 0, bytesRead);
}
}
urlConnection.disconnect();

Related

Security with HTTPS and SSL

I have developed an android app where I am accessing one server url to access one xml file.In that server we have deployed one SelfSigned SSL Server Certificate.
I tried this code
// My Code:
My Code is here as follows
CertificateFactory cf = CertificateFactory.getInstance("X.509");
InputStream caInput = this.getResources().openRawResource(R.raw.server2); //server2 is the server2.crt Certificate file
Certificate ca;
try {
ca = cf.generateCertificate(caInput);
System.out.println("ca=" + ((X509Certificate) ca).getSubjectDN());
} 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
HttpsURLConnection urlConnection =
(HttpsURLConnection)url.openConnection();
urlConnection.setSSLSocketFactory(context.getSocketFactory());
InputStream in = urlConnection.getInputStream();
But I am getting "javax.net.ssl.SSLHandshakeException: Handshake failed" Error.PLease help me how to access that URL?

Android cannot generate certificate

I have problem while generating certificate.
Following is related code.
#SuppressLint("SdCardPath")
public HttpsURLConnection setUpHttpsConnection(String urlString)
{
try
{
CertificateFactory cf = CertificateFactory.getInstance("X.509","BC");
AssetManager assManager = context.getAssets();
InputStream caInput = assManager.open("testCert.pfx");
KeyStore keyStore = KeyStore.getInstance("PKCS12");
Certificate ca = cf.generateCertificate(caInput);
System.out.println("ca=" + ((X509Certificate) ca).getSubjectDN());
keyStore.load(null, null);
keyStore.setCertificateEntry("ca", ca);
String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
tmf.init(keyStore);
SSLContext context = SSLContext.getInstance("TLS");
context.init(null, tmf.getTrustManagers(), null);
URL url = new URL(urlString);
HttpsURLConnection urlConnection = (HttpsURLConnection)url.openConnection();
urlConnection.setSSLSocketFactory(context.getSocketFactory());
return urlConnection;
}
catch (Exception ex)
{
Log.e("fff", "Failed to establish SSL connection to server: " + ex.toString());
ex.printStackTrace();
return null;
}
}
Program gives an error at following line.
CertificateFactory cf = CertificateFactory.getInstance("X.509","BC");
Error trace ;
com.android.org.bouncycastle.jcajce.provider.asymmetric.x509.CertificateFactory$ExCertificateException
rer: Use EGL_SWAP_BEHAVIOR_PRESERVED: true
at com.android.org.bouncycastle.jcajce.provider.asymmetric.x509.CertificateFactory.engineGenerateCertificate(CertificateFactory.java:220)
at java.security.cert.CertificateFactory.generateCertificate(CertificateFactory.java:196)
at com.example.hilalinan.cert.ServiceCall.setUpHttpsConnection(ServiceCall.java:125)
at com.example.hilalinan.cert.ServiceCall.doInBackground(ServiceCall.java:58)
at android.os.AsyncTask$2.call(AsyncTask.java:295)
at java.util.concurrent.FutureTask.run(FutureTask.java:237)
at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:234)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1113)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:588)
at java.lang.Thread.run(Thread.java:818)
Caused by: java.lang.IllegalArgumentException: unknown object in getInstance: com.android.org.bouncycastle.asn1.ASN1Integer
at com.android.org.bouncycastle.asn1.ASN1Sequence.getInstance(ASN1Sequence.java:98)
at com.android.org.bouncycastle.asn1.x509.TBSCertificate.getInstance(TBSCertificate.java:64)
at com.android.org.bouncycastle.asn1.x509.Certificate.<init>(Certificate.java:61)
at com.android.org.bouncycastle.asn1.x509.Certificate.getInstance(Certificate.java:45)
at com.android.org.bouncycastle.jcajce.provider.asymmetric.x509.CertificateFactory.readDERCertificate(CertificateFactory.java:68)
at com.android.org.bouncycastle.jcajce.provider.asymmetric.x509.CertificateFactory.engineGenerateCertificate(CertificateFactory.java:215)
... 9 more
When I searched this issue I found this question in stackoverflow. I understood that my phone does not have BC provider in trusted credentials. OP of the question suggested that I add BC provider to my phone. But even though I install, how can I provide other users install that who are using my program.
Also when I googled it how to add BC to my list I couldnt find anything useful.
Is there any idea how to solve my issue?
Thanks in advance.
A pfx file is a PKCS#12 file which may contain multiple certificates and keys (unless you changed the file extension).
The code you use expects a simple certificate (.cer, .crt or .der) file.
Therefore you have to load it directly as PKCS12 keystore and not try to generate a certificate object from it:
InputStream caInput = assManager.open("testCert.pfx");
String pfxPassword = "password"; // change it to the correct password
keyStore.load(caInput, pfxpassword.toCharArray());
String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
tmf.init(keyStore);

HttpsURLConnection - Trusting server in Android application

I'm currently developing an Android app (Android Studio 2.0), that will connect to my server (Glassfish 4.1, Netbeans 8.1) through HTTPS (using javax.net.ssl.HttpsURLConnection). This is currently all being run on my local network (smartphone + laptop). The problem I'm getting now is:
javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.
I think because my Android application doesn't trust the server yet. How do I go about so it does indeed trust the server?
In some official documentation (http://developer.android.com/training/articles/security-ssl.html) and other guides, I've seen getting Keystores mentioned, or using .crt files. But where do I get either of those and how do I get them on my mobile device?
Currently my code looks like:
public static HttpsURLConnection setupHttpsConnection(URL url, Context context) {
try {
// Load CAs from an InputStream
CertificateFactory cf = CertificateFactory.getInstance("X.509");
InputStream caInput = new BufferedInputStream(context.getAssets().open("localhost.crt"));
Certificate ca;
try {
ca = cf.generateCertificate(caInput);
Log.d(LOG_TAG, "> setupHttpsConnection > ca.getSubjectDN = " + ((X509Certificate) ca).getSubjectDN());
} 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 sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, tmf.getTrustManagers(), null);
/*
HostnameVerifier hostnameVerifier = new HostnameVerifier() {
#Override
public boolean verify(String hostname, SSLSession session) {
HostnameVerifier hv =
HttpsURLConnection.getDefaultHostnameVerifier();
return hv.verify("192.168.0.121", session);
}
};
*/
// Tell the URLConnection to use a SocketFactory from our SSLContext
HttpsURLConnection urlConnection = (HttpsURLConnection) url.openConnection();
//urlConnection.setHostnameVerifier(hostnameVerifier);
urlConnection.setSSLSocketFactory(sslContext.getSocketFactory());
return urlConnection;
} catch (Exception e) {
e.printStackTrace();
Log.d(LOG_TAG, e.toString());
return null;
}
}
Then in another place I call:
urlConnection = Utility.setupHttpsConnection(url, context);
urlConnection.connect(); // <-- Exception thrown here!

Do we need to create any certificate to call https web service in Android

Hi I am working on android application where I am using https protocol in all the web service. so to communicate with https enabled server from android app do we need to add any certificate in my raw folder of android ?
If yes then what is the process of it. I checked many answers but people are simply ignoring the https procotol just accepting all certificates or by pass.
Thanks in advance.
Create BouncyCastle KeyStore, put your certificate in it (you can use openssl), later put created KeyStore into res/raw folder.
In app:
Load your keystore file into java KeyStore
Feed your HttpClient with your KeyStore
Example:
// Load CAs from an InputStream
// (could be from a resource or ByteArrayInputStream or ...)
CertificateFactory cf = CertificateFactory.getInstance("X.509");
// From https://www.washington.edu/itconnect/security/ca/load-der.crt
InputStream caInput = new BufferedInputStream(new FileInputStream("load-der.crt"));
Certificate ca;
try {
ca = cf.generateCertificate(caInput);
System.out.println("ca=" + ((X509Certificate) ca).getSubjectDN());
} 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
URL url = new URL("https://certs.cac.washington.edu/CAtest/");
HttpsURLConnection urlConnection =
(HttpsURLConnection)url.openConnection();
urlConnection.setSSLSocketFactory(context.getSocketFactory());
InputStream in = urlConnection.getInputStream();
copyInputStreamToOutputStream(in, System.out);
source:
https://developer.android.com/training/articles/security-ssl.html

Untrusted SSL certificate - Custom keystore issues

I have my own PKI infrastructure for my web server with a CA and an intermediate CA. Then I have a web certificate signed by the intermediate CA and I want my app to communicate with the server. According to the Developer docs the solution is to create my own keystore. So I bundled the root CA certificate with the app and tried to see if that would work. It didnt, and I got the following error:
javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.
So my question is this. Since the SSL certificate is signed by my intermediate CA, is that the certificate I should be importing into the custom key store, or do I need both, or is there something else wrong here?
Ultimately, with a SSL certificate signed by a CA not trusted by Android, is this the right way to go about it?
Thanks!
Here's the code that sets up the keystore manager.
public SSLContext getTrusted() throws Exception{
// Load CAs from an InputStream
CertificateFactory cf = CertificateFactory.getInstance("X.509");
AssetManager assManager = context.getAssets();
InputStream is = null;
try {
is = assManager.open("ca.cert.crt");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
InputStream caInput = new BufferedInputStream(is);
Certificate ca;
try {
ca = cf.generateCertificate(caInput);
Log.d("TrustMan", "ca=" + ((X509Certificate) ca).getSubjectDN());
} 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);
return context;
}
And then I am trying to use it as follows.
HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
TrustMan tm = new TrustMan(context);
SSLContext sslContext;
sslContext = tm.getTrusted();
connection.setSSLSocketFactory(sslContext.getSocketFactory());
The code did actually work, but not when I returned the SSLContext from my class. I adjusted the class to return the TrustManagerFactory instead, and using the intermediate CA certificate works fine now!
Thanks!

Categories

Resources