I have a website which I want to open in an Android Webview. The website uses a certificate emitted by COMODO RSA Domain Validation Secure Server CA. The problem is that I get an Unkown Certificate Error for devices all running a version less than (including) Android 5.
I serched in the documentation and as far as I understood the problem is that the CA was created before the release of Android 5.
I could do a handler.proceed(); on onReceivedSslError but I want to keep the app secure and I think Google can reject the app on play store anyway.
I found that I cound do something like this
// 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);
but the problem is that my code is like so
myWebView.loadUrl("https://site.domain.com")
How can I install the certificate of the CA within my Webview?
Thanks
You need to override method to your WebViewClient implementation. Try this:
public void onReceivedSslError(WebView view, SslErrorHandler handler,
SslError error) { handler.proceed(); }
Also, it could be with the SSL certificate installed on the website that you wish to run on webview. The SSL certificate provided by the authority is not supported on mobile, assuming if there's no error viewing the website on a desktop.
Related
I am currently attempting to connect to a server which has no domain name and is only reachable by its ip address. I previously attempted to do it by using the library volley, but after spending a day of research I couldn't figure out why the ssl handshake wouldn't work. after switching to Okhttp I got the warning:
javax.net.ssl.SSLPeerUnverifiedException: Hostname 185.101.92.193 not verified:
certificate: sha256/QMgPlAslWrBi1dd/P17AKxJCniO2RfHQ5MufVO5Xji4=
DN: 1.2.840.113549.1.9.1=#1619626c61636b6a61636b34323636323440676d61696c2e636f6d,CN=185.101.92.193,O=Internet Widgits Pty Ltd,L=Berlin,ST=Berlin,C=DE
subjectAltNames: []
Now this problem has already been addressed on github: https://github.com/square/okhttp/issues/1467
I have "resolved" the problem with the following code (look at HostnameVerifier at the bottom):
// loading CAs from an InputStream
try {
CertificateFactory cf = CertificateFactory.getInstance("X.509");
InputStream cert = context.getResources().openRawResource(R.raw.servercert);
Certificate ca;
try {
ca = cf.generateCertificate(cert);
} finally {
cert.close();
}
// creating a KeyStore containing our trusted CAs
String keyStoreType = KeyStore.getDefaultType();
KeyStore keyStore = KeyStore.getInstance(keyStoreType);
keyStore.load(null, null);
keyStore.setCertificateEntry("ca", ca);
// creating a TrustManager that trusts the CAs in our KeyStore
String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
tmf.init(keyStore);
// creating an SSLSocketFactory that uses our TrustManager
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, tmf.getTrustManagers(), null);
client = new OkHttpClient.Builder().sslSocketFactory(sslContext.getSocketFactory())
.hostnameVerifier(new HostnameVerifier() {
#Override
public boolean verify(String s, SSLSession sslSession) {
if(s.equals(myIPAddress)){
return true;
}else{
return false;
}
}
})
.build();
}catch (Exception e){
e.printStackTrace();
}
Now this kinda looks like bad practice to me and my actual question is: what problems could arise from implementing the HostnameVerifier like this (security-wise) and how could I solve this matter in a more sophisticated way?
There is nothing to do with the domain name, the only issue is that your application (android) can't verify the certificate because it's private (self signed certificate). What you did in your code is just trying to bypass the validation process by overriding the SSLFactory and created a new one that uses your CAs.
Check android documentation below:
https://developer.android.com/training/articles/security-ssl.html#CommonProblems
You can continue of what you have. or buy a certificate and then there is no need for this code at all.
My app connects to mqtt broker using certificate file through ssl connection.
I setup the certificate file (.crt) and the key file (.key) in my broker.
I use Paho Mqtt Client api, and this api needs a SSLSocketFactory to establish connection.
I use a Google example to create a SSLSocketFactory instance with a TrustManager who trust our CA in a keystore.
// Load CAs from an InputStream
// (could be from a resource or ByteArrayInputStream or ...)
CertificateFactory cf = CertificateFactory.getInstance("X.509");
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);
Everything works fine right now. BUT, the certificate is going to expire in 5/30/2017.
How do i must deal with the certificate in the client side (Android)?
I've have tested in testing environment with a new certificate. The app can't connect to broker. Exception is:
java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.
How can a deal with certificate renewal on Android?
I have request a new certificate using the same .key file and all clients did not have any issue connecting to broker with the new .crt.
But, is it safe to use the same key for certificate renewal?
It throws an exception like below
javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.
This Android program connects to my ASP.NET MVC deployed on IIS , and I create the .csr , .key , .crt file of my CA and my server's certificate using OpenSSL.
And I convert the .crt file of my server's certificate to .pfx format and import it in IIS .
I found the solution on Android Developer .But my code is similar to the code it lists .
CertificateFactory cf = CertificateFactory.getInstance("X.509","BC");
InputStream caInput = getResources().openRawResource(R.raw.root); //here "R.raw.root" is a .bks file converted from my CA's .crt file , and a .bks file converted from my server's .crt file , but both throw this exception
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("BKS");
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://172.18.13.178:63123/Home/Test");
HttpsURLConnection urlConnection =(HttpsURLConnection) url.openConnection();
urlConnection.setSSLSocketFactory(context.getSocketFactory());
InputStream in = urlConnection.getInputStream();
//omit other code
}
The Android Developer says this is the solution of this error , but I write my code similar to it , but it fails.
So anyone tell me where the problem is ? The way Android Develop gives does not work .
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
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!