需要以下主要步骤来实现来自不被 android 平台信任的证书颁发机构的安全连接。
根据许多用户的要求,我在此处反映了我的博客文章中最重要的部分:
- 获取所有必需的证书(根证书和任何中间 CA)
- 使用 keytool 和BouncyCastle提供程序创建密钥库并导入证书
- 在您的 android 应用程序中加载密钥库并将其用于安全连接(我建议使用Apache HttpClient而不是标准
java.net.ssl.HttpsURLConnection
(更容易理解,性能更高)
获取证书
您必须获取从端点证书一直到根 CA 构建链的所有证书。这意味着,任何(如果存在)中间 CA 证书以及根 CA 证书。您不需要获取端点证书。
创建密钥库
下载BouncyCastle Provider并将其存储到已知位置。还要确保您可以调用 keytool 命令(通常位于 JRE 安装的 bin 文件夹下)。
现在将获得的证书(不要导入端点证书)导入到 BouncyCastle 格式的密钥库中。
我没有测试它,但我认为导入证书的顺序很重要。这意味着,首先导入最低的中间 CA 证书,然后一直导入到根 CA 证书。
使用以下命令,将创建一个密码为 mysecret的新密钥库(如果尚未存在),并将导入中间 CA 证书。我还定义了 BouncyCastle 提供程序,它可以在我的文件系统和密钥库格式中找到。对链中的每个证书执行此命令。
keytool -importcert -v -trustcacerts -file "path_to_cert/interm_ca.cer" -alias IntermediateCA -keystore "res/raw/mykeystore.bks" -provider org.bouncycastle.jce.provider.BouncyCastleProvider -providerpath "path_to_bouncycastle/bcprov-jdk16-145.jar" -storetype BKS -storepass mysecret
验证证书是否已正确导入密钥库:
keytool -list -keystore "res/raw/mykeystore.bks" -provider org.bouncycastle.jce.provider.BouncyCastleProvider -providerpath "path_to_bouncycastle/bcprov-jdk16-145.jar" -storetype BKS -storepass mysecret
应该输出整个链:
RootCA, 22.10.2010, trustedCertEntry, Thumbprint (MD5): 24:77:D9:A8:91:D1:3B:FA:88:2D:C2:FF:F8:CD:33:93
IntermediateCA, 22.10.2010, trustedCertEntry, Thumbprint (MD5): 98:0F:C3:F8:39:F7:D8:05:07:02:0D:E3:14:5B:29:43
现在您可以将密钥库作为原始资源复制到您的 android 应用程序下res/raw/
在您的应用程序中使用密钥库
首先,我们必须创建一个自定义 Apache HttpClient,它使用我们的密钥库进行 HTTPS 连接:
import org.apache.http.*
public class MyHttpClient extends DefaultHttpClient {
final Context context;
public MyHttpClient(Context context) {
this.context = context;
}
@Override
protected ClientConnectionManager createClientConnectionManager() {
SchemeRegistry registry = new SchemeRegistry();
registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
// Register for port 443 our SSLSocketFactory with our keystore
// to the ConnectionManager
registry.register(new Scheme("https", newSslSocketFactory(), 443));
return new SingleClientConnManager(getParams(), registry);
}
private SSLSocketFactory newSslSocketFactory() {
try {
// Get an instance of the Bouncy Castle KeyStore format
KeyStore trusted = KeyStore.getInstance("BKS");
// Get the raw resource, which contains the keystore with
// your trusted certificates (root and any intermediate certs)
InputStream in = context.getResources().openRawResource(R.raw.mykeystore);
try {
// Initialize the keystore with the provided trusted certificates
// Also provide the password of the keystore
trusted.load(in, "mysecret".toCharArray());
} finally {
in.close();
}
// Pass the keystore to the SSLSocketFactory. The factory is responsible
// for the verification of the server certificate.
SSLSocketFactory sf = new SSLSocketFactory(trusted);
// Hostname verification from certificate
// http://hc.apache.org/httpcomponents-client-ga/tutorial/html/connmgmt.html#d4e506
sf.setHostnameVerifier(SSLSocketFactory.STRICT_HOSTNAME_VERIFIER);
return sf;
} catch (Exception e) {
throw new AssertionError(e);
}
}
}
我们已经创建了我们的自定义 HttpClient,现在我们可以将它用于安全连接。例如,当我们对 REST 资源进行 GET 调用时:
// Instantiate the custom HttpClient
DefaultHttpClient client = new MyHttpClient(getApplicationContext());
HttpGet get = new HttpGet("https://www.mydomain.ch/rest/contacts/23");
// Execute the GET call and obtain the response
HttpResponse getResponse = client.execute(get);
HttpEntity responseEntity = getResponse.getEntity();
而已 ;)