使用URL.openConnection()建立HTTPS连接

15

我试图创建一个HTTPS连接到一个证书将在2013年四月过期,使用GlobalSign作为根证书的服务器

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

目前情况是,当调用getOutputStream()时,会抛出javax.net.ssl.SSLHandshakeException: org.bouncycastle.jce.exception.ExtCertPathValidatorException: Could not validate certificate signature.的异常。

相同的网站和证书在原装HTC网络浏览器和桌面浏览器中有效。当我使用相同的代码访问Google时,它可以正常工作(但然后会抱怨404错误)。StackOverflow上的各种帖子暗示它应该“只是工作”,而其他人则建议设置自己的密钥库(或禁用所有HTTPS验证!)我假设行为上的差异归因于使用的不同根密钥库(有人可以澄清一下吗?)。

我现在尝试使用Bouncy Castle创建密钥库,但无法在我的设备上加载它。

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

在调用keystore.Load()时,出现了java.io.IOException: Wrong version of key store.的错误。

我已经确保传递了-storetype BKS使用了长度小于等于7个字符的密钥库密码,将CA证书添加到了密钥库中,并且使用Bouncy Castle版本1.45和1.47创建了密钥库,但报告的错误消息没有改变。

我的开发环境是运行在Windows 8上的Eclipse Juno 4.2.1,使用JRE 1.7u9b5。我正在测试的设备是一台运行原生Android 2.3的HTC Sensation手机。该应用程序的最低SDK版本为7,目标版本为15。

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

您可以下载整个项目(包括撰写时的状态),以及必要时生成的密钥库


3
这绝对是我在任何网站上见过的最好的提问,用词组织非常精准。 - Andrea Ligios
@AndreaLigios 我已经回答了很多问题,我应该知道规则和如何“提出一个合理的问题”:p - Deanna
@Deanna 当您创建密钥库时,应该在选项中包含-trustedcacerts,以便使用受信任的证书验证证书吗?(从未使用过BKS,猜测基于核心Java的相似性) - Kevin D
我已经尝试将上面的两个CA证书添加到密钥库中并信任它们,但结果仍然相同 :|. - Deanna
那么错误是发生在Windows 8上?还是在Android设备上? - President James K. Polk
显示剩余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)生成,通过在keytool命令行上指定-storetype bks来实现。
    如果Bouncy Castle未正确安装,则会失败并出现各种异常,包括java.security.KeyStoreException:BKS not found。 如果未使用Bouncy Castle提供程序创建密钥库,则可能会出现java.io.IOException:Wrong version of key store.异常,从而导致下一个情况混淆。

  3. 您需要使用适当的版本(123)的Bouncy Castle提供程序。在大多数情况下,似乎是版本1.46
    这可以放入您的JRE的lib/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)与签名证书的SubjectName(CN=ABC)不匹配。,或者再次出现通用的javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found. 我没有找到一种在密钥库中对它们进行排序的方法,所以转而在运行时使用代码进行排序

有些人认为使用超过7个字符的密钥库密码也会导致其失败,但我没有发现这一点。

我认为这涵盖了我发现的每一个陷阱,但请随意扩展并添加相关问题的链接。


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

我已经在使用1.45版本(最初使用的是1.47版本,请参考问题),那么升级到1.46版本会有什么不同吗?请注意,我当前的目标是Android 2.3,它似乎使用的是1.45版本。 - Deanna
嗯,我没有尝试过1.45版本,但我可以说你的项目成功加载了由1.46版本创建的密钥库。 - Akdeniz
谢谢,使用1.46解决了版本问题。我已经写了一个完整的解决方案/说明如下。 - Deanna

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接