在安卓应用中与客户端证书建立 HTTPS 连接
我正在尝试将当前工作的HTTP连接替换为我正在编写的Android应用程序中的HTTPS连接。HTTPS连接的额外安全性是必要的,因此我不能忽略此步骤。
我有以下几点:
- 配置为建立 HTTPS 连接并需要客户端证书的服务器
- 此服务器具有由标准大规模 CA 颁发的证书。简而言之,如果我通过Android中的浏览器访问此连接,它可以正常工作,因为设备信任库可以识别CA(因此它不是自签名的)
- 实质上是自签名的客户端证书。(由内部 CA 颁发)
- 加载此客户端证书并尝试连接到上述服务器的 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连接的内容时遗漏了什么,我有两种理论,但是由于这确实是我的第一次尝试,因此我对解决此问题的实际需求感到有些困惑。
据我所知,第一种可能性是,我需要使用设备的信任库配置此SSSocketFactory,其中包括所有标准的中间和端点证书颁发机构。也就是说,设备的默认值是将一组 CA 加载到工厂的信任库中,该信任库用于在发送其证书时信任服务器,这就是我代码中失败的原因,因为我没有正确加载信任库。如果这是真的,我将如何最好地加载这些数据?SSLSocketFactory.getSocketFactory()
第二种可能性是由于客户端证书是自签名的(或由内部证书颁发机构颁发 - 如果我错了,请纠正我,但这些实际上相当于同样的事情,出于这里的所有意图和目的)。事实上,我缺少的正是这个信任库,基本上我需要为服务器提供一种使用内部 CA 验证证书的方法,并且还要验证此内部 CA 是否确实是“可信任的”。如果这是真的,我到底在寻找什么样的东西?我看到了一些这方面的参考,使我相信这可能是我的问题,就像在这里一样,但我真的不确定。如果这确实是我的问题,我会要求维护内部CA的人做什么,然后我如何将其添加到我的代码中,以便我的HTTPS连接能够正常工作?
第三个,希望是不太可能的解决方案,是我在这里的某个点上完全错了,并且错过了关键的一步,或者完全忽略了HTTPS / SSL的一部分,而我目前还不了解。如果是这样的话,你能不能给我一点方向,这样我就可以去学习我需要学习的东西?
感谢您的阅读!