需要以下主要步骤来实现来自证书颁发机构的安全连接,而该证书颁发机构不会被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();
而已 ;)