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

2022-09-03 15:57:51

我正在尝试与证书设置为 2013 年 4 月到期的服务器建立 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()

已确保我正在传递 -store 类型 BKS使用 <=7 个字符的密钥库密码,将 CA 证书添加到密钥存储,并使用 Bouncy Castle 版本 1.45 和 1.47 来创建密钥存储,报告的错误消息没有变化。

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

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

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


答案 1

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

  1. 如果HTTPS站点的证书由受信任的根证书签名,那么它将在没有自定义的情况下开箱即用。受信任的根证书可能与浏览器使用的根证书不同,因此不要假设如果它在 Android Web 浏览器中工作,那么它将在您的应用程序中工作。
    如果它不是受信任的根证书,并且您收到类似 的异常,则需要创建并加载密钥存储,如下所示。SSLSocketFactoryjavax.net.ssl.SSLHandshakeException: org.bouncycastle.jce.exception.ExtCertPathValidatorException: Could not validate certificate signature.

  2. 密钥存储需要使用充气城堡提供程序 (1) 通过在命令行上指定来生成。
    如果未正确安装充气城堡,则此操作将失败,并出现各种例外情况,包括.如果未使用 Bouncy Castle 提供程序创建密钥存储,则可能会遇到异常,从而导致与下一个案例混淆。-storetype bkskeytooljava.security.KeyStoreException: BKS not foundjava.io.IOException: Wrong version of key store.

  3. 您需要使用适当版本(123)的充气城堡提供程序。在大多数情况下,这似乎是版本 1.46
    这可以放入 JRE 的文件夹中,并将类名添加到 中,或直接在命令行上指定为 。如果它是不兼容的版本(或存储类型),您将再次获得异常。lib/ext/lib/security/java.securitykeytooljava.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添加到相关问题中。


答案 2

充气城堡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