2

我正在尝试在我的 volley 请求中实现 ssl 支持(我也在 SO 中看到了类似问题的答案,但这对我没有帮助)

在本文的帮助下,我将证书扩展名从 .cer 转换为 .bks

根据这个 SO answer我接下来做

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());
            }
        }
    }};
}

我得到下一个错误

com.android.org.conscrypt.OpenSSLX509CertificateFactory$ParsingException: com.android.org.conscrypt.OpenSSLX509CertificateFactory$ParsingException: java.lang.RuntimeException: error:0c0890ba:ASN.1 编码例程:asn1_check_tlen:WRONG_TAG

正因为如此,我得到了这样的回应

错误的请求

错误请求 - 无效标头


HTTP 错误 400。请求的标头名称无效。

我究竟做错了什么?

随意问

编辑 1

所以现在我的getSSLSocketFactory()方法看起来像这样

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();
}

现在我没有收到关于错误 TAG 的消息,但我仍然收到bad respond

ResponseJsonString = 错误请求

错误请求 - 无效标头


HTTP 错误 400。请求的标头名称无效。

4

2 回答 2

3

在此代码中,您似乎以 BKS 格式加载密钥库,因为它将是 X.509 编码的证书,这必然会失败

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

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

您可以像这样加载密钥库:

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

KeyStore ks = KeyStore.getInstance("BKS");
ks.load(ksInStream, keystorePasswordCharArray);
Certificate cert = ks.getCertificate("entryAlias");
ksInStream.close();
于 2017-10-24T09:31:42.793 回答
0

最终我没有找到问题的解决方案,我找到了另一种实现方法

所以按照这篇文章

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

另外,如果有任何关于转换.cer.bks这里的问题,我的 SO 问题和答案

证书扩展名 .cer 转换为 .bks

于 2017-10-24T09:13:28.950 回答