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

查看:115
本文介绍了在一个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. (所以它不是自签名)
  1. A server configured to establish a HTTPS connection, and require a client certificate
    • This server has a certificate that is issued by a standard large-scale CA. In short, if I access this connection via the browser in Android, it works fine because the devices truststore recognizes the CA. (So it's not self-signed)
  • 在客户端可以连接到服务器时,服务器被配置为不可以需要一个客户端证书。基本上,如果我用 SSLSocketFactory.getSocketFactory()连接工作正常,但客户端证书是这种应用的规格要求的一部分,因此:
  • 在客户端生成一个 javax.net.ssl​​.SSLPeerUnverifiedException:没有对方的证书例外,当我试图用我的自定义连接 SSLSocketFactory的,但我不能完全肯定这是为什么。此异常互联网的各种解决方案各地寻找这种后似乎有点暧昧。
  • 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.

下面是relavent code客户端:

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

我已经验证了,是的的确密钥库加载证书,并为所有的高兴。

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,进厂的信任库,用于信任服务器时,它发出的证书,这是什么是失败在我的code,因为我没有正确地加载了信任存储区。如果这是真的,我将如何最好去加载这些数据?

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实际上是在值得信赖的的。如果这是真的,什么样的事情我在找什么?我已经看到了一些参考这一点,使我相信这可能是我的问题,如<一个href="http://stackoverflow.com/questions/1666052/java-https-client-certificate-authentication">here,但我真的不能确定。如果这确实是我的问题,那么我要求从谁维护的内部CA的人,再怎么样我想补充一点,我的code,使我的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?

感谢您的阅读!

推荐答案

我觉得这是问题。

第一种可能,据我所知道的是,我需要   配置此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;
    }
}

编辑:请在这里使用code前看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天全站免登陆