使用给定的 p12 证书连接到 https 站点
Posted
技术标签:
【中文标题】使用给定的 p12 证书连接到 https 站点【英文标题】:Connect to a https site with a given p12 certificate 【发布时间】:2011-10-23 02:57:02 【问题描述】:服务器端给了我一个.p12
证书文件,我点击并安装在我的机器上,然后我可以通过浏览器访问HTTPS
站点。现在他们希望我使用给定的证书抓取他们的网站。我被困在它的第一阶段,试图从httpsURLConnection
获取inputStream
。该网站没有登录。它只检查你是否有证书。
到目前为止,我所做的是使用 Firefox 以 .crt
文件格式导出证书。然后我使用 keytool 命令将它(.crt
文件,而不是 .p12
)导入到 java 密钥库中。然后在代码中:
KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
File ksFile = new File(keystorePath);
in = new FileInputStream(ksFile);
ks.load(in, "changeit".toCharArray());
X509Certificate cert = (X509Certificate) ks.getCertificate(certificateAlias);
SSLContext sc = SSLContext.getInstance("TLS");
sc.init(null, trustAllCerts, new java.security.SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
HttpsURLConnection con = (HttpsURLConnection) (new URL(urlString)).openConnection();
con.connect();
con.getInputStream();
con.disconnect();
getInputStream()
会给我 403 错误禁止访问。我搜索了其他相关主题,实际上比阅读它们之前更加困惑。非常感谢您的回答。
其他细节:
我刚刚实例化了证书,并没有让程序知道任何类型的密钥(私有、公共等)。所以我认为我必须将这些密钥提供给服务器,让它知道我实际上持有证书。我完全不知道如何做到这一点,无论是逻辑还是语法。 我已尝试使用 keytool 命令将 .p12 证书文件导入密钥库,但不知何故,keytool 无法识别 -pkcs12 选项。关于如何直接使用这个 .p12 证书的任何想法也很好。 trustAllCert 是 TrustMangers 的单元素数组,它不验证任何内容(全部信任)。我不知道我是否应该继续使用这个。事实上,现在我实际上只有一个证书可以信任。在这种情况下,编写 trustManger 的正确方法是什么? 我无法控制服务器端。我得到的只是访问他们网站的 URL,这是在 HTTPS 协议下的,以及一个 .p12 证书。该网站没有登录。如果安装了证书,我就可以进去了。【问题讨论】:
【参考方案1】:添加这个作为答案,因为我需要更多的空间来写。
首先,一个问题:证书是否由威瑞信等受信任的权威机构签署?如果不是,则信任库应具有使 p12 证书“有效”的 CA 证书(通常是 .pem 文件)。默认的 java 信任库包含来自大公司的大部分(如果不是全部)CA 证书,例如 Verisign 和 Thawte。
此外,您可以测试您的应用以连接到安全服务器,而无需编写 SSL 配置,但使用一些命令行参数,例如:
java -Djavax.net.ssl.keyStore=[path_to_p12_cert] \
-Djavax.net.ssl.keyStorePassword=[p12_password] \
-Djavax.net.ssl.keyStoreType=PKCS12 \
-Djavax.net.ssl.trustStore=[path_to_trust_store_with_CA_certificates] \
-Djavax.net.ssl.trustStorePassword=[trust_store_password] \
[MainClass]
然后你的代码就变成了
HttpsURLConnection con = (HttpsURLConnection) (new URL(urlString)).openConnection();
con.connect();
con.getInputStream();
con.disconnect();
如果您感到自虐,JSSE ref guide 非常有趣。
【讨论】:
嗨。谢谢你的帮助。我不知道如何生成证书。我的猜测是服务器正在使用 keytool -genkey 或某种自签名或易于创建的证书。对于密钥库,cacerts 文件(我相信默认)是空的。我导入的证书是目前里面唯一的证书。即使我将文件移出 $JAVA_HOME/jre/lib/security,我仍然可以正常进入网站。我会查看您链接的指南。 您可以在firefox中看到颁发机构。转到网站,然后单击地址栏左侧的 。出现一个弹出窗口,点击“更多信息”,点击“安全”选项卡。然后单击“查看证书”并检查显示“颁发者”的信息。如果这不是全球受信任的 CA 机构之一,那么您需要获取该 CA 证书并使用该 CA 证书创建一个信任库。 嗨。我检查了发行人,结果是 Entrust, Inc.。我相信它是全球受信任的 CA 机构。对于我的情况,哪一个是不幸的,因为您的建议适用于不是的情况? Entrust 不在默认的 cacerts 文件中(它位于 $JAVA_HOME/jre/lib/security/cacerts 中),至少在 JDK 1.6.26 中是这样。您可以通过在同一文件夹中运行命令keytool -list -keystore cacerts
来列出所有 CA 证书。我认为您可以从 this 页面从 Entrust 下载 CA 证书。您只需要其中一个证书,在firefox中检查证书名称,站点中应该有一个匹配的。
你好。不幸的是,这无济于事。该证书原来是一个由 3 个证书组成的链。根是 Entrust 2048,我是从您链接的站点导入的。中间的是委托证书颁发机构 L1B,我不知道如何获得。最后一个是我提供的 .p12 文件。安装根 2048 证书后,我仍然收到 403 错误。我相信我需要做的不仅仅是导入所有必要的证书。类似于通过某种请求或身份验证向服务器提供证书/密钥?【参考方案2】:
如果您想尝试编写 SSL 配置代码,您可以使用提供给您的 P12 文件,而无需将其转换为 JKS。此外,您将需要使用 P12 中的私钥,而不仅仅是复制到 JKS 中的证书。不确定这是否会直接满足您的需求,但这可能会让您走上正确的道路:
KeyStore clientStore = KeyStore.getInstance("PKCS12");
clientStore.load(new FileInputStream("test.p12"), "testPass".toCharArray());
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmf.init(clientStore, "testPass".toCharArray());
KeyManager[] kms = kmf.getKeyManagers();
KeyStore trustStore = KeyStore.getInstance("JKS");
trustStore.load(new FileInputStream("cacerts"), "changeit".toCharArray());
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(trustStore);
TrustManager[] tms = tmf.getTrustManagers();
SSLContext sslContext = null;
sslContext = SSLContext.getInstance("TLS");
sslContext.init(kms, tms, new SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory());
URL url = new URL("https://www.testurl.com");
HttpsURLConnection urlConn = (HttpsURLConnection) url.openConnection();
以这种方式配置 trustStore 是可选的。您可以使用 P12 链中的所有证书创建一个 JKS,或者只是确保它们在您的 JRE 的 cacerts 文件中。至于keytool,作为参考,您可以在P12上运行keytool命令(指定-storetype pkcs12),但不能将P12导入JKS。您也不能使用 keytool 命令从 P12 中仅导出一个密钥。
我目前没有设置服务器来测试这段代码,所以试一试,看看你是否仍然收到 403 错误。
【讨论】:
您好。非常感谢。这完美地解决了我的问题。所以这就是我错过了关键管理人员部分? 我对这个主题很陌生,不了解 JKS 密钥库的部分。我也只有一个 p12 文件,但我不知道如何获取 JKS 文件。任何帮助都会很棒。 就我而言,我需要sslContext = SSLContext.getInstance("SSL");
。
这个答案也解决了我的问题。非常感谢,@bobz32!【参考方案3】:
简单的 keytool 命令会将您的 .p12 密钥库导出到 .jks 密钥库:
keytool -importkeystore -srckeystore keystore.p12 -srcstoretype PKCS12 -deststoretype JKS -destkeystore keystore.jks
【讨论】:
【参考方案4】:这对我有用:
KeyStore keyStore = KeyStore.getInstance("PKCS12");
FileInputStream instream = new FileInputStream(new File("client-p12-keystore.p12"));
try
keyStore.load(instream, "password".toCharArray());
finally
instream.close();
// Trust own CA and all self-signed certs
SSLContext sslcontext = SSLContexts.custom()
.loadKeyMaterial(keyStore, "password".toCharArray())
//.loadTrustMaterial(trustStore, new TrustSelfSignedStrategy())
.build();
// Allow TLSv1 protocol only
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
sslcontext,
SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
CloseableHttpClient httpclient = HttpClients.custom()
.setHostnameVerifier(SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER)
.setSSLSocketFactory(sslsf)
.build();
try
HttpGet httpget = new HttpGet("https://localhost:8443/secure/index");
System.out.println("executing request" + httpget.getRequestLine());
CloseableHttpResponse response = httpclient.execute(httpget);
try
HttpEntity entity = response.getEntity();
System.out.println("----------------------------------------");
System.out.println(response.getStatusLine());
if (entity != null)
System.out.println("Response content length: " + entity.getContentLength());
EntityUtils.consume(entity);
finally
response.close();
finally
httpclient.close();
【讨论】:
【参考方案5】:如果您使用的是 Spring,则可以通过 RestTemplate 访问它:
public RestTemplate restTemplate() throws Exception
KeyStore clientStore = KeyStore.getInstance("PKCS12");
InputStream resource = this.getClass().getClassLoader().getResourceAsStream("path_to_certificate.p12");
clientStore.load(resource, "p12_password".toCharArray());
SSLContextBuilder sslContextBuilder = new SSLContextBuilder();
sslContextBuilder.setProtocol("TLS");
sslContextBuilder.loadKeyMaterial(clientStore, "p12_password".toCharArray());
sslContextBuilder.loadTrustMaterial(new TrustSelfSignedStrategy());
SSLConnectionSocketFactory sslConnectionSocketFactory = new SSLConnectionSocketFactory(sslContextBuilder.build());
CloseableHttpClient httpClient = HttpClients.custom()
.setSSLSocketFactory(sslConnectionSocketFactory)
.build();
HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory(httpClient);
return new RestTemplate(requestFactory);
【讨论】:
以上是关于使用给定的 p12 证书连接到 https 站点的主要内容,如果未能解决你的问题,请参考以下文章
连接到 HTTPS 站点时出现 SSLHandshakeException
Xamarin Android 问题通过 HTTPS 连接到具有自签名证书的站点:“未找到证书路径的信任锚。”