HttpsURLConnection - Trusting server in Android application - android

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!

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?

MediaPlayer with custom socketFactory

I have a HTTPS MEDIA server with self signed certificate from which I want to stream audio in my Android app. As per this link from developer.android.com, to allow self signed certificate, I have generated a certificate by reading the .crt file, created a keystore with this certificate and initialized this keyStore to a TrustManagerFactory.
CertificateFactory cf = CertificateFactory.getInstance("X.509");
InputStream caInput = new BufferedInputStream(getResources().openRawResource(R.raw.certificate));
Certificate ca;
try {
ca = cf.generateCertificate(caInput);
Log.d(TAG, "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);
The link describes an example to use this custom SSLContext with an HttpsURLConnection object.
// 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();
But in my app for HTTP, I'm using MediaPlayer class by setting the audio link as below.
MediaPlayer mediaPlayer = new MediaPlayer();
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mediaPlayer.setDataSource(address); //address is a String variable like http://example.com/audio.aac
The question is, how do i use the above SSLContext's socketFactory object with the MediaPlayer, so that the MediaPlayer could use HTTPS audio source.
To use this custom SSLContext connection, we have to set the socketFactory object to the HttpsURLConnection before providing the source to the media player.
HttpsURLConnection.setDefaultSSLSocketFactory(context.getSocketFactory());
This allows the MediaPlayer to use the connection with our self signed certificate.
Other than this, I also had to provide a custom HostnameVerifier implementation to verify the subjectAlternateNames manually.
final HostnameVerifier hv =
HttpsURLConnection.getDefaultHostnameVerifier();
HostnameVerifier hostnameVerifier = new HostnameVerifier() {
#Override
public boolean verify(String hostname, SSLSession session) {
return hv.verify("www.youdomainname.com", session);
}
};
And this custom HostnameVerifier could be linked to the MediaPlayer by the below code.
HttpsURLConnection.setDefaultHostnameVerifier(hostnameVerifier);

How to connect an SSL enabled asmx web service with android

i'm trying to connect an asmx web service which has SSL enabled certificate from symantec. The web service works fine in browser with green indication. But unable to connect the webservice with android using ksoap library. I have added the public key of certificate(.cer file) in the aseets folder and added it to the trustmanager.
Here is the complete solution for how to handle SSL over android and c# webservice by using ksoap.
customise your org.ksoap2.transport.org.ksoap2.transport class constructor like this
public HttpTransportSE(Certificate ca, String url) {
super(ca, url);
}
2. change some codes in org.ksoap2.transport.ServiceConnectionSE class
public ServiceConnectionSE(Certificate ca, String url) throws IOException
{
// Create a KeyStore containing our trusted CAs
String keyStoreType = KeyStore.getDefaultType();
KeyStore keyStore;
SSLContext context = null;
try {
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
context = SSLContext.getInstance("TLS");
context.init(null, tmf.getTrustManagers(), null);
} catch (KeyStoreException | NoSuchAlgorithmException | CertificateException | KeyManagementException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// Tell the URLConnection to use a SocketFactory from our SSLContext
connection = (HttpsURLConnection) new URL(url).openConnection();
connection.setSSLSocketFactory(context.getSocketFactory());
connection.setUseCaches(false);
connection.setDoOutput(true);
connection.setDoInput(true);
}
And send your Certificate ca
CertificateFactory cf = CertificateFactory.getInstance("X.509");
AssetManager assetManager = getAssets();
InputStream caInput = new BufferedInputStream(assetManager.open("your_cert.cer"));
Certificate ca = cf.generateCertificate(caInput);
AndroidHttpTransport androidHttpTransport = new AndroidHttpTransport(ca, url);

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!

SSLHandshakeException CertPathValidatorException on Android 2.3 but not on Android 4

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();

Categories

Resources