How to connect to the SSL server in android [duplicate] - java

This question already has answers here:
HTTPS GET (SSL) with Android and self-signed server certificate
(7 answers)
Closed 8 years ago.
I need to connect the server which is having a URL like this:
https://www.abc.xxx/
When I tried to connect it through default httpClient, its throwing me SSL certificate exception. And I dont want to bypass SSL certificate also.
Can somebody please let me know whats is the procedure to connect to web server which have self signed SSL certificate.Do I need to get the certificate/key from the server.
If I get the key/certificate how to use that key while making HTTP request.

You can put your CA certificate in res/raw, and use it to connect the Self-Signed web server as follow:
sslSocketFactory = new MySSLSocketFactory(context);
schemeRegistry.register(new Scheme("https", sslSocketFactory, 443));
ClientConnectionManager cm = new ThreadSafeClientConnManager(params,
schemeRegistry);
mHttpClient = new DefaultHttpClient(cm, params);
And MySSLSocketFactory:
public class MySSLSocketFactory extends SSLSocketFactory {
private SSLContext sslContext;
public MySSLSocketFactory(Context context)
throws NoSuchAlgorithmException, KeyManagementException,
KeyStoreException, UnrecoverableKeyException, CertificateException,
IOException {
super(null);
sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, new TrustManager[] {
new MyTrustManager(context)
}, null);
}
#Override
public Socket createSocket() throws IOException {
// TODO Auto-generated method stub
return sslContext.getSocketFactory().createSocket();
}
#Override
public Socket createSocket(Socket socket, String host, int port,
boolean autoClose) throws IOException, UnknownHostException {
// TODO Auto-generated method stub
return sslContext.getSocketFactory().createSocket(socket, host, port,
autoClose);
}
}
Now is MyTrustManager:
public class MyTrustManager implements X509TrustManager {
private X509TrustManager defaultTrustManager;
private X509TrustManager myTrustManager;
private X509Certificate[] acceptedIssuers;
public MyTrustManager(Context context) throws KeyStoreException,
NoSuchAlgorithmException, CertificateException, IOException {
defaultTrustManager = getDefaultManager();
myTrustManager = getLocalManager(context);
if (defaultTrustManager == null || myTrustManager == null) {
throw new IOException("Couldn't load X509TrustManager");
}
ArrayList<X509Certificate> acceptedIssuersList = new ArrayList<X509Certificate>();
this.addToAccepted(acceptedIssuersList, defaultTrustManager);
this.addToAccepted(acceptedIssuersList, myTrustManager);
acceptedIssuers = acceptedIssuersList
.toArray(new X509Certificate[acceptedIssuersList.size()]);
}
private void addToAccepted(
ArrayList<X509Certificate> x509Certificates,
X509TrustManager x509TrustManager) {
for (X509Certificate x509Certificate : x509TrustManager
.getAcceptedIssuers()) {
x509Certificates.add(x509Certificate);
}
}
private X509TrustManager getX509TrustManager(
TrustManagerFactory trustManagerFactory) {
TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
for (TrustManager trustManager : trustManagers) {
if (trustManager instanceof X509TrustManager) {
return (X509TrustManager) trustManager;
}
}
return null;
}
private X509TrustManager getDefaultManager() throws KeyStoreException,
NoSuchAlgorithmException {
TrustManagerFactory trustManagerFactory = TrustManagerFactory
.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init((KeyStore) null);
return getX509TrustManager(trustManagerFactory);
}
private X509TrustManager getLocalManager(Context context)
throws CertificateException, NoSuchAlgorithmException,
KeyStoreException, IOException {
if (context == null) {
return null;
}
InputStream inputStream = context
.getResources().openRawResource(
R.raw.ca);
CertificateFactory certificateFactory = CertificateFactory
.getInstance("X.509");
X509Certificate x509Certificate = (X509Certificate) certificateFactory
.generateCertificate(inputStream);
TrustManagerFactory trustManagerFactory = TrustManagerFactory
.getInstance(TrustManagerFactory.getDefaultAlgorithm());
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(null);
keyStore.setCertificateEntry("ca", x509Certificate);
trustManagerFactory.init(keyStore);
return getX509TrustManager(trustManagerFactory);
}
#Override
public void checkClientTrusted(X509Certificate[] ax509certificate, String s)
throws CertificateException {
// TODO Auto-generated method stub
try {
myTrustManager.checkClientTrusted(ax509certificate, s);
} catch (CertificateException e) {
// TODO Auto-generated catch block
e.printStackTrace();
defaultTrustManager.checkClientTrusted(ax509certificate, s);
}
}
#Override
public void checkServerTrusted(X509Certificate[] ax509certificate, String s)
throws CertificateException {
// TODO Auto-generated method stub
try {
myTrustManager.checkServerTrusted(ax509certificate, s);
} catch (CertificateException e) {
// TODO Auto-generated catch block
e.printStackTrace();
defaultTrustManager.checkServerTrusted(ax509certificate, s);
}
}
public X509Certificate[] getAcceptedIssuers() {
// TODO Auto-generated method stub
return acceptedIssuers;
}
}

