Getting OkHttp to accept self-signed certificate - android

I successfully got the server to use a certificate in the form of a JKS file. HTTPS is working as expected when used with web browsers and other web clients.
For Android, my team uses the following to persuade OkHttp to accept the certificate.
static KeyStore readKeyStore() throws KeyStoreException, CertificateException, NoSuchAlgorithmException
{
KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
// get user password and file input stream
char[] password = "password".toCharArray();
java.io.InputStream fis = null;
try {
fis = ServiceProducer.class.getClassLoader().getResourceAsStream("res/raw/keystore.jks");
ks.load(fis, password);
} catch (IOException e)
{
} finally
{
if (fis != null)
{
try
{
fis.close();
} catch (IOException e)
{
}
}
}
return ks;
}
The code that uses the key:
OkHttpClient.Builder builder = new OkHttpClient.Builder();
KeyStore keyStore = readKeyStore();
SSLContext sslContext = SSLContext.getInstance("SSL");
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(keyStore);
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
keyManagerFactory.init(keyStore, "password".toCharArray());
sslContext.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), new SecureRandom());
builder.sslSocketFactory(sslContext.getSocketFactory());
OkHttpClient client = builder.build();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://192.168.5.91:9443")
.addConverterFactory(JacksonConverterFactory.create())
.client(client)
.build();
However, accessing the service throws the following exception:
java.security.cert.CertPathValidationException: Trust anchor for certification path not found.
Have we done the certificate installation correctly? Or are we facing a different kind of problem?

Related

SSL Connection Error While making a connection

I am totally new on this and after some research, trying to make a SSL/TLS connection with server to allow SOAP call outside defined network. I am using OKHttp3 library to make the call and sharing a .P12 file with private key and setting a SSL Socket Factory as shown below
setupKeyCert(context);
final OkHttpClient client = new OkHttpClient();
client.newBuilder()
.connectTimeout(30, TimeUnit.SECONDS)
.sslSocketFactory(sslContext.getSocketFactory(), mainX509TrustManager);
and setting keystone and Trust Manager in
setupKeyCert()
as
try {
KeyStore keyStore = KeyStore.getInstance("PKCS12");
KeyManagerFactory keyManagerFactory = null;
if(buildEnvironment == "prod")
{
keyStore.load(context.getAssets().open(Constants.CERT_PROD_FILE), password);
keyManagerFactory = KeyManagerFactory.getInstance("X509");
keyManagerFactory.init(keyStore, Constants.CERT_PROD_VALUE.toCharArray());
} else
{
keyStore.load(context.getAssets().open(Constants.CERT_FILE), Constants.CERT_VALUE.toCharArray());
keyManagerFactory = KeyManagerFactory.getInstance("X509");
keyManagerFactory.init(keyStore, Constants.CERT_VALUE.toCharArray());
}
KeyManager[] keyManagers = keyManagerFactory.getKeyManagers();
//Adding TrustManagerFactory
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(keyStore);
TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
if (trustManagers.length != 1 || !(trustManagers[0] instanceof X509TrustManager)) {
throw new IllegalStateException("Unexpected default trust managers:"
+ Arrays.toString(trustManagers));
}
mainX509TrustManager = (X509TrustManager) trustManagers[0];
sslContext = SSLContext.getInstance("TLSv1.2");
sslContext.init(keyManagers, null, null);
} catch (FileNotFoundException f)
{
Log.e("Excption File", String.valueOf(f));
} catch (Exception i)
{
Log.e(TAG, "Exception", i);
}
Where Constant.CERT_PROD_FILE refers to constant string which contain file name.
When debugging or making call, I am getting below error
javax.net.ssl.SSLProtocolException: SSL handshake terminated:
ssl=0x8a163cc0: Failure in SSL library, usually a protocol error
error:10000410:SSL
routines:OPENSSL_internal:SSLV3_ALERT_HANDSHAKE_FAILURE
(external/boringssl/src/ssl/s3_pkt.c:610 0x9f7e3d00:0x00000001)
I am not sure what am I missing and I have tried different solution including bug related to android 7 issue but it's still not working. I tried to debug and can only see the above error, so not sure if the issue is with server or client. Thanks for help in advance.

xamarin.android adding client certificate

