Android 7: Trust anchor for certification path not found - java

I have an application that connects to a server in the local ip network. This connection is TLS encrypted with a custom certificate. Following the guides on this side I made it work under all android version up to android 7. Sadly since Android 7 it is no longer working. Please does anybody know why this is not working anymore?
I found this article and included a network config file with the following code (I know this might not be secure, but first this has to work...):
<network-security-config>
<base-config>
<trust-anchors>
<!-- Only trust the CAs included with the app
for connections to internal.example.com -->
<certificates src="#raw/ca_cert" />
<certificates src="system"/>
</trust-anchors>
</base-config>
</network-security-config>
Sadly it is still not working. I also added it in the manifest as android:networkSecurityConfig="#xml/network_security_config".
The exception I am getting (Only Android 7+)!
java.security.cert.CertPathValidatorException: Trust anchor for certification path not found
This is the code for initializing my SSL Context
// Step 1: Initialize a ssl context with highest version
ssl_ctx = SSLContext.getInstance("TLSv1.2");
// Step 2: Add certificates to context
// Step 2.1 get private key
int pkeyId = context.getResources().getIdentifier("raw/clientkeypkcs", null, context.getPackageName());
InputStream fis = context.getResources().openRawResource(pkeyId);
DataInputStream dis = new DataInputStream(fis);
byte[] bytes = new byte[dis.available()];
dis.readFully(bytes);
ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
byte[] key = new byte[bais.available()];
KeyFactory kf = KeyFactory.getInstance("RSA");
bais.read(key, 0, bais.available());
bais.close();
PKCS8EncodedKeySpec keysp = new PKCS8EncodedKeySpec ( key );
PrivateKey ff = kf.generatePrivate (keysp);
//Step 2.2 get certificates
int caresId = context.getResources().getIdentifier("raw/ca_cert", null, context.getPackageName());
InputStream caCertIS = context.getResources().openRawResource(caresId);
CertificateFactory cacf = CertificateFactory.getInstance("X.509");
X509Certificate caCert = (X509Certificate)cacf.generateCertificate(caCertIS);
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
ks.load(null); // You don't need the KeyStore instance to come from a file.
ks.setCertificateEntry("caCert", caCert);
tmf.init(ks);
int clientresId = context.getResources().getIdentifier("raw/client_cert", null, context.getPackageName());
InputStream clientCertIS = context.getResources().openRawResource(clientresId);
CertificateFactory clientcf = CertificateFactory.getInstance("X.509");
X509Certificate clientCert = (X509Certificate)clientcf.generateCertificate(clientCertIS);
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
ks.setCertificateEntry("clientCert", clientCert);
kmf.init(ks, "***********".toCharArray());
Certificate[] chain = new Certificate[] { clientCert};
//ks.load(null); // You don't need the KeyStore instance to come from a file.
ks.setKeyEntry("importkey", ff, "***********".toCharArray(), chain );
ssl_ctx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);

You probably might have the user certificate missing:
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<base-config>
<trust-anchors>
<certificates src="system" />
<certificates src="user" />
</trust-anchors>
</base-config>
</network-security-config>

I faced this same issue on Android Oreo device
javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.
Its due to device date is set to old date for some other test purpose. I never know that could cause this kind of SSLHandshakeException issue. After lot of struggle, i just set device date back to current date. Solved the issue. :D
I think your scenario may be different and need to handle in other way. But I just posted this answer, Just in case it may help somebody.

Related

Certificate chain validation using java, checking revokation and OCSP status

