2

I am trying to implement ssl support in my volley request (also I saw answers in SO with similar issues, but it does not help me)

With help of this article I converted my certificate extension from .cer to .bks

That according to this SO answer I do next

mRequestQueue = Volley.newRequestQueue(this, hurlStack);

private HurlStack hurlStack = new HurlStack()
{
    @Override
    protected HttpURLConnection createConnection(URL url) throws IOException
    {
        HttpsURLConnection httpsURLConnection = (HttpsURLConnection) super.createConnection(url);
        try
        {
            httpsURLConnection.setSSLSocketFactory(getSSLSocketFactory());
            httpsURLConnection.setHostnameVerifier(getHostnameVerifier());
        }
        catch (Exception e)
        {
            AppUtils.printLog(Log.ERROR, TAG, e.getMessage());
        }
        return httpsURLConnection;
    }
};

private SSLSocketFactory getSSLSocketFactory() throws CertificateException, KeyStoreException, IOException, NoSuchAlgorithmException, KeyManagementException
{
    CertificateFactory cf = CertificateFactory.getInstance("X.509");
    InputStream caInput = getResources().openRawResource(R.raw.keystore); // this cert file stored in \app\src\main\res\raw folder path

    Certificate ca = cf.generateCertificate(caInput);
    caInput.close();

    KeyStore keyStore = KeyStore.getInstance("BKS");
    keyStore.load(null, null);
    keyStore.setCertificateEntry("ca", ca);

    String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
    TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
    tmf.init(keyStore);

    TrustManager[] wrappedTrustManagers = getWrappedTrustManagers(tmf.getTrustManagers());

    SSLContext sslContext = SSLContext.getInstance("TLS");
    sslContext.init(null, wrappedTrustManagers, null);

    return sslContext.getSocketFactory();
}

// Let's assume your server app is hosting inside a server machine
// which has a server certificate in which "Issued to" is "localhost",for example.
// Then, inside verify method you can verify "localhost".
// If not, you can temporarily return true
private HostnameVerifier getHostnameVerifier()
{
    return new HostnameVerifier()
    {
        @Override
        public boolean verify(String hostname, SSLSession session)
        {
            //return true; // verify always returns true, which could cause insecure network traffic due to trusting TLS/SSL server certificates for wrong hostnames
            HostnameVerifier hv = HttpsURLConnection.getDefaultHostnameVerifier();
            return hv.verify("localhost", session);
        }
    };
}

private TrustManager[] getWrappedTrustManagers(TrustManager[] trustManagers)
{
    final X509TrustManager originalTrustManager = (X509TrustManager) trustManagers[0];
    return new TrustManager[] {new X509TrustManager()
    {
        public X509Certificate[] getAcceptedIssuers()
        {
            return originalTrustManager.getAcceptedIssuers();
        }

        public void checkClientTrusted(X509Certificate[] certs, String authType)
        {
            try
            {
                if (certs != null && certs.length > 0)
                {
                    certs[0].checkValidity();
                }
                else
                {
                    originalTrustManager.checkClientTrusted(certs, authType);
                }
            }
            catch (CertificateException e)
            {
                Log.w("checkClientTrusted", e.toString());
            }
        }

        public void checkServerTrusted(X509Certificate[] certs, String authType)
        {
            try
            {
                if (certs != null && certs.length > 0)
                {
                    certs[0].checkValidity();
                }
                else
                {
                    originalTrustManager.checkServerTrusted(certs, authType);
                }
            }
            catch (CertificateException e)
            {
                Log.w("checkServerTrusted", e.toString());
            }
        }
    }};
}

And I get next error

com.android.org.conscrypt.OpenSSLX509CertificateFactory$ParsingException: com.android.org.conscrypt.OpenSSLX509CertificateFactory$ParsingException: java.lang.RuntimeException: error:0c0890ba:ASN.1 encoding routines:asn1_check_tlen:WRONG_TAG

And because of this I get such respond

Bad Request

Bad Request - Invalid Header


HTTP Error 400. The request has an invalid header name.

What am I doing wrong?

