Android 上具有客户端身份验证的 SSL/TLS http 客户端的运行时配置

发布于 2024-12-02 06:30:59 字数 2921 浏览 1 评论 0原文

以下测试设置在 Android 模拟器上运行良好。它打开一个基于 SSL/TLS 的连接 使用相互身份验证的外部服务器:

ca.crt(验证服务器证书):
-----开始证书-----
BASE64 编码的东西
-----END CERTIFICATE-----

client.p12(包括由服务器信任的私有 CA 签名的客户端证书): PKCS#12 格式

成功运行的 Java/Android 代码:

trustStore = KeyStore.getInstance("bks");
trustStore.load(null, null);
caCertificate = getX509Certificate("/some/path/ca.crt");
trustStore.setCertificateEntry("ca-cert", caCertificate);

keyStore = KeyStore.getInstance("pkcs12");
keyStore.load(null, null);
InputStream is = new FileInputStream("/some/path/client.p12");
keyStore.load(is, "passwd".toCharArray());

TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance("X509");
trustManagerFactory.init(trustStore);
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("X509");
keyManagerFactory.init(keyStore, null);

context = SSLContext.getInstance("TLS");
context.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), new SecureRandom());

URL url = new URL("https://www.backend.com");
HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
connection.setSSLSocketFactory(context.getSocketFactory());
connection.setDoInput(true);
connection.setDoInput(true);
BufferedReader urlReader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
String inputLine;
while ( (inputLine=urlReader.readLine()) != null ){
    System.out.println(inputLine);
}

但是, client.p12 在运行时不可用。 http 客户端通过单独的接收以下配置 专用通道:

  • PEM 编码的 X509 客户端证书 (client.crt)
  • DER 格式的私钥客户端 (client.der)

因此,我将上面以 client.p12 作为输入的密钥库初始化更改为以下内容:

keyStore = KeyStore.getInstance("bks");
keyStore.load(null, null);
clientCertificate = getX509Certificate("/some/path/client.crt");
byte[] privateKey =  getBytesFromFile("/some/path/client.der");
Certificate[] chain = new Certificate[2];
chain[1] = caCertificate;
chain[0] = clientCertificate;        
keyStore.setCertificateEntry("client-cert", clientCertificate);
keyStore.setKeyEntry("client-cert-key", privateKey, chain);

运行时,抛出异常执行

context = SSLContext.getInstance("TLS");
---->  context.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), new SecureRandom());

java.lang.RuntimeException: forget something!
at org.bouncycastle.jce.provider.JDKKeyStore$StoreEntry.getObject(JDKKeyStore.java:314)
at org.bouncycastle.jce.provider.JDKKeyStore.engineGetKey(JDKKeyStore.java:604)
at java.security.KeyStoreSpi.engineGetEntry(KeyStoreSpi.java:376)
at java.security.KeyStore.getEntry(KeyStore.java:734)
at org.apache.harmony.xnet.provider.jsse.KeyManagerImpl.<init>(KeyManagerImpl.java:72)

摘要:使用 pcks12 证书/私钥对一切正常,但不使用上述格式的两者。

对于前面提到的 client.der/client.pem,有什么建议或建议实施客户端身份验证吗?

PS 运行 keytool 运行时不是一个选项,因为我在运行时没有它,而且我不想那样做。

The following test setup runs fine on the Android emulator. It opens a SSL/TLS based connection to an
external server using mutual authentication:

ca.crt (to verify the server certificate):
-----BEGIN CERTIFICATE-----
BASE64 ENCODED STUFF
-----END CERTIFICATE-----

client.p12 (including a client certificate signed by a private CA which is trusted by the server): PKCS#12 format

The Java/Android code which runs succesfull:

trustStore = KeyStore.getInstance("bks");
trustStore.load(null, null);
caCertificate = getX509Certificate("/some/path/ca.crt");
trustStore.setCertificateEntry("ca-cert", caCertificate);

