简体   繁体   English

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

[英]Creating an Https connecion with Client Side Certificate from PKCS#10 with SpongyCastle

The goal 目标

I'm working on implementing communication with Client-Certificate. 我正在努力实现与客户证书的沟通。

Step 1: Create a PKCS#10 request (CSR) and give it to the my server for signing. 步骤1:创建PKCS#10请求(CSR)并将其提供给我的服务器进行签名。 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). 服务器联系人将CSR传递给CA,CA对其进行签名,并返回PKCS#7(带有签名的PKCS#10和CA的证书)。

Step 2: Create a PKCS#12, store it securely on the Android device 第2步:创建PKCS#12,将其安全地存储在Android设备上

Step 3: Create SSL connection so that the client will be authenticated according to the certificate. 步骤3:创建SSL连接,以便根据证书对客户端进行身份验证。

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

The question 这个问题

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? 如何创建和存储客户端证书与Android的SSLContext一起使用所需的一切,以及如何创建这样的SSLContext?

What I tried so far 到目前为止我尝试了什么

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

Thanks! 谢谢!

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. 你的流量很好,我做的几乎是一样的。

I'm keeping my keys in dynamically created keystore. 我将密钥保存在动态创建的密钥库中。 Additionaly i have keystore with trusted certificates created with openssl tool. 另外,我有一个带有openssl工具创建的可信证书的密钥库。

For communication I've used okHttp + retrofit 为了沟通,我使用了okHttp +改造

https://github.com/square/okhttp https://github.com/square/retrofit https://github.com/square/okhttp https://github.com/square/retrofit

Generate KeyPair : 生成KeyPair

public static KeyPair generateKeyPair() throws NoSuchAlgorithmException {
    KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
    keyPairGenerator.initialize(2048);
    KeyPair keyPair = keyPairGenerator.genKeyPair();
    return keyPair;
}

Generate csr: 生成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;
}

Send csr (you may need to convert it to pem format), receive certificate . 发送csr(您可能需要将其转换为pem格式),接收证书。

Init keystore: 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);
    }

Init truststore: 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();
}

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

keyStore.setKeyEntry(alias, privateKey, password, new X509Certificate[]{certificate});

Create okHttpClient with its own SSLContext : 使用自己的SSLContext创建okHttpClient:

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);

Look at Nikolay Elenkov blog, you can find many usefull informations with source code as well. 看看Nikolay Elenkov的博客,你也可以找到许多有用的源代码信息。

@edit @编辑

Post your exception 发布您的例外

@edit2 @ EDIT2

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. 在您的情况下,您需要从Web服务响应中提取X509Certificate ,将其存储在密钥库中,其中privatekey用于生成csr请求,并将CA证书存储在另一个密钥库中,该密钥库将用作信任库。 (It can be the same keystore, but it's not recommended). (它可以是相同的密钥库,但不建议这样做)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM