Android 应用程序中与客户端证书的 HTTPS 连接 [英] HTTPS connection with client certificate in an android app

查看:27
本文介绍了Android 应用程序中与客户端证书的 HTTPS 连接的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试用我正在编写的 Android 应用程序中的 HTTPS 连接替换当前工作的 HTTP 连接.HTTPS 连接的额外安全性是必要的,所以我不能忽略这一步.

I am trying to replace the currently working HTTP connection with a HTTPS connection in a Android app that I am writing. The additional security of a HTTPS connection is necessary and so I cannot ignore this step.

我有以下几点:

  1. 配置为建立 HTTPS 连接的服务器,并且需要客户端证书
    • 此服务器具有由标准大型 CA 颁发的证书.简而言之,如果我通过 Android 中的浏览器访问此连接,它工作正常,因为设备信任库识别 CA.(所以它不是自签名的)
  • 当服务器配置为不需要需要客户端证书时,客户端可以连接到服务器.基本上,如果我使用 SSLSocketFactory.getSocketFactory() 连接工作正常,但客户端证书是此应用程序规范的必需部分,因此:
  • 当我尝试连接我的自定义 SSLSocketFactory 时,客户端产生一个 javax.net.ssl.SSLPeerUnverifiedException: No peer certificate 异常,但我不完全确定原因.在互联网上搜索了各种解决方案后,这个例外似乎有点模棱两可.
  • The client can connect to the server when the server is configured to not require a client certificate. Basically, if I use SSLSocketFactory.getSocketFactory() the connection works fine, but the client certificate is a required part of this applications specifications, so:
  • The client produces a javax.net.ssl.SSLPeerUnverifiedException: No peer certificate exception when I attempt to connect with my custom SSLSocketFactory, but I am not entirely certain why. This exception seems a little ambiguous after searching around the internet for various solutions to this.

这是客户端的相关代码:

Here is the relavent code for the client:

SSLSocketFactory socketFactory = null;

public void onCreate(Bundle savedInstanceState) {
    loadCertificateData();
}

private void loadCertificateData() {
    try {
        File[] pfxFiles = Environment.getExternalStorageDirectory().listFiles(new FileFilter() {
            public boolean accept(File file) {
                if (file.getName().toLowerCase().endsWith("pfx")) {
                    return true;
                }
                return false;
            }
        });

        InputStream certificateStream = null;
        if (pfxFiles.length==1) {
            certificateStream = new FileInputStream(pfxFiles[0]);
        }

        KeyStore keyStore = KeyStore.getInstance("PKCS12");
        char[] password = "somePassword".toCharArray();
        keyStore.load(certificateStream, password);

        System.out.println("I have loaded [" + keyStore.size() + "] certificates");

        KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
        keyManagerFactory.init(keyStore, password);

        socketFactory = new SSLSocketFactory(keyStore);
    } catch (Exceptions e) {
        // Actually a bunch of catch blocks here, but shortened!
    }
}

