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

时间:2013-01-01 21:41:52

标签: java android https bouncycastle keystore

我正在尝试与证书设置为2013年4月到期的server建立HTTPS连接,并使用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();

现在,当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()失败。

我有ensured I'm passing -storetype BKSused a <=7 character keystore password,将CA证书添加到密钥存储区,并使用Bouncy Castle版本1.45和1.47创建密钥存储区,而不会更改报告的错误消息。< / p>

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

如果有人能解释如何在Windows 8上创建有效的BKS密钥存储区,或者我如何让Java使用与浏览器(或系统?)相同的密钥存储区,那将是值得赞赏的。

您可以在撰写本文时下载entire project,如果需要,可以下载generated keystore

2 个答案:

答案 0 :(得分:3)

感谢各界人士对此的暗示,有许多事情都需要正确才能发挥作用。

  1. 如果HTTPS站点的证书由受信任的根证书签名,那么它将在没有自定义SSLSocketFactory的情况下开箱即用。 受信任的根证书可以与浏览器使用的证书不同,因此不要假设如果它在Android Web浏览器中有效,那么它将在您的应用程序中运行。
    如果它不是受信任的根证书,并且您获得javax.net.ssl.SSLHandshakeException: org.bouncycastle.jce.exception.ExtCertPathValidatorException: Could not validate certificate signature.之类的例外,那么您需要创建并加载密钥库,如下所示。

  2. 需要使用Bouncy Castle提供程序(1)生成密钥存储区,方法是在-storetype bks命令行上指定keytool。 如果没有正确安装Bouncy Castle,那么这将失败,包括java.security.KeyStoreException: BKS not found在内的各种例外情况。 如果未使用Bouncy Castle提供程序创建密钥库,则可能会出现java.io.IOException: Wrong version of key store.异常,从而导致与下一个案例混淆。

  3. 您需要使用Bouncy Castle提供商的相应版本(123)。在大多数情况下,这似乎是version 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) does not match SubjectName(CN=ABC) of signing certificate.或再次获得通用javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found. 我还没有办法在密钥库中订购它们,所以求助于doing it in code at runtime

  6. 有些人认为using a keystore password longer than 7 characters也会导致失败,但这不是我发现的。

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

答案 1 :(得分:2)

Bouncy Castle 1.47正在使用不同版本的标题。你可以尝试1.46 version,它应该有效。

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