Feel free to ask

EDIT 1

so now my getSSLSocketFactory() method look like this

private SSLSocketFactory getSSLSocketFactory() throws CertificateException, KeyStoreException, IOException, NoSuchAlgorithmException, KeyManagementException
{
    InputStream ksInStream = getResources().openRawResource(R.raw.keystore);

    KeyStore ks = KeyStore.getInstance("BKS");
    ks.load(ksInStream, SslUtils.KEYSTORE_PASSWORD_SSL.toCharArray());

//      Certificate cert = ks.getCertificate("alias");
//      ks.setCertificateEntry("ca", cert);

    ksInStream.close();

    String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
    TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
    tmf.init(ks);

    TrustManager[] wrappedTrustManagers = getWrappedTrustManagers(tmf.getTrustManagers());

    SSLContext sslContext = SSLContext.getInstance("TLS");
    sslContext.init(null, wrappedTrustManagers, null);

    return sslContext.getSocketFactory();
}

Now I did not get message about wrong TAG , but I still get bad respond

ResponseJsonString = Bad Request

Bad Request - Invalid Header


HTTP Error 400. The request has an invalid header name.

Sirop4ik
  • 4,543
  • 2
  • 54
  • 121
  • From different sources this issue has been associated with having extract trailing spaces at the end of the certificate that you used for conversion. Double check your starting certificated and make it has no trailing space or newline at the end – Tarun Lalwani Oct 22 '17 at 19:52

2 Answers2

3

In this code you seem to load keystore in BKS format as it would be X.509 encoded certificate, which is bound to fail

CertificateFactory cf = CertificateFactory.getInstance("X.509");
InputStream caInput = getResources().openRawResource(R.raw.elalkeystore);

Certificate ca = cf.generateCertificate(caInput);
caInput.close();

You can load keystore like this:

InputStream ksInStream = getResources().openRawResource(R.raw.elalkeystore);

KeyStore ks = KeyStore.getInstance("BKS");
ks.load(ksInStream, keystorePasswordCharArray);
Certificate cert = ks.getCertificate("entryAlias");
ksInStream.close();
divanov
  • 6,173
  • 3
  • 32
  • 51
  • But according to your answer there is not used `cert` value. I suppose that you forgot to mention this line `ks.setCertificateEntry("ca", cert);` , did you? – Sirop4ik Oct 24 '17 at 10:15
  • I think that he indeed forgot to add that line to his example. Then it should work like expected. – Nico Oct 24 '17 at 12:13
  • Why would you add certificate to a keystore, which already has it? I extract certificate just to do have a possibility to cross check that the keystore actually has it and to make it similar to the code being replaced. – divanov Oct 24 '17 at 14:59
  • @divanov So if I understood you correctly I don't really need to invoke `.setCertificateEntry()` method? Anyway I put my EDIT 1 in question, could you please take a look? – Sirop4ik Oct 25 '17 at 09:25
  • This method adds a certificate to a key store, while it originally had it. I suggest you to collect tcpdump of your connection attempt and then check with wireshark what is happening there. Also original error Bad Request - Invalid header didn't change despite you solved exception during TLS setup. Which makes me wonder if this error is related to the original problem here or the question is not accurate. – divanov Oct 25 '17 at 11:31
  • yes your are right, my question was about `wrong_tag`, so your answer really helped me to get rid of it. I need to dig deeply, and figure our why I have this `bad request` issue. Will try to use wireshark. Thaks! – Sirop4ik Oct 26 '17 at 06:09
  • Another option is to port the code to desktop and use -Djavax.net.debug=all, however, I assume taking tcpdump is easier. You just need to run it in emulator or connect a phone through you laptop to the Internet. – divanov Oct 26 '17 at 12:32
0

Eventually I did not find solution for the issue, I found another approach for implementation

So follow this article

http://ogrelab.ikratko.com/using-android-volley-with-self-signed-certificate/

also if there is any issue about converting .cer to .bks here my SO question and answer

Extension of certificate .cer convert to .bks

Sirop4ik
  • 4,543
  • 2
  • 54
  • 121