private void someMethodInvokedToEstablishAHttpsConnection() {
    try {
        HttpParams standardParams = new BasicHttpParams();
        HttpConnectionParams.setConnectionTimeout(standardParams, 5000);
        HttpConnectionParams.setSoTimeout(standardParams, 30000);

        SchemeRegistry schRegistry = new SchemeRegistry();
        schRegistry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
        schRegistry.register(new Scheme("https", socketFactory, 443));
        ClientConnectionManager connectionManager = new ThreadSafeClientConnManager(standardParams, schRegistry);

        HttpClient client = new DefaultHttpClient(connectionManager, standardParams);
        HttpPost request = new HttpPost();
        request.setURI(new URI("https://TheUrlOfTheServerIWantToConnectTo));
        request.setEntity("Some set of data used by the server serialized into string format");
        HttpResponse response = client.execute(request);
        resultData = EntityUtils.toString(response.getEntity());
    } catch (Exception e) {
        // Catch some exceptions (Actually multiple catch blocks, shortened)
    }
}

我已经证实,是的,keyStore 确实加载了一个证书并且对此很满意.

I have verified that, yes indeed the keyStore loads a certificate and is all happy with that.

关于我在阅读 HTTPS/SSL 连接时遗漏了什么,我有两种理论,但由于这确实是我的第一次尝试,我对解决此问题实际上需要什么感到有些困惑.

I have two theories as to what I'm missing from reading about HTTPS/SSL connections, but as this is really my first foray, I am a little puzzled as to what I actually need to resolve this issue.

据我所知,第一种可能性是我需要使用设备的信任库配置这个 SSLSocketFactory,其中包括所有标准的中间和端点证书颁发机构.也就是说,设备的默认 SSLSocketFactory.getSocketFactory() 将一些 CA 集加载到工厂的信任库中,用于在服务器发送证书时信任服务器,这就是我的代码失败的原因,因为我没有正确加载信任存储.如果这是真的,我最好如何加载这些数据?

The first possibility, as far as I can tell, is that I need to configure this SSLSocketFactory with the devices' truststore that includes all of the standard Intermediate and endpoint Certificate Authorities. That is, the device's default of SSLSocketFactory.getSocketFactory() loads some set of CAs into the factory's truststore that is used to trust the server when it sends its certificate, and that is what is failing in my code, because I do not properly have the trust store loaded. If this is true, how would I best go about loading this data?

第二种可能性是由于客户端证书是自签名的(或由内部证书颁发机构颁发的 - 如果我错了,请纠正我,但这些实际上等同于所有意图和这里的目的).事实上,我缺少的是这个信任库,基本上我需要为服务器提供一种方法来验证内部 CA 的证书,并验证这个内部 CA 实际上是 值得信赖".如果这是真的,我到底在寻找什么样的东西?我看到了一些对此的引用,这让我相信这可能是我的问题,如 here,但我真的不确定.如果这确实是我的问题,我会向维护内部 CA 的人提出什么要求,然后我将如何将其添加到我的代码中以便我的 HTTPS 连接正常工作?

The second possibility is due to the fact that the client certificate is self-signed (or issued by an internal certificate authority -- correct me if I'm wrong, but these really amount to the same thing, for all intents and purposes here). It is in fact this truststore that I am missing, and basically I need to provide a way for the server to validate the certificate with the internal CA, and also validate that this internal CA is in fact "trustable". If this is true, exactly what sort of thing am I looking for? I have seen some reference to this that makes me believe this may be my problem, as in here, but I am truly not certain. If this is indeed my problem, what would I ask for from the person who maintains the internal CA, and then how would I add this to my code so that my HTTPS connection would work?

第三个,希望不太可能的解决方案是,我在这里的某些点完全错了,错过了一个关键步骤,或者完全忽略了我目前一无所知的 HTTPS/SSL 部分.如果是这种情况,能否请您给我一个方向,以便我可以去了解我需要学习的内容?

The third, and hopefully less possible solution, is that I'm entirely wrong about some point here and have missed a crucial step or am completely neglecting a portion of HTTPS/SSL that I just don't currently have any knowledge of. If this is the case, could you please provide me with a bit of a direction so that I can go and learn what it is I need to learn?

感谢阅读!

推荐答案

我认为确实是这个问题.

I think this is indeed the issue.

据我所知,第一种可能性是我需要使用设备的信任库配置此 SSLSocketFactory包括所有标准的中间和端点证书当局

The first possibility, as far as I can tell, is that I need to configure this SSLSocketFactory with the devices' truststore that includes all of the standard Intermediate and endpoint Certificate Authorities

如果这是真的,我最好如何加载这些数据?

If this is true, how would I best go about loading this data?

尝试这样的操作(你需要让你的套接字工厂使用这个默认的信任管理器):

Try something like this (you'll need to get your socket factory to use this default trust manager):

X509TrustManager manager = null;
FileInputStream fs = null;

TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());

try
{
    fs = new FileInputStream(System.getProperty("javax.net.ssl.trustStore")); 
    keyStore.load(fs, null);
}
finally
{
    if (fs != null) { fs.close(); }
}

trustManagerFactory.init(keyStore);
TrustManager[] managers = trustManagerFactory.getTrustManagers();

for (TrustManager tm : managers)
{
    if (tm instanceof X509TrustManager) 
    {
        manager = (X509TrustManager) tm;
        break;
    }
}

在使用此处的代码之前,请先查看 Pooks 的回答.听起来现在有更好的方法可以做到这一点.

Please look at Pooks' answer before using the code here. It sounds like there's a better way to do this now.

这篇关于Android 应用程序中与客户端证书的 HTTPS 连接的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