Related

AWS MQTT Websocket issue

I am trying use AWS connection keys in Android for connection with Device using MQTT websocket but did not find a solution yet, please help me on that. If any one provide MQTT over SSL/TLS with 3 certificate AWS (ca,cert,private) android code.
final MqttAndroidClient mqttAndroidClient = new MqttAndroidClient(MainActivity.this, "ssl://" + pref.getMqttUrl(), clientId, persistence);
try {
String clientId = MqttClient.generateClientId();
MqttConnectOptions connectionOptions = new MqttConnectOptions();
connectionOptions.setCleanSession(true);
Log.e("Test", "ssl://" + pref.getMqttUrl());
try {
InputStream trustStoresIs = context.getResources().openRawResource(R.raw.ca_key);
String trustStoreType = KeyStore.getDefaultType();
KeyStore trustStore = KeyStore.getInstance(trustStoreType);
trustStore.load(trustStoresIs, context.getString(R.string.bks_password).toCharArray());
String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
tmf.init(trustStore);
InputStream keyStoreStream = context.getResources().openRawResource(R.raw.user_cer_key);
KeyStore keyStore = null;
keyStore = KeyStore.getInstance("BKS");
keyStore.load(keyStoreStream, context.getString(R.string.bks_password).toCharArray());
KeyManagerFactory keyManagerFactory = null;
keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
keyManagerFactory.init(keyStore, context.getString(R.string.bks_password).toCharArray());
SSLContext context = SSLContext.getInstance("SSL");
context.init(keyManagerFactory.getKeyManagers(), tmf.getTrustManagers(), null);
SSLSocketFactory sslsocketfactory = (SSLSocketFactory) context.getSocketFactory();
connectionOptions.setSocketFactory(sslsocketfactory);
} catch (KeyManagementException | CertificateException | KeyStoreException | IOException | NoSuchAlgorithmException | UnrecoverableKeyException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
mqttAndroidClient.connect(connectionOptions, null, new IMqttActionListener() {
#Override
public void onSuccess(IMqttToken asyncActionToken) {
Log.e("Mqtt","Connection Success!");
}
#Override
public void onFailure(IMqttToken asyncActionToken, Throwable exception) {
Log.e("Mqtt","Connection Failure!");
}
});
mqttAndroidClient.setCallback(new MqttCallback() {
#Override
public void connectionLost(Throwable cause) {
Log.e("Mqtt","Connection was lost!");
}
#Override
public void messageArrived(String topic, MqttMessage message) throws Exception {
}
#Override
public void deliveryComplete(IMqttDeliveryToken token) {
System.out.println("mqtt Delivery Complete!");
}
});
} catch (Exception ex) {
ex.printStackTrace();
}
Got error like
Mqttjavax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.
Thanks.
The error implies your truststore doesn't have either the right or the full certificate chain for to authenticate the server.
The following line bothers me:
InputStream trustStoresIs =
context.getResources().openRawResource(R.raw.ca_key);
It implys you are loading a key not a certificate for your CA chain. Also CA chains tend to have more than one layer (Primary CA cert signs Intermediate CA cert) so I would expect you to need to add more than one cert to the trust store.

Handshake failed Android 23

I've got a problem with Keystores in Android.
I'm trying to connect a client in android to a server in java. My code is working well with API from 15 to 22 of android, but not with the new API 23 update:
i've got the error on the android client:
javax.net.ssl.SSLHandshakeException: Handshake failed
and this error on the server:
javax.net.ssl.SSLHandshakeException: no cipher suites in common
Here's my code, that was working well with API 22 or before:
in client, R.raw.publickey is the public .bks certificate, and R.raw.publickey_v1 is the older version of .bks for compatibility with API 15.
Server:
public static SSLServerSocket getServerSocketWithCert(int port, InputStream pathToCert, String passwordFromCert) throws IOException,
KeyManagementException, NoSuchAlgorithmException, CertificateException, KeyStoreException, UnrecoverableKeyException{
TrustManager[] tmm;
KeyManager[] kmm;
KeyStore ks = KeyStore.getInstance("JKS");
ks.load(pathToCert, passwordFromCert.toCharArray());
tmm=tm(ks);
kmm=km(ks, passwordFromCert);
SSLContext ctx = SSLContext.getInstance("TLS");
ctx.init(kmm, tmm, null);
SSLServerSocketFactory socketFactory = (SSLServerSocketFactory) ctx.getServerSocketFactory();
SSLServerSocket ssocket = (SSLServerSocket) socketFactory.createServerSocket(port);
return ssocket;
}
private static TrustManager[] tm(KeyStore keystore) throws NoSuchAlgorithmException, KeyStoreException {
TrustManagerFactory trustMgrFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustMgrFactory.init(keystore);
return trustMgrFactory.getTrustManagers();
};
private static KeyManager[] km(KeyStore keystore, String password) throws NoSuchAlgorithmException, KeyStoreException, UnrecoverableKeyException {
KeyManagerFactory keyMgrFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
keyMgrFactory.init(keystore, password.toCharArray());
return keyMgrFactory.getKeyManagers();
};
public static void main(String[] args){
SSLServerSocket ss = null;
try {
ss = getServerSocketWithCert(12345, Server.class.getResourceAsStream("/privateKey.store"), "password");
} catch(BindException e){
e.printStackTrace();
System.exit(1);
} catch (KeyManagementException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (CertificateException e) {
e.printStackTrace();
} catch (KeyStoreException e) {
e.printStackTrace();
} catch (UnrecoverableKeyException e) {
e.printStackTrace();
}
while(true){
SSLSocket s = ss.accept();
new DataOutputStream(s.getOutputStream()).writeUTF("test");
//TODO ERROR IS APPENING HERE
}
}
Client:
public static SSLSocket getSocketWithCert(InetAddress ip, int port, InputStream pathToCert, String passwordFromCert) throws IOException,
KeyManagementException, NoSuchAlgorithmException, CertificateException, KeyStoreException {
TrustManager[] tmm;
KeyStore ks = KeyStore.getInstance("BKS");
ks.load(pathToCert, passwordFromCert.toCharArray());
tmm=tm(ks);
SSLContext ctx = SSLContext.getInstance("TLS");
ctx.init(null, tmm, null);
SSLSocketFactory SocketFactory = (SSLSocketFactory) ctx.getSocketFactory();
SSLSocket socket = (SSLSocket) SocketFactory.createSocket();
socket.connect(new InetSocketAddress(ip, port), 5000);
return socket;
}
private static TrustManager[] tm(KeyStore keystore) throws NoSuchAlgorithmException, KeyStoreException {
TrustManagerFactory trustMgrFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustMgrFactory.init(keystore);
return trustMgrFactory.getTrustManagers();
};
public static void(String[] args){
int id;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN){
id = R.raw.publickey;
} else {
id = R.raw.publickey_v1;
}
try {
Socket s = SSLSocketKeystoreFactory.getSocketWithCert("myip", 12345, HackerMainActivity.this.getResources().openRawResource(id), "password");
} catch (UnknownHostException | SecurityException e) {
e.printStackTrace();
return;
} catch(SocketTimeoutException e){
e.printStackTrace();
return;
} catch (KeyManagementException | NoSuchAlgorithmException | CertificateException | KeyStoreException e) {
e.printStackTrace();
}
DataInputStream in = new DataInputStream(s.getInputStream());
//TODO ERROR IS APPENING HERE
}
Thanks a lot for you help !
I finally managed to fix it...
The error was that Android 6.0 is the version that dropped support for SHA-1.
For the one who had the same error, just re-create your certificates using SHA-256...

Android 5.0 SSL Connectivity Issue

I am trying to interact with a webservice which is a HTTPS call that works totally fine on different variants of 4.0(I havent checked it below 4.0 so I cant say about them) and its perfectly working. The issue I am facing is on Android 5.0 and the device I was able to grab was Nexus 5 and below is the exception i get when doing connectivity
javax.net.ssl.SSLPeerUnverifiedException: No peer certificate
at org.apache.harmony.xnet.provider.jsse.SSLSessionImpl.getPeerCertificates(SSLSessionImpl.java:146)
at org.apache.http.conn.ssl.AbstractVerifier.verify(AbstractVerifier.java:93)
After tonnes of searching and analyzing our production server SSL certificate i figured out that the server accept TLSv1 and the only cipher suite it supports is TLS_RSA_WITH_3DES_EDE_CBC_SHA. Though i understand that its not safe and it should be upgraded but right now i have to find out some way to get my Android app connected with the server.
I tried through the way suggested on this page
https://code.google.com/p/android-developer-preview/issues/attachmentText?id=1200&aid=12000009000&name=CompatSSLSocketFactory.java&token=ABZ6GAcWKpRZhuG6Skof32VtvF0Lzv3Z-A%3A1435550700632
And replaced my required algorithm i.e TLS_RSA_WITH_3DES_EDE_CBC_SHA but now the problem is that i am seeing this exception
Caused by: java.lang.IllegalArgumentException: cipherSuite
TLS_RSA_WITH_3DES_EDE_CBC_SHA is not supported.
at com.android.org.conscrypt.NativeCrypto.checkEnabledCipherSuites(NativeCrypto.java:1091)
at com.android.org.conscrypt.SSLParametersImpl.setEnabledCipherSuites(SSLParametersImpl.java:244)
at com.android.org.conscrypt.OpenSSLSocketImpl.setEnabledCipherSuites(OpenSSLSocketImpl.java:822)
So according to this exception the cipher suite i required is not supported by Android 5.0. But i got puzzled after seeing it in Android 5.0's supported list on this page
http://developer.android.com/reference/javax/net/ssl/SSLEngine.html
Anybody any idea whats this mystery?
I got the answer finally after working out on the issue for three days. Posting out the correct solution for people who gets stuck in a similar issue in future
First implement CustomTrustManager
public class CustomX509TrustManager implements X509TrustManager {
#Override
public void checkClientTrusted(X509Certificate[] chain, String authType)
throws CertificateException {
}
#Override
public void checkServerTrusted(X509Certificate[] certs,
String authType) throws CertificateException {
// Here you can verify the servers certificate. (e.g. against one which is stored on mobile device)
// InputStream inStream = null;
// try {
// inStream = MeaApplication.loadCertAsInputStream();
// CertificateFactory cf = CertificateFactory.getInstance("X.509");
// X509Certificate ca = (X509Certificate)
// cf.generateCertificate(inStream);
// inStream.close();
//
// for (X509Certificate cert : certs) {
// // Verifing by public key
// cert.verify(ca.getPublicKey());
// }
// } catch (Exception e) {
// throw new IllegalArgumentException("Untrusted Certificate!");
// } finally {
// try {
// inStream.close();
// } catch (IOException e) {
// e.printStackTrace();
// }
// }
}
public X509Certificate[] getAcceptedIssuers() {
return null;
}
}
Than implement your own Socket Factory
public class CustomSSLSocketFactory extends SSLSocketFactory {
SSLContext sslContext = SSLContext.getInstance("TLS");
public CustomSSLSocketFactory(KeyStore truststore)
throws NoSuchAlgorithmException, KeyManagementException,
KeyStoreException, UnrecoverableKeyException {
super(truststore);
TrustManager tm = new CustomX509TrustManager();
sslContext.init(null, new TrustManager[] { tm }, null);
}
public CustomSSLSocketFactory(SSLContext context)
throws KeyManagementException, NoSuchAlgorithmException,
KeyStoreException, UnrecoverableKeyException {
super(null);
sslContext = context;
}
#Override
public Socket createSocket(Socket socket, String host, int port,
boolean autoClose) throws IOException, UnknownHostException {
Socket newSocket = sslContext.getSocketFactory().createSocket(socket, host, port,
autoClose);
((SSLSocket) newSocket).setEnabledCipherSuites(((SSLSocket) newSocket).getSupportedCipherSuites());
AdjustSocket(newSocket);
return newSocket;
}
#Override
public Socket createSocket() throws IOException {
Socket socket = sslContext.getSocketFactory().createSocket();
((SSLSocket) socket).setEnabledCipherSuites(((SSLSocket) socket).getSupportedCipherSuites());
adjustSocket(socket);
return socket;
}
private void adjustSocket(Socket socket)
{
String[] cipherSuites = ((SSLSocket) socket).getSSLParameters().getCipherSuites();
ArrayList<String> cipherSuiteList = new ArrayList<String>(Arrays.asList(cipherSuites));
cipherSuiteList.add("TLS_RSA_WITH_3DES_EDE_CBC_SHA");
cipherSuites = cipherSuiteList.toArray(new String[cipherSuiteList.size()]);
((SSLSocket) socket).getSSLParameters().setCipherSuites(cipherSuites);
String[] protocols = ((SSLSocket) socket).getSSLParameters().getProtocols();
ArrayList<String> protocolList = new ArrayList<String>(Arrays.asList(protocols));
for (int ii = protocolList.size() - 1; ii >= 0; --ii )
{
if ((protocolList.get(ii).contains("SSLv3")) || (protocolList.get(ii).contains("TLSv1.1")) || (protocolList.get(ii).contains("TLSv1.2")))
protocolList.remove(ii);
}
protocols = protocolList.toArray(new String[protocolList.size()]);
((SSLSocket)socket).setEnabledProtocols(protocols);
}
}
Now add a function in the class to create a HttpClient
public HttpClient createHttpClient(){
try {
KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
trustStore.load(null, null);
CustomSSLSocketFactory sf = new CustomSSLSocketFactory(trustStore);
sf.setHostnameVerifier(CustomSSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
HttpParams params = new BasicHttpParams();
HttpConnectionParams.setConnectionTimeout(params, 15000);
HttpConnectionParams.setSoTimeout(params, 5000);
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);
return new DefaultHttpClient(ccm, params);
} catch (Exception e) {
return new DefaultHttpClient();
}
And now write below lines to call the server/webservice
HttpClient httpClient = createHttpClient();
HttpPost httpost = new HttpPost(url);
HttpResponse response = null;
try {
response = httpClient.execute(httpost);
StatusLine statusLine = response.getStatusLine();
} catch (IOException e) {
e.printStackTrace();
}

Programmatically add a certificate authority while keeping Android system SSL certificates

There are lots of questions about this topic on StackOverflow, but I do not seem to find one related to my problem.
I have an Android application that needs to communicate with HTTPS servers: some signed with a CA registered in the Android system keystore (common HTTPS websites), and some signed with a CA I own but not in the Android system keystore (a server with an autosigned certificate for instance).
I know how to add my CA programmatically and force every HTTPS connection to use it. I use the following code:
public class SslCertificateAuthority {
public static void addCertificateAuthority(InputStream inputStream) {
try {
// Load CAs from an InputStream
// (could be from a resource or ByteArrayInputStream or ...)
CertificateFactory cf = CertificateFactory.getInstance("X.509");
InputStream caInput = new BufferedInputStream(inputStream);
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
HttpsURLConnection.setDefaultSSLSocketFactory(context.getSocketFactory());
} catch (CertificateException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (KeyStoreException e) {
e.printStackTrace();
} catch (KeyManagementException e) {
e.printStackTrace();
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
However, doing that disables the use of the Android system keystore, and I cannot query HTTPS sites signed with other CA any more.
I tried to add my CA in the Android keystore, using:
KeyStore.getInstance("AndroidCAStore")
... but I cannot then add my CA in it (an exception is launched).
I could use the instance method HttpsURLConnection.setSSLSocketFactory(...) instead of the static global HttpsURLConnection.setDefaultSSLSocketFactory(...) to tell on a case by case basis when my CA has to be used.
But it isn't practical at all, all the more since sometimes I cannot pass a preconfigured HttpsURLConnection object to some libraries.
Some ideas about how I could do that?
EDIT - ANSWER
Ok, following the given advice, here is my working code. It might need some enhancements, but it seems to work as a starting point.
public class SslCertificateAuthority {
private static class UnifiedTrustManager implements X509TrustManager {
private X509TrustManager defaultTrustManager;
private X509TrustManager localTrustManager;
public UnifiedTrustManager(KeyStore localKeyStore) throws KeyStoreException {
try {
this.defaultTrustManager = createTrustManager(null);
this.localTrustManager = createTrustManager(localKeyStore);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
}
private X509TrustManager createTrustManager(KeyStore store) throws NoSuchAlgorithmException, KeyStoreException {
String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
tmf.init((KeyStore) store);
TrustManager[] trustManagers = tmf.getTrustManagers();
return (X509TrustManager) trustManagers[0];
}
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
try {
defaultTrustManager.checkServerTrusted(chain, authType);
} catch (CertificateException ce) {
localTrustManager.checkServerTrusted(chain, authType);
}
}
#Override
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
try {
defaultTrustManager.checkClientTrusted(chain, authType);
} catch (CertificateException ce) {
localTrustManager.checkClientTrusted(chain, authType);
}
}
#Override
public X509Certificate[] getAcceptedIssuers() {
X509Certificate[] first = defaultTrustManager.getAcceptedIssuers();
X509Certificate[] second = localTrustManager.getAcceptedIssuers();
X509Certificate[] result = Arrays.copyOf(first, first.length + second.length);
System.arraycopy(second, 0, result, first.length, second.length);
return result;
}
}
public static void setCustomCertificateAuthority(InputStream inputStream) {
try {
// Load CAs from an InputStream
// (could be from a resource or ByteArrayInputStream or ...)
CertificateFactory cf = CertificateFactory.getInstance("X.509");
InputStream caInput = new BufferedInputStream(inputStream);
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 and system CA
UnifiedTrustManager trustManager = new UnifiedTrustManager(keyStore);
// Create an SSLContext that uses our TrustManager
SSLContext context = SSLContext.getInstance("TLS");
context.init(null, new TrustManager[]{trustManager}, null);
// Tell the URLConnection to use a SocketFactory from our SSLContext
HttpsURLConnection.setDefaultSSLSocketFactory(context.getSocketFactory());
} catch (CertificateException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (KeyStoreException e) {
e.printStackTrace();
} catch (KeyManagementException e) {
e.printStackTrace();
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
It is an old question, but I met the same problem, so probably it is worth posting my answer. You tried to add your certificate to KeyStore.getInstance("AndroidCAStore"), but got an exception. Actually you should have done the opposite - add entries from that keystore to the one you created.
My code is a bit different from yours, I just post it for the sake of complete answer even though only middle part matters.
KeyStore keyStore=KeyStore.getInstance("BKS");
InputStream in=activity.getResources().openRawResource(R.raw.my_ca);
try
{
keyStore.load(in,"PASSWORD_HERE".toCharArray());
}
finally
{
in.close();
}
KeyStore defaultCAs=KeyStore.getInstance("AndroidCAStore");
if(defaultCAs!=null)
{
defaultCAs.load(null,null);
Enumeration<String> keyAliases=defaultCAs.aliases();
while(keyAliases.hasMoreElements())
{
String alias=keyAliases.nextElement();
Certificate cert=defaultCAs.getCertificate(alias);
try
{
if(!keyStore.containsAlias(alias))
keyStore.setCertificateEntry(alias,cert);
}
catch(Exception e)
{
System.out.println("Error adding "+e);
}
}
}
TrustManagerFactory tmf=TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(keyStore);
// Get a new SSL context
SSLContext ctx = SSLContext.getInstance("SSL");
ctx.init(null,tmf.getTrustManagers(),new java.security.SecureRandom());
return ctx.getSocketFactory();
This might be too late but this is a tried and tested approach that helps bypass the certificate check done by Java.
I cannot claim credit for this code, it was written by one of my colleagues :) . It can be used during development to test your code. In case you don't want to deal with certificates at all, you can make Java always certificates from any host for your HttpURLConnection object. Which seems to be exactly what you're trying to do here.
Here's a class that should help you do that :
import javax.net.ssl.*;
import java.net.HttpURLConnection;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
/***
* Should only be used in development, this class will allow connections to an HTTPS server with unverified certificates.
* obviously this should not be used in the real world
*/
public class TrustModifier {
private static final TrustingHostnameVerifier TRUSTING_HOSTNAME_VERIFIER = new TrustingHostnameVerifier();
private static SSLSocketFactory factory;
/**
* Call this with any HttpURLConnection, and it will modify the trust settings if it is an HTTPS connection.
*
* #param conn the {#link HttpURLConnection} instance
* #throws KeyManagementException if an error occurs while initializing the context object for the TLS protocol
* #throws NoSuchAlgorithmException if no Provider supports a TrustManagerFactorySpi implementation for the TLS protocol.
*/
public static void relaxHostChecking(HttpURLConnection conn) throws KeyManagementException, NoSuchAlgorithmException {
if (conn instanceof HttpsURLConnection) {
HttpsURLConnection httpsConnection = (HttpsURLConnection) conn;
SSLSocketFactory factory = prepFactory();
httpsConnection.setSSLSocketFactory(factory);
httpsConnection.setHostnameVerifier(TRUSTING_HOSTNAME_VERIFIER);
}
}
/**
* Returns an {#link SSLSocketFactory} instance for the protocol being passed, this represents a secure communication context
*
* #return a {#link SSLSocketFactory} object for the TLS protocol
* #throws NoSuchAlgorithmException if no Provider supports a TrustManagerFactorySpi implementation for the specified protocol.
* #throws KeyManagementException if an error occurs while initializing the context object
*/
static synchronized SSLSocketFactory prepFactory() throws NoSuchAlgorithmException, KeyManagementException {
if (factory == null) {
SSLContext ctx = SSLContext.getInstance("TLS");
ctx.init(null, new TrustManager[]{new AlwaysTrustManager()}, null);
factory = ctx.getSocketFactory();
}
return factory;
}
private static final class TrustingHostnameVerifier implements HostnameVerifier {
public boolean verify(String hostname, SSLSession session) {
return true;
}
}
private static class AlwaysTrustManager implements X509TrustManager {
public void checkClientTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {
}
public void checkServerTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {
}
public X509Certificate[] getAcceptedIssuers() {
return null;
}
}
}
All you need to do is call the function relaxHostChecking() like this :
if (conn instanceof HttpsURLConnection) {
TrustModifier.relaxHostChecking(conn);
}
This will result in java trusting whichever host you're trying to connect to using HttpURLConnection.

PKIX path building failed while making SSL connection

I'm integrating with a Merchant Account called CommWeb and I'm sending an SSL post to their URL (https://migs.mastercard.com.au/vpcdps). When I try to send the post, I get the following exception:
sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
The code (which I didn't write, and that already exists in our codebase) that performs the post is:
public static HttpResponse sendHttpPostSSL(String url, Map<String, String> params) throws IOException {
PostMethod postMethod = new PostMethod(url);
for (Map.Entry<String, String> entry : params.entrySet()) {
postMethod.addParameter(entry.getKey(), StringUtils.Nz(entry.getValue()));
}
HttpClient client = new HttpClient();
int status = client.executeMethod(postMethod);
if (status == 200) {
StringBuilder resultBuffer = new StringBuilder();
resultBuffer.append(postMethod.getResponseBodyAsString());
return new HttpResponse(resultBuffer.toString(), "");
} else {
throw new IOException("Invalid response code: " + status);
}
}
The documentation for the Merchant Account integration says nothing about certificates. They did provide some sample JSP code that seems to blindly accept certificates:
<%! // Define Static Constants
// ***********************
public static X509TrustManager s_x509TrustManager = null;
public static SSLSocketFactory s_sslSocketFactory = null;
static {
s_x509TrustManager = new X509TrustManager() {
public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[] {}; }
public boolean isClientTrusted(X509Certificate[] chain) { return true; }
public boolean isServerTrusted(X509Certificate[] chain) { return true; }
};
java.security.Security.addProvider(new com.sun.net.ssl.internal.ssl.Provider());
try {
SSLContext context = SSLContext.getInstance("TLS");
context.init(null, new X509TrustManager[] { s_x509TrustManager }, null);
s_sslSocketFactory = context.getSocketFactory();
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e.getMessage());
}
}
...
...
// write output to VPC
SSLSocket ssl = (SSLSocket)s_sslSocketFactory.createSocket(s, vpc_Host, vpc_Port, true);
ssl.startHandshake();
os = ssl.getOutputStream();
// get response data from VPC
is = ssl.getInputStream();
...
...
%>
Our webapp has a keystore, and I tried adding the certificate (which I exported from firefox) using the keytool command, but that didn't work and I got the same error. I've tried solutions on the web (importing the key and using System.setProperty) but that seems kind of clunky and it didn't work (gave me a NoSuchAlgorithmError). Any help is appreciated!
Evidently the valicert class 3 CA certificate is not in your default truststore (which is probably the cacerts file in your JRE lib/security directory, but see the JSSE documentation for the full story).
You could add this certificate to the cacerts file, but I don't recommend this. Instead, I think you should create your own truststore file (which can be a copy of the cacerts file) and add the valicert root ca to this. Then point to this file with the javax.net.ssl.trustStore system property.
I figure I should update this answer with what I actually did. Using the documentation that GregS provided, I created a trust manager for valicert. In the trust manager, I load the certificate files:
public class ValicertX509TrustManager implements X509TrustManager {
X509TrustManager pkixTrustManager;
ValicertX509TrustManager() throws Exception {
String valicertFile = "/certificates/ValicertRSAPublicRootCAv1.cer";
String commwebDRFile = "/certificates/DR_10570.migs.mastercard.com.au.crt";
String commwebPRODFile = "/certificates/PROD_10549.migs.mastercard.com.au.new.crt";
Certificate valicert = CertificateFactory.getInstance("X509").generateCertificate(this.getClass().getResourceAsStream(valicertFile));
Certificate commwebDR = CertificateFactory.getInstance("X509").generateCertificate(this.getClass().getResourceAsStream(commwebDRFile));
Certificate commwebPROD = CertificateFactory.getInstance("X509").generateCertificate(this.getClass().getResourceAsStream(commwebPRODFile));
KeyStore keyStore = KeyStore.getInstance("JKS");
keyStore.load(null, "".toCharArray());
keyStore.setCertificateEntry("valicert", valicert);
keyStore.setCertificateEntry("commwebDR", commwebDR);
keyStore.setCertificateEntry("commwebPROD", commwebPROD);
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance("PKIX");
trustManagerFactory.init(keyStore);
TrustManager trustManagers[] = trustManagerFactory.getTrustManagers();
for(TrustManager trustManager : trustManagers) {
if(trustManager instanceof X509TrustManager) {
pkixTrustManager = (X509TrustManager) trustManager;
return;
}
}
throw new Exception("Couldn't initialize");
}
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
pkixTrustManager.checkServerTrusted(chain, authType);
}
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
pkixTrustManager.checkServerTrusted(chain, authType);
}
public X509Certificate[] getAcceptedIssuers() {
return pkixTrustManager.getAcceptedIssuers();
}
}
Now, using this trust manager, I had to create a socket factory:
public class ValicertSSLProtocolSocketFactory implements ProtocolSocketFactory {
private SSLContext sslContext = null;
public ValicertSSLProtocolSocketFactory() {
super();
}
private static SSLContext createValicertSSLContext() {
try {
ValicertX509TrustManager valicertX509TrustManager = new ValicertX509TrustManager();
SSLContext context = SSLContext.getInstance("TLS");
context.init(null, new ValicertX509TrustManager[] { valicertX509TrustManager}, null);
return context;
}
catch(Exception e) {
Log.error(Log.Context.Net, e);
return null;
}
}
private SSLContext getSSLContext() {
if(this.sslContext == null) {
this.sslContext = createValicertSSLContext();
}
return this.sslContext;
}
public Socket createSocket(String host, int port, InetAddress clientHost, int clientPort) throws IOException {
return getSSLContext().getSocketFactory().createSocket(host, port, clientHost, clientPort);
}
public Socket createSocket(final String host, final int port, final InetAddress localAddress, final int localPort, final HttpConnectionParams params) throws IOException {
if(params == null) {
throw new IllegalArgumentException("Parameters may not be null");
}
int timeout = params.getConnectionTimeout();
SocketFactory socketFactory = getSSLContext().getSocketFactory();
if(timeout == 0) {
return socketFactory.createSocket(host, port, localAddress, localPort);
}
else {
Socket socket = socketFactory.createSocket();
SocketAddress localAddr = new InetSocketAddress(localAddress, localPort);
SocketAddress remoteAddr = new InetSocketAddress(host, port);
socket.bind(localAddr);
socket.connect(remoteAddr, timeout);
return socket;
}
}
public Socket createSocket(String host, int port) throws IOException {
return getSSLContext().getSocketFactory().createSocket(host, port);
}
public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException {
return getSSLContext().getSocketFactory().createSocket(socket, host, port, autoClose);
}
public boolean equals(Object obj) {
return ((obj != null) && obj.getClass().equals(ValicertSSLProtocolSocketFactory.class));
}
public int hashCode() {
return ValicertSSLProtocolSocketFactory.class.hashCode();
}
}
Now I just register a new protocol:
Protocol.registerProtocol("vhttps", new Protocol("vhttps", new ValicertSSLProtocolSocketFactory(), 443));
PostMethod postMethod = new PostMethod(url);
for (Map.Entry<String, String> entry : params.entrySet()) {
postMethod.addParameter(entry.getKey(), StringUtils.Nz(entry.getValue()));
}
HttpClient client = new HttpClient();
int status = client.executeMethod(postMethod);
if (status == 200) {
StringBuilder resultBuffer = new StringBuilder();
resultBuffer.append(postMethod.getResponseBodyAsString());
return new HttpResponse(resultBuffer.toString(), "");
} else {
throw new IOException("Invalid response code: " + status);
}
The only disadvantage is that I had to create a specific protocol (vhttps) for this particular certificate.

Categories