问题描述
目标
我正在努力实现与 Client-Certificate 的通信.
I'm working on implementing communication with Client-Certificate.
第 1 步:创建 PKCS#10 请求 (CSR) 并将其提供给我的服务器进行签名.服务器联系人将 CSR 传递给 CA,CA 对其进行签名,并返回一个 PKCS#7(带有签名的 PKCS#10 和 CA 的证书).
Step 1: Create a PKCS#10 request (CSR) and give it to the my server for signing. The server contacts passes the CSR to the CA, and the CA signs it, and returns a PKCS#7 (with the signed PKCS#10 and the CA's certificate).
第 2 步:创建 PKCS#12,将其安全地存储在 Android 设备上
Step 2: Create a PKCS#12, store it securely on the Android device
步骤 3:创建 SSL 连接,以便客户端根据证书进行身份验证.
Step 3: Create SSL connection so that the client will be authenticated according to the certificate.
现在,第 1 步使用 SpongyCastle 1.50.0.0 可以完美运行,但我被困在其他步骤上......我目前遇到了 SSL-Handshake 异常,但我觉得我应该重新考虑我的实现.
Now, Step 1 works perfectly using SpongyCastle 1.50.0.0, but I'm stuck on the other steps... I'm currently getting an SSL-Handshake exception, but I got the feeling that I should re-think on my implementation.
问题
有人知道如何实现流程吗?如何创建和存储客户端证书所需的任何内容以与 Android 的 SSLContext 一起工作,以及如何创建这样的 SSLContext?
Does anyone know how to implement the flow? How to create and store whatever's needed for a client side certificate to work well with Android's SSLContext, and how to create such SSLContext?
到目前为止我尝试了什么
我的第一次尝试是使用 KeyChain,但我们想避免那里描述的用户交互.我的第二次尝试是遵循 Rich Freedman 的步骤,但我不知道如何从 PKCS#7 和私钥创建 PKCS#12.为了持久性,我回顾了 这篇文章,但是(a)它是 C#,(b)它是未加密的,(c)我认为 android 平台有更好的密钥持久性机制,我对此一无所知.最后,这段代码(用于从 PEM 和 PKCS#7 创建 PKCS12)效果不佳,因为我不知道如何获取 CER 文件以及它需要的其他内容.
My first attempt was to use the KeyChain, but we'd like to avoid the user interaction as described there. My second attempt was to follow Rich Freedman's steps, but I don't know how to create a PKCS#12 from the PKCS#7 and the private key. For persistence, I went over this post, but (a) it's C#, (b) it's unencrypted and (c) I think that the android platform has a better keys persistence mechanism, one that I yet know nothing about. Lastly, this code (for creating a PKCS12 from PEM and PKCS#7) didn't work as well, as I didn't know how to get a CER file and the rest of the things it needs.
谢谢!
推荐答案
也许不是最好的代码,但它确实有效,它并没有严格回答你所有的问题,但也许你会找到可以使用的部分.
Maybe not the best code, but it works, it does not strictly answer all you questions but maybe you will find pieces you can use.
你的流程很好,我正在做几乎同样的事情.
Your flow is good, I'm doing the almost the same thing.
我将密钥保存在动态创建的密钥库中.另外,我有使用 openssl 工具创建的带有可信证书的密钥库.
I'm keeping my keys in dynamically created keystore. Additionaly i have keystore with trusted certificates created with openssl tool.
为了沟通,我使用了 okHttp + 改造
For communication I've used okHttp + retrofit
https://github.com/square/okhttphttps://github.com/square/retrofit
生成KeyPair
:
public static KeyPair generateKeyPair() throws NoSuchAlgorithmException {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(2048);
KeyPair keyPair = keyPairGenerator.genKeyPair();
return keyPair;
}
生成csr:
private static PKCS10CertificationRequest generateCSRFile(KeyPair keyPair) throws IOException, OperatorCreationException {
String principal = "CN=company1, OU=company1, O=company1, C=GB";
AsymmetricKeyParameter privateKey = PrivateKeyFactory.createKey(keyPair.getPrivate().getEncoded());
AlgorithmIdentifier signatureAlgorithm = new DefaultSignatureAlgorithmIdentifierFinder()
.find("SHA1WITHRSA");
AlgorithmIdentifier digestAlgorithm = new DefaultDigestAlgorithmIdentifierFinder().find("SHA-1");
ContentSigner signer = new BcRSAContentSignerBuilder(signatureAlgorithm, digestAlgorithm).build(privateKey);
PKCS10CertificationRequestBuilder csrBuilder = new JcaPKCS10CertificationRequestBuilder(new X500Name(
principal), keyPair.getPublic());
ExtensionsGenerator extensionsGenerator = new ExtensionsGenerator();
extensionsGenerator.addExtension(X509Extension.basicConstraints, true, new BasicConstraints(true));
extensionsGenerator.addExtension(X509Extension.keyUsage, true, new KeyUsage(KeyUsage.keyCertSign
| KeyUsage.cRLSign));
csrBuilder.addAttribute(PKCSObjectIdentifiers.pkcs_9_at_extensionRequest, extensionsGenerator.generate());
PKCS10CertificationRequest csr = csrBuilder.build(signer);
return csr;
}
发送csr(可能需要转成pem格式),接收证书.
Send csr (you may need to convert it to pem format), receive certificate .
初始化密钥库:
KeyStore store = KeyStore.getInstance("BKS");
InputStream in;
try {
in = App.getInstance().getApplicationContext().openFileInput(filename);
try {
store.load(in, password);
} finally {
in.close();
}
} catch (FileNotFoundException e) {
//create new keystore
store.load(null, password);
}
初始化信任库:
KeyStore trustStore = KeyStore.getInstance("BKS");
InputStream in = App.getInstance().getApplicationContext().getResources().openRawResource(R.raw.truststore);
try {
trustStore.load(in, trustorePassword);
} finally {
in.close();
}
将密钥添加到密钥库(确保您的私钥和证书匹配,如果不匹配,密钥库不会抛出异常,并且使用 okHttp 这可能会导致 libssl 崩溃(仅在 api 低于 4.1 的设备上):
Add key to keystore (make sure your private key, and certificate match, keystore won't throw exception if they don't, and with okHttp this can cause libssl crashes (only on devices with api below 4.1):
keyStore.setKeyEntry(alias, privateKey, password, new X509Certificate[]{certificate});
用自己的SSLContext
创建okHttpClient:
Create okHttpClient with its own SSLContext
:
OkHttpClient client = new OkHttpClient();
KeyStore keyStore = App.getInstance().getKeyStoreUtil().getKeyStore();
KeyStore trustStore = App.getInstance().getKeyStoreUtil().getTrustStore();
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(trustStore);
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmf.init(keyStore, keyStorePassword);
SSLContext sslCtx = SSLContext.getInstance("TLS");
sslCtx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
client.setSslSocketFactory(sslCtx.getSocketFactory());
client.setHostnameVerifier(org.apache.http.conn.ssl.SSLSocketFactory.STRICT_HOSTNAME_VERIFIER);
查看 Nikolay Elenkov 博客,您还可以找到许多有用的信息以及源代码.
Look at Nikolay Elenkov blog, you can find many usefull informations with source code as well.
- http://nelenkov.blogspot.com/
- http://nelenkov.blogspot.com/2011/11/using-ics-keychain-api.html
- http://nelenkov.blogspot.in/2011/12/ics-trust-store-implementation.html
- http://nelenkov.blogspot.com/2011/12/using-custom-certificate-trust-store-on.html
- http://nelenkov.blogspot.com/2012/05/storing-application-secrets-in-androids.html
- http://nelenkov.blogspot.com/2013/08/credential-storage-enhancements-android-43.html
@编辑
发布您的例外情况
@edit2
在您的情况下,您需要从 Web 服务响应中提取您的 X509Certificate
,将其存储在密钥库中,其中私钥用于生成 csr 请求,并将 CA 证书存储在另一个将用作信任库的密钥库中.(可以是同一个keystore,但不推荐).
In your case you need to extract your X509Certificate
from webservice response, store it in keystore with privatekey used for generating csr request and store CA cert in another keystore which will work as truststore. (It can be the same keystore, but it's not recommended).
这篇关于使用 SpongyCastle 使用来自 PKCS#10 的客户端证书创建 Https 连接的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!