I am new to the world of PKI , certificates in general. I am writing a service which needs to validate a chain of certiticates.
The general approach taken is as follows
a) Generate a List of certificates from the data sent
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(data);
CertPathValidatorResult certPathValidatorResult = null;
try {
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
List<X509Certificate> x509Certificates =
(List<X509Certificate>) certificateFactory.generateCertificates(byteArrayInputStream);
CertPath certPath = certificateFactory.generateCertPath(x509Certificates);
Load the JDK keystore, with something like this
//Load the JDK's cacerts keystore file
String filename =
System.getProperty("java.home")
+ "/lib/security/cacerts".replace('/', File.separatorChar);
FileInputStream is = new FileInputStream(filename);
KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType());
String password = "changeit";
keystore.load(is, password.toCharArray());
CertPathValidator certPathValidator = CertPathValidator.getInstance("PKIX");
PKIXParameters pkixParameters = new PKIXParameters(keystore);
//pkixParameters.setRevocationEnabled(false);
PKIXParameters certPathValidatorResult = certPathValidator.validate(certPath, pkixParameters);
I am assuming if this is not a valid chain it would throw an exception. Would this validation check expired Certificates, Valid Public Key ?
also I need to be able to find the the OCSP staus of a certificate or check if it is revoked>? How can this be done using the Cryptography API
Is the use fo bouncy castle recommended over the API ? Does Bouncy castle have a way to check CRL and OCSP status of a certificate?
Thanks for all the pointers and help in advance. Appreciate it.
Best Regards
It's correct, you can use CertificateFactory to load certificates chain.
If you want validate a chain of certiticates, you don't need a KeyStore. The certificates are validated with the certificate of autority who emit that certificate.
For example:
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(chain.getBytes());
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
List<X509Certificate> x509Certificates = (List<X509Certificate>) certificateFactory.generateCertificates(byteArrayInputStream);
x509Certificates.get(1).verify(x509Certificates.get(0).getPublicKey());
In this case you can use it to validate a certificate if you don't know the root ca.
You can check the period with
x509Certificates.get(1).getNotBefore()
and
x509Certificates.get(1).getNotAfter()
Is important validate the status of certificate.
Yes BouncyCastle is great library for it.

How to prevent root device to bypass certificate pinning in Android?

I am developing a project that require the Android app can prevent bypassing certificate pinning/trust a fake cert when doing network calling even in a rooted devices.
So far I can make it when the device is not rooted. I just need to prevent some bypassing method like using JustTrustMe in Xposed framework.
I am using retrofit and okHttp during network calling.
I have tried the using CertPinner in okHttp and its version is 3.10.0
and also tried to follow the code in android developer https://developer.android.com/training/articles/security-ssl#java
here is the sample code i have tried and copied from google
// 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);
And the cert pinning sample code
String hostname = "publicobject.com";
CertificatePinner certificatePinner = new CertificatePinner.Builder()
.add(hostname, "sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=")
.build();
OkHttpClient client = OkHttpClient.Builder()
.certificatePinner(certificatePinner)
.build();
Request request = new Request.Builder()
.url("https://" + hostname)
.build();
client.newCall(request).execute();
Both are the simplest code but none of it working
I would like to make it at least prevent some bypassing method like using JustTrustMe in Xposed framework/some easy automated bypassing method.
May i know if it is possible to do it or not, I have also tried some libs like
https://github.com/moxie0/AndroidPinning
suggested by JustTrustMe
https://github.com/Fuzion24/JustTrustMe
After some testing, load CAs from an InputStream would not work for all rooted devices with bypassing module enabled. It still works for normal device
The only way I could prevent it is to use public key cert pinning with proguard at the same time, hope this only help some ppl encounter the same problems.

CRL check during SSH connection

