14

我正在尝试与证书设置为 2013 年 4 月到期并使用 GlobalSign 作为根证书的服务器建立 HTTPS 连接。

HttpsURLConnection urlConnection = (HttpsURLConnection) url.openConnection();
// urlConnection.setSSLSocketFactory(sslSocketFactory);
urlConnection.setDoOutput(true);
urlConnection.setChunkedStreamingMode(0);

// Send the POST data
OutputStream out = new BufferedOutputStream(urlConnection.getOutputStream());
out.write(postParamString.toString().getBytes("UTF8"));

// Read the reply
InputStream in = urlConnection.getInputStream();

javax.net.ssl.SSLHandshakeException: org.bouncycastle.jce.exception.ExtCertPathValidatorException: Could not validate certificate signature.就目前而言,这在getOutputStream()被调用时抛出。

此相同站点和证书在股票 HTC 网络浏览器和桌面浏览器中有效。当我使用相同的代码访问Google时,它​​可以工作(但随后抱怨 404 错误)。StackOverflow 上的各种帖子暗示它应该“正常工作”,其他人说设置自己的密钥存储(或禁用所有 HTTPS 验证!)我认为行为差异取决于使用的不同根密钥存储(谁能澄清这一点?)。

我现在尝试使用充气城堡创建密钥存储,但无法将其加载到我的设备上。

从 Firefox 导出证书后,我使用以下命令创建密钥库:

keytool.exe -import -alias onlinescoutmanager -file www.onlinescoutmanager.co.uk.crt -storetype BKS -keystore res\raw\keystore

然后使用以下方法在应用程序中加载和使用它:

InputStream stream = context.getResources().openRawResource(R.raw.keystore);
// BKS seems to be the default but we want to be explicit
KeyStore ks = KeyStore.getInstance("BKS");
ks.load(stream, "www.onlinescoutmanager.co.uk".toCharArray());
stream.close();

TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(ks);
X509TrustManager defaultTrustManager = (X509TrustManager) tmf.getTrustManagers()[0];
SSLContext context2 = SSLContext.getInstance("TLS");
context2.init(null, new TrustManager[] { defaultTrustManager }, null);
sslSocketFactory = context2.getSocketFactory();

java.io.IOException: Wrong version of key store.这在何时keystore.Load()被调用时失败。

我已确保通过-storetype BKS使用 <=7 字符密钥库密码,将 CA 证书添加到密钥库,并使用 Bouncy Castle 版本 1.45 和 1.47 创建密钥库,报告的错误消息没有变化。

我的环境是在 Windows 8 上运行 JRE 1.7u9b5 的 Eclipse Juno 4.2.1。我正在测试的设备是运行股票 Android 2.3 的 HTC 感觉。该应用程序的最低 SDK 版本为 7,目标为 15。

如果有人能解释如何在 Windows 8 上创建有效的 BKS 密钥库,或者我如何让 Java 使用与浏览器(或系统?)相同的密钥库,那将不胜感激。

您可以下载撰写本文时的整个项目,如果需要,还可以下载生成的密钥库

4

2 回答 2

3

感谢各种人对此的提示,有很多事情都需要正确才能使其正常工作。

  1. 如果 HTTPS 站点的证书由受信任的根证书签名,则无需自定义SSLSocketFactory. 受信任的根证书可能与浏览器使用的证书不同,因此不要假设如果它在 Android 网络浏览器中工作,那么它将在您的应用程序中工作。
    如果它不是受信任的根证书并且您遇到类似的异常javax.net.ssl.SSLHandshakeException: org.bouncycastle.jce.exception.ExtCertPathValidatorException: Could not validate certificate signature.,那么您需要创建并加载一个密钥库,如下所示。

  2. 密钥库需要使用 Bouncy Castle 提供程序 ( 1 ) 通过-storetype bkskeytool命令行上指定来生成。
    如果 Bouncy Castle 没有正确安装,那么这将失败并出现各种异常,包括java.security.KeyStoreException: BKS not found. 如果密钥库不是使用 Bouncy Castle 提供程序创建的,那么您可能会遇到java.io.IOException: Wrong version of key store.异常,从而导致与下一个案例混淆。

  3. 您需要使用 Bouncy Castle 提供程序的适当版本(123)。在大多数情况下,这似乎是1.46 版本
    这可以放入您的 JRElib/ext/文件夹并将类名添加到lib/security/java.security,或直接在命令行中指定到keytool. 如果它是不兼容的版本(或商店类型),您将再次遇到异常java.io.IOException: Wrong version of key store.

  4. 您必须包括所有中介和根证书。如果缺少任何内容,您将获得javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.异常。

  5. 证书链必须是为了正确验证它们。如果不是,您将获得javax.net.ssl.SSLHandshakeException: org.bouncycastle.jce.exception.ExtCertPathValidatorException: IssuerName(CN=XYZ) does not match SubjectName(CN=ABC) of signing certificate.或再次获得一个通用的,javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found. 我还没有找到在密钥库中对它们进行排序的方法,因此只能在运行时在代码中进行

有些人建议使用超过 7 个字符的密钥库密码也会导致它失败,但我发现不是这样。

我认为这涵盖了我发现的所有陷阱,但请随时扩展并将 linsk 添加到相关问题中。

于 2013-01-03T00:22:29.570 回答
2

Bouncy Castle 1.47 使用不同的版本标头。你可以试试1.46 版本,它应该可以工作。

keytool -import -alias onlinescoutmanager -file www.onlinescoutmanager.co.uk.crt -storetype BKS -storepass osmosm -keystore C:/keystore -provider org.bouncycastle.jce.provider.BouncyCastleProvider -providerpath bcprov-ext-jdk15on-1.46.jar
于 2013-01-02T03:11:26.290 回答