【问题标题】:Creating an Https connecion with Client Side Certificate from PKCS#10 with SpongyCastle使用 SpongyCastle 来自 PKCS#10 的客户端证书创建 Https 连接
【发布时间】:2014-06-25 20:37:49
【问题描述】:

目标

我正在努力实现与 Client-Certificate 的通信。

第 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's steps,但我不知道如何从 PKCS#7 和私钥创建 PKCS#12。对于持久性,我检查了this post,但是(a)它是 C#,(b)它是未加密的,并且(c)我认为 android 平台具有更好的密钥持久性机制,我对此一无所知。最后,this code(用于从 PEM 和 PKCS#7 创建 PKCS12)效果不佳,因为我不知道如何获取 CER 文件以及所需的其他内容。

谢谢!

【问题讨论】:

  • 嗨,我正在做同样的任务并且挂着大量代码,没有完全指导实现,经历了很多线程,你能帮我谈谈你在这个特定问题上的经验

标签: android security ssl ssl-certificate spongycastle


【解决方案1】:

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

你的流程很好,我正在做几乎同样的事情。

我将密钥保存在动态创建的密钥库中。另外,我有使用 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;
}

生成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格式),接收证书。

初始化密钥库:

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 的设备上):

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

用自己的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);

查看 Nikolay Elenkov 博客,您还可以找到许多有用的信息以及源代码。

@编辑

发布您的例外情况

@edit2

在您的情况下,您需要从 Web 服务响应中提取您的 X509Certificate,将其存储在密钥库中,并使用用于生成 csr 请求的私钥并将 CA 证书存储在另一个将用作信任库的密钥库中。 (可以是同一个keystore,但不推荐)。

【讨论】:

  • 首先,+1。我尝试了您写的所有内容,除了: 1. 我认为我可以使用 javax.HttpsURLConnection,并且不需要 OkHttpClient。 2.目前,我从“kmf.getKeyManagers()”中得到了一些异常(由于代码在 AsyncResult 中,所以它被隐藏了:()
  • 1.您可以使用 javax.HttpsURLConnection 它将正常工作。我有 okHttp 的代码,所以我发布了它。 2. 如果没有异常/代码,我无法判断出什么问题。
  • 它没有用。我从服务器收到 403,这意味着某些内容不适用于 Client-Certificate。除此之外,您的代码非常有帮助 - 所以我将此标记为答案,针对我遇到的更具体的问题打开了另一个问题 (stackoverflow.com/questions/24476591/…)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-09-28
  • 1970-01-01
  • 2017-01-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-05-19
相关资源
最近更新 更多