I'm trying to send a request to a web api in Xamarin.Android. The api requires a client certificate. I followed the advice in this question: xamarin.ios httpclient clientcertificate not working with https, but I get a "method not implemented" exception. Can anyone help?
Here's my code:
string result = await CallApi(new System.Uri("myurl"));
protected async Task<string> CallApi(Uri url)
{
try
{
AndroidClientHandler clientHandler = new AndroidClientHandler();
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls | SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Ssl3;
using (var mmstream = new MemoryStream())
{
Application.Context.Assets.Open("mycert.pfx").CopyTo(mmstream);
byte[] b = mmstream.ToArray();
X509Certificate2 cert = new X509Certificate2(b, "password", X509KeyStorageFlags.DefaultKeySet);
clientHandler.ClientCertificates.Add(cert);
}
ServicePointManager.ServerCertificateValidationCallback += new RemoteCertificateValidationCallback((sender, certificate, chain, policyErrors) => { return true; });
HttpClient client = new HttpClient(clientHandler);
HttpResponseMessage response = await client.GetAsync(url);
response.EnsureSuccessStatusCode();
string responseBody = await response.Content.ReadAsStringAsync();
Console.WriteLine(responseBody);
return responseBody;
}
catch (HttpRequestException e)
{
Console.WriteLine("\nException Caught!");
return string.Empty;
}
}
In the post you mentioned probably the managed handler is used. Since this handler currently doesn't support TLS 1.2 you shouldn't use it, but instead really use the AndroidClientHandler (see also Xamarin and TLS 1.2).
Unfortunately ClientCertificates is indeed not implemented in AndroidClientHandler.
If you want to use client certificate with android you can extend the AndroidClientHandler:
using Java.Security;
using Java.Security.Cert;
using Javax.Net.Ssl;
using Xamarin.Android.Net;
using Xamarin.Forms;
public class AndroidHttpsClientHandler : AndroidClientHandler
{
private SSLContext sslContext;
public AndroidHttpsClientHandler(byte[] customCA, byte[] keystoreRaw) : base()
{
IKeyManager[] keyManagers = null;
ITrustManager[] trustManagers = null;
// client certificate
if (keystoreRaw != null)
{
using (MemoryStream memoryStream = new MemoryStream(keystoreRaw))
{
KeyStore keyStore = KeyStore.GetInstance("pkcs12");
keyStore.Load(memoryStream, clientCertPassword.ToCharArray());
KeyManagerFactory kmf = KeyManagerFactory.GetInstance("x509");
kmf.Init(keyStore, clientCertPassword.ToCharArray());
keyManagers = kmf.GetKeyManagers();
}
}
// custom truststore if you have your own ca
if (customCA != null)
{
CertificateFactory certFactory = CertificateFactory.GetInstance("X.509");
using (MemoryStream memoryStream = new MemoryStream(customCA))
{
KeyStore keyStore = KeyStore.GetInstance("pkcs12");
keyStore.Load(null, null);
keyStore.SetCertificateEntry("MyCA", certFactory.GenerateCertificate(memoryStream));
TrustManagerFactory tmf = TrustManagerFactory.GetInstance("x509");
tmf.Init(keyStore);
trustManagers = tmf.GetTrustManagers();
}
}
sslContext = SSLContext.GetInstance("TLS");
sslContext.Init(keyManagers, trustManagers, null);
}
protected override SSLSocketFactory ConfigureCustomSSLSocketFactory(HttpsURLConnection connection)
{
SSLSocketFactory socketFactory = sslContext.SocketFactory;
if (connection != null)
{
connection.SSLSocketFactory = socketFactory;
}
return socketFactory;
}
}
If you refer to AndroidClientHandler Source Code, you can find following statement:
AndroidClientHandler also supports requests to servers with "invalid" (e.g. self-signed) SSL certificates. Since this process is a bit convoluted using
the Java APIs, AndroidClientHandler defines two ways to handle the situation. First, easier, is to store the necessary certificates (either CA or server certificates)
in the collection or, after deriving a custom class from AndroidClientHandler, by overriding one or more methods provided for this purpose(, and ). The former method should be sufficient for most use cases...
So, for usage of AndroidClientHandler you should use clientHandler.TrustedCerts together with Java.Security.Cert.X509Certificate:
Java.Security.Cert.X509Certificate cert = null;
try
{
CertificateFactory factory = CertificateFactory.GetInstance("X.509");
using (var stream = Application.Context.Assets.Open("MyCert.pfx"))
{
cert = (Java.Security.Cert.X509Certificate)factory.GenerateCertificate(stream);
}
} catch (Exception e)
{
System.Console.WriteLine(e.Message);
}
if (clientHandler.TrustedCerts != null)
{
clientHandler.TrustedCerts.Add(cert);
}
else
{
clientHandler.TrustedCerts = new List<Certificate>();
clientHandler.TrustedCerts.Add(cert);
}
Notes: don't use Application.Context.Assets.Open("ca.pfx").CopyTo(mmstream); otherwise you will get inputstream is empty exception.

Error https request android <20

I'm trying to develop an app to communicate with amazon alexa using OKhttp3. Everything was working fine while I was trying the app on an android API 26.
But once I started using an android API 19 every request to https://avs-alexa-na.amazon.com/ started failing.
Before working for API 19 this was how I used this function to open the down channel.
final String url = "https://avs-alexa-na.amazon.com/";
final String version = "v20160207";
final Request request = new Request.Builder()
.get()
.header("Authorization", "Bearer " + myApplication.getAccessToken())
.url(url + version + "/directives")
.build();
try {
final OkHttpClient client = new OkHttpClient();
Response response = client.newCall(request).execute();
Log.i("Down channel", "opened");
} catch (Exception ex) {
Log.e("Down channel", "error Sending request " + ex.getLocalizedMessage());
}
But now this same function returns javax.net.ssl.SSLProtocolException: SSL handshake aborted: ssl=0xb969de80: Failure in SSL library, usually a protocol error error:14077410:SSL routines:SSL23_GET_SERVER_HELLO:sslv3 alert handshake failure (external/openssl/ssl/s23_clnt.c:741 0x8d954990:0x00000000)
So after following some posts on line I learned that android 19 doesn't install by default TLSv1.2 so I added a function to install it and started having this error :unexpected end of stream on Connection{avs-alexa-na.amazon.com:443, proxy=DIRECT# hostAddress=avs-alexa-na.amazon.com/54.239.39.74:443 cipherSuite=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 protocol=http/1.1}
I started exploring other ways working with certificates (my certificate, and the one from amazon gotten by openssl s_client -connect avs-alexa-eu.amazon.com:443:
SSLContext sslContext;
TrustManager[] trustManagers;
try {
final KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(null, null);
final InputStream certInputStream = activity.getResources().openRawResource(R.raw.cert);
InputStream avscertInputStream = activity.getResources().openRawResource(R.raw.amazon_avs_cert);
final CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
final Certificate cert = certificateFactory.generateCertificate(certInputStream);
final Certificate avscert = certificateFactory.generateCertificate(avscertInputStream);
keyStore.setCertificateEntry("amazon_avs", avscert);
keyStore.setCertificateEntry("cert", cert);
final TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(keyStore);
trustManagers = trustManagerFactory.getTrustManagers();
sslContext = SSLContext.getInstance("TLSv1.2");
sslContext.init(null, trustManagers, null);
return new OkHttpClient.Builder()
.sslSocketFactory(sslContext.getSocketFactory(), (X509TrustManager) trustManagers[0])
.build();
} catch (Exception e) {
e.printStackTrace();
return new OkHttpClient();
}
But it led to a java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.
I'm lost, I don't know what I did wrong or what I did not do. Thanks for you help.

ssl android certificate with volley

i have implemented the certificate and keystore to my app and succeeded in making request to my server , but now i want to accept another server's HTTPS (for online payment) to be able to integrate it in my app but volley says that it doesn't accept it , can i accept this domain without their keystore
and this is my code for accepting my server's certificate
private SSLSocketFactory newSslSocketFactory() {
try {
// Get an instance of the Bouncy Castle KeyStore format
KeyStore trusted = KeyStore.getInstance("BKS");
// Get the raw resource, which contains the keystore with
// your trusted certificates (root and any intermediate certs)
InputStream in = getApplicationContext().getResources().openRawResource(R.raw.keystore);
try {
// Initialize the keystore with the provided trusted certificates
// Provide the password of the keystore
trusted.load(in, KEYSTORE_PASSWORD);
} finally {
in.close();
}
String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
tmf.init(trusted);
SSLContext context = SSLContext.getInstance("TLS");
context.init(null, tmf.getTrustManagers(), null);
SSLSocketFactory sf = context.getSocketFactory();
return sf;
} catch (Exception e) {
throw new AssertionError(e);
}
}

How can I add trust store in SSLSocketFactory in Android?

I'm writing an Android's app with SSL connection (not HTTP). I read a lot of tutorials about HTTPS in Android but it keeping product error "SSL socket factory is abstract cannot be instantiated." with SSLSocketFactory sf = new SSLSocketFactory(truststore).
My question is: How can I add key store to Android?.
This is my example:
public void run() {
try {
KeyStore trusted = KeyStore.getInstance("BKS");
InputStream in = context.getResources().openRawResource(R.raw.keystore);
try {
trusted.load(in, "1234567".toCharArray());
SSLSocketFactory sSLSocketFactory = (SSLSocketFactory) SSLSocketFactory.getDefault(); // <<<<< Line 40
SSLSocket sslSocket = (SSLSocket) sSLSocketFactory.createSocket("10.0.2.2", 9998);
sslSocket.startHandshake();
Log.i("SSLsocket", "true");
} finally {
in.close();
}
} catch (Exception e) {
throw new AssertionError(e);
}
}
Caused by: javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.
at com.android.org.conscrypt.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:322)
at com.example.begood.voip.SSlconnection.run(SSlconnection.java:40)
Try something like this, given your trustStore:
KeyManagerFactory kmfactory = KeyManagerFactory.getInstance(
KeyManagerFactory.getDefaultAlgorithm());
kmfactory.init(trustStore, "password".toCharArray());
KeyManager[] keymanagers = kmfactory.getKeyManagers();
TrustManagerFactory tmf=TrustManagerFactory
.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(trustStore);
SSLContext sslContext=SSLContext.getInstance("TLSv1.2");
sslContext.init(keymanagers, tmf.getTrustManagers(), new SecureRandom());
SSLSocketFactory factory=sslContext.getSocketFactory();

Categories

Resources