keyStore = KeyStore.getInstance("pkcs12");
keyStore.load(null, null);
InputStream is = new FileInputStream("/some/path/client.p12");
keyStore.load(is, "passwd".toCharArray());

TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance("X509");
trustManagerFactory.init(trustStore);
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("X509");
keyManagerFactory.init(keyStore, null);

context = SSLContext.getInstance("TLS");
context.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), new SecureRandom());

URL url = new URL("https://www.backend.com");
HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
connection.setSSLSocketFactory(context.getSocketFactory());
connection.setDoInput(true);
connection.setDoInput(true);
BufferedReader urlReader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
String inputLine;
while ( (inputLine=urlReader.readLine()) != null ){
    System.out.println(inputLine);
}

However, client.p12 is not available at runtime. The http client receives following configuration via a separate
dedicated channel:

  • PEM encoded X509 client certificate (client.crt)
  • DER formatted private key client (client.der)

Therefore I changed the keystore intitialisation above taking the client.p12 as input, into following:

keyStore = KeyStore.getInstance("bks");
keyStore.load(null, null);
clientCertificate = getX509Certificate("/some/path/client.crt");
byte[] privateKey =  getBytesFromFile("/some/path/client.der");
Certificate[] chain = new Certificate[2];
chain[1] = caCertificate;
chain[0] = clientCertificate;        
keyStore.setCertificateEntry("client-cert", clientCertificate);
keyStore.setKeyEntry("client-cert-key", privateKey, chain);

runtime an exception is thrown when executing

context = SSLContext.getInstance("TLS");
---->  context.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), new SecureRandom());

java.lang.RuntimeException: forget something!
at org.bouncycastle.jce.provider.JDKKeyStore$StoreEntry.getObject(JDKKeyStore.java:314)
at org.bouncycastle.jce.provider.JDKKeyStore.engineGetKey(JDKKeyStore.java:604)
at java.security.KeyStoreSpi.engineGetEntry(KeyStoreSpi.java:376)
at java.security.KeyStore.getEntry(KeyStore.java:734)
at org.apache.harmony.xnet.provider.jsse.KeyManagerImpl.<init>(KeyManagerImpl.java:72)

Summary: everything is working using a pcks12 certificate/privatekey pair, but not using the two in the mentioned format.

Any suggestions what is going wrong or a suggestion for implementing client authentication given the client.der/client.pem mentioned previously?

P.S. running the keytool runtime is not an option, because I don't have it at runtime and I do not want to go that way.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

很酷不放纵 2024-12-09 06:30:59

简单:未实现将键作为字节数组传递。引用JDKKeyStore.java

        else
        {
            throw new RuntimeException("forget something!");
            // TODO
            // if we get to here key was saved as byte data, which
            // according to the docs means it must be a private key
            // in EncryptedPrivateKeyInfo (PKCS8 format), later...
            //
        }

您可以尝试使用void setKeyEntry(Stringalias, Key key, char[]password,Certificate[]chain)来注册您的密钥和证书方法,似乎是支持的(未经测试)。

Simple: passing a key as byte array is not implemented. Quote from JDKKeyStore.java:

        else
        {
            throw new RuntimeException("forget something!");
            // TODO
            // if we get to here key was saved as byte data, which
            // according to the docs means it must be a private key
            // in EncryptedPrivateKeyInfo (PKCS8 format), later...
            //
        }

You can try to register your key and certificate using the void setKeyEntry(String alias, Key key, char[] password, Certificate[] chain) method, it seems it is supported (untested).

无可置疑 2024-12-09 06:30:59

后有效

sslContext.init(kmf.keyManagers, null, null)

对我来说,它在变成

sslContext.init(kmf.keyManagers, null, SecureRandom())

for me it worked after changing

sslContext.init(kmf.keyManagers, null, null)

into

sslContext.init(kmf.keyManagers, null, SecureRandom())
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文