在Android应用中使用客户端证书进行HTTPS连接

克维克:

我正在尝试用我编写的Android应用中的HTTPS连接替换当前工作的HTTP连接。HTTPS连接的附加安全性是必需的,因此我不能忽略此步骤。

我有以下内容:

  1. 配置为建立HTTPS连接并需要客户端证书的服务器
    • 该服务器具有由标准大型CA颁发的证书。简而言之,如果我通过Android中的浏览器访问此连接,则可以正常运行,因为设备信任库可以识别CA。(因此它不是自签名的)
  2. 基本上是自签名的客户端证书。(由内部CA发行)
  3. 加载此客户端证书并尝试连接到上述服务器的Android应用,但存在以下问题/属性:
    • 当服务器被配置为客户端可以连接到服务器要求客户端证书。基本上,如果使用SSLSocketFactory.getSocketFactory()连接正常,但是客户端证书是此应用程序规范的必需部分,因此:
    • javax.net.ssl.SSLPeerUnverifiedException: No peer certificate当我尝试与我的自定义连接时,客户端会产生异常SSLSocketFactory,但我不确定原因为何。在互联网上搜索各种解决方案后,该例外似乎有点模棱两可。

这是客户端的相关代码:

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加载了证书并且对此感到满意。

关于阅读HTTPS / SSL连接所缺少的内容,我有两种理论,但是由于这确实是我的首次尝试,因此我对于解决此问题的实际目的感到有些困惑。

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

第二种可能性是由于客户证书是自签名的(或由内部证书颁发机构颁发的,如果我错了,请更正我,但出于此处的所有意图和目的,它们实际上是同一件事) 。实际上,我缺少信任库,并且基本上,我需要为服务器提供一种使用内部CA验证证书的方法,并且还需要验证此内部CA实际上是“可信任的”如果这是真的,那么我到底在寻找什么样的东西?我已经看到一些对此的参考,这使我相信这可能是我的问题,例如此处,但我确实不确定。如果这确实是我的问题,那么我将向维护内部CA的人员提出什么要求,然后如何将其添加到我的代码中,以便HTTPS连接正常工作?

第三个也是希望不太可能的解决方案是,我在这里的某些地方完全错了,错过了关键步骤,或者完全忽略了我目前不了解的HTTPS / SSL部分。如果是这样,您能不能给我提供一些指导,以便我可以去学习我需要学习的内容?

谢谢阅读!

jglouie:

我认为这确实是问题。

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

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

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

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的回答。听起来现在有更好的方法可以做到这一点。

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章

如何在节点中使用客户端证书进行HTTPS GET

如何在HTTPS中使用客户端证书?

如何在客户端Java应用程序中使用客户端证书?

使用Xamarin Android与客户端证书进行SSL通信

在WebJob中使用客户端证书

使用SpongyCastle从PKCS#10创建带有客户端证书的Https连接

转到:使用存储在智能卡上的客户端证书进行HTTPS请求(Windows)

使用自签名证书的Java ssl / https客户端

在Alamofire 2.0中使用客户端证书

在Curl命令中使用客户端证书

在 Jenkins 中使用 withCredentials 绑定中的客户端证书

无法在Tornado中使用SSL客户端证书

Java HTTPS客户端证书认证

如何在MQCSP中使用userId在C客户端中进行IBM MQ连接认证

使用HTTPS协议的android连接中的Socket.io客户端失败?

使用客户端证书的扭曲的HTTP客户端

在没有Spring Boot的Spring中使用证书进行客户端身份验证

带有 ssl 证书的 Elasticsearch https 和 Nest 客户端连接

https客户端使用客户端证书和密码与cpp-netlib一起获得

如何使用自签名证书连接到 Android(paho 客户端)中的 Mqtt 服务器?

如何在Java中使用Webclient添加CA证书和客户端证书

在Android应用中使用http客户端模拟表单发布?

如何在Spring-Boot中使用SSL证书并为Android客户端生成公钥

在Android上使用客户端/服务器证书进行双向身份验证SSL套接字

如何在Flutter应用中使用WebSocket客户端?

是否可以使用HTTP(而非HTTPS)客户端证书?

如何使用Powershell通过https将自签名证书发送给客户端

如何使用带有 ssl 证书的 https 客户端调用 apis 表单 java

使用证书,证书订单列表或默认证书进行客户端身份验证