I have an Android application where I use HttpURLConnection for SSL connection to my server. The server certificate contains CRL Distribution Points with valid URI. This certificate was revoked and CRL by URI contains this information. But I don’t receive any exception during the handshake and I can receive any information from my server. I use Android 6 and 7.
I found some posts where developers write that Android disables revocation checks by default. Also, I saw some examples with setting PREFER_CRLS option to PKIXRevocationChecker and setting it to TrustManagerFactory but seems that it applied only for Java SE, when I try this code in my app I receive exception initializing TrustManagerFactory:
java.security.InvalidAlgorithmParameterException: Unsupported spec: javax.net.ssl.CertPathTrustManagerParameters#dccac9. Only android.security.net.config.RootTrustManagerFactorySpi$ApplicationConfigParameters supported
at android.security.net.config.RootTrustManagerFactorySpi.engineInit(RootTrustManagerFactorySpi.java:44)
network_security_config.xml file is correct:
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<base-config>
<trust-anchors>
<certificates src="#raw/ca_test"/>
</trust-anchors>
</base-config>
</network-security-config>
here is my code:
CertificateFactory cf = CertificateFactory.getInstance("X.509");
AssetManager am = getResources().getAssets();
Certificate ca;
try (InputStream caInput = am.open("ca_test.pem")) {
ca = cf.generateCertificate(caInput);
Log.d(LOG_TAG, "ca = " + ((X509Certificate) ca).getSubjectDN());
}
// Create a KeyStore containing our trusted CAs
String keyStoreType = KeyStore.getDefaultType();
KeyStore keyStore = KeyStore.getInstance(keyStoreType);
keyStore.load(null, null);
keyStore.setCertificateEntry("ca", ca);
KeyManagerFactory kmf =
KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
CertPathBuilder cpb = CertPathBuilder.getInstance("PKIX");
PKIXRevocationChecker rc =(PKIXRevocationChecker)cpb.getRevocationChecker();
rc.setOptions(EnumSet.of(
PKIXRevocationChecker.Option.PREFER_CRLS, // prefer CLR over OCSP
PKIXRevocationChecker.Option.SOFT_FAIL)); // handshake should not fail when CRL is not available
PKIXBuilderParameters pkixParams = new PKIXBuilderParameters(keyStore, new X509CertSelector());
pkixParams.addCertPathChecker(rc);
// Create a TrustManager that trusts the CAs in our KeyStore
String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
tmf.init(new CertPathTrustManagerParameters(pkixParams));
kmf.init(keyStore, null);
So I try to understand how can I enable CRL check for my app.
Can it be done via shell for root device?
Is there any way to override parameters for your own key store? Or there is any way to enable it for system android key store?
Also, I found the bug on this here: https://issuetracker.google.com/issues/36993981
but I don't see any updates for this issue.
Someone knows any solution for Android app developers?

Android: Installing .p12 certificate into device programmatically [duplicate]

I'm having trouble loading a .p12 certificate to my Android project. Here is a chunk of source code:
char[] password = "<my pass>".toCharArray();
FileInputStream fIn = new FileInputStream("<name of cert>");
KeyStore keystore = KeyStore.getInstance("PKCS12");
keystore.load(fIn, password);
On line 2 an error occurred opening cert file.
How can I properly add the cert file to my Android program?
...
In Android, I see people programmatically install keystore in the
following way (The code is from Android developer blog):
byte[] keystore = . . (read from a PKCS#12 keystore)
Intent installIntent = KeyChain.createInstallIntent();
installIntent.putExtra(KeyChain.EXTRA_PKCS12, keystore);
startActivityForResult(installIntent, INSTALL_KEYSTORE_CODE);
I also see people programmatically install only the certificate
wrapped inside keystore:
Intent intent = KeyChain.createInstallIntent();
intent.putExtra(KeyChain.EXTRA_CERTIFICATE, cert);
startActivity(intent);
...which leads --#Leem.fin
question
may find that the following link a better place to start:
https://developer.android.com/studio/publish/app-signing.html#signing-manually
Try this
File cert = new File("mnt/sdcard/" + filename + ".p12");
InputStream inputStreamFromDownload = null;
keyStore = KeyStore.getInstance("PKCS12");
inputStreamFromDownload = new BufferedInputStream(new FileInputStream(cert));
Log.i("Certificate", inputStreamFromDownload.available() + "");

How does a Java client automatically accept a self-signed certificate from the server

I am implementing a server that uses self-signed certificates. What is the best way to distribute the certificates to the clients? I could import the certificate into the java keystore and setup the client. But is there any way to avoid every client from importing the certificate manually. Can this be done automatically by the java client? I went through the JSSE reference but could not figure out how to do this. Would appreciate any help.
Regards,
Sampath.
Check out the KeyStore class. It allows you to manipulate Java keystores.
Code example:
KeyStore ks = KeyStore.getInstance("JKS");
ks.load(null, null); // Creates a new keystore
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("cert.cer")); // Or read from URL
CertificateFactory cf = CertificateFactory.getInstance("X.509");
Certificate cert = null;
if (bis.available() > 0) {
cert = cf.generateCertificate( bis );
ks.setCertificateEntry( "SGCert", cert );
}
ks.setCertificateEntry("SGCert", cert);
ks.store(new FileOutputStream("out.keystore"), "secret".toCharArray() );

Categories