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

Fel*_*lix 3 security ssl android ssl-certificate spongycastle

目标

我正在努力实现与客户证书的沟通.

步骤1:创建PKCS#10请求(CSR)并将其提供给我的服务器进行签名.服务器联系人将CSR传递给CA,CA对其进行签名,并返回PKCS#7(带有签名的PKCS#10和CA的证书).

第2步:创建PKCS#12,将其安全地存储在Android设备上

步骤3:创建SSL连接,以便根据证书对客户端进行身份验证.

现在,第1步使用SpongyCastle 1.50.0.0完美运行,但是我坚持其他步骤......我目前正在获得SSL握手异常,但我觉得我应该重新思考我的实现.

这个问题

有谁知道如何实现流程?如何创建和存储客户端证书与Android的SSLContext一起使用所需的一切,以及如何创建这样的SSLContext?

到目前为止我尝试了什么

我的第一次尝试是使用KeyChain,但我们希望避免那里描述的用户交互.我的第二次尝试是遵循Rich Freedman的步骤,但我不知道如何从PKCS#7和私钥创建PKCS#12.对于持久性,我去了这个帖子,但(一)这是C#,(B),它是未加密和(c)我认为,Android系统平台拥有更好的按键持久性机制,一个是我还一无所知.最后,这段代码(用于从PEM和PKCS#7创建PKCS12)不起作用,因为我不知道如何获得CER文件以及它需要的其他东西.

谢谢!

Tha*_*han 5

也许不是最好的代码,但它有效,它并不能严格回答你所有的问题,但也许你会找到你可以使用的部分.

你的流量很好,我做的几乎是一样的.

我将密钥保存在动态创建的密钥库中.另外,我有一个带有openssl工具创建的可信证书的密钥库.

为了沟通,我使用了okHttp +改造

https://github.com/square/okhttp https://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;
}
Run Code Online (Sandbox Code Playgroud)

生成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;
}
Run Code Online (Sandbox Code Playgroud)

发送csr(您可能需要将其转换为pem格式),接收证书.

Init密钥库:

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);
    }
Run Code Online (Sandbox Code Playgroud)

Init truststore:

KeyStore trustStore = KeyStore.getInstance("BKS");
InputStream in = App.getInstance().getApplicationContext().getResources().openRawResource(R.raw.truststore);
try {
    trustStore.load(in, trustorePassword);
} finally {
    in.close();
}
Run Code Online (Sandbox Code Playgroud)

将密钥添加到密钥库(确保您的私钥和证书匹配,密钥库不会抛出异常,如果不是,并且使用okHttp,这可能导致libssl崩溃(仅在api低于4.1的设备上):

keyStore.setKeyEntry(alias, privateKey, password, new X509Certificate[]{certificate});
Run Code Online (Sandbox Code Playgroud)

用自己的方法创建okHttpClient 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);
Run Code Online (Sandbox Code Playgroud)

看看Nikolay Elenkov的博客,你也可以找到许多有用的源代码信息.

@编辑

发布您的例外

@ EDIT2

在您的情况下,您需要X509Certificate从web服务响应中提取,将其存储在密钥库中,其中privatekey用于生成csr请求,并将CA证书存储在另一个密钥库中,该密钥库将用作信任库.(它可以是相同的密钥库,但不建议这样做).