Java SSL - InstallCert 识别证书,但仍然“无法找到有效的证书路径”错误?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/11087121/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Java SSL - InstallCert recognizes certificate, but still "unable to find valid certification path" error?
提问by Bill
Thinking I'd hit the same issue as other folks, I've been going through the numerous similar problems and potential solutions, but with no luck.
认为我会遇到与其他人相同的问题,我已经经历了许多类似的问题和潜在的解决方案,但没有运气。
The trust store I'm using is cacerts, located in lib/security of a Java 1.6.0 JRE (build 1.6.0_20-b02... could this be the root of the problem?). I've also tried with jssecacerts.
我使用的信任库是 cacerts,位于 Java 1.6.0 JRE 的 lib/security 中(构建 1.6.0_20-b02 ......这可能是问题的根源吗?)。我也尝试过 jssecacerts。
Using InstallCert (per other similar issues posted), I can see my certificate is in fact installed and valid (and I've removed it, re-imported it, etc to make sure I'm seeing the right data):
使用 InstallCert(根据发布的其他类似问题),我可以看到我的证书实际上已安装且有效(并且我已将其删除、重新导入等以确保我看到的是正确的数据):
java InstallCert <my host name>
Loading KeyStore jssecacerts...
Opening connection to <my host name>:443...
Starting SSL handshake...
No errors, certificate is already trusted
Checking in keytool and Portecle, re-importing the cert (I've tried generating from openssl with -showcert, exporting from browsers and scp'ing it over, etc) gives me "That already exists under this other alias over here" type of message. So there doesn't appear to be any issue with the way the cert is getting into the tool(s).
检查 keytool 和 Portecle,重新导入证书(我尝试使用 -showcert 从 openssl 生成,从浏览器导出并对其进行 scp 等)给了我“这已经存在于这里的另一个别名下”类型的信息。因此,证书进入工具的方式似乎没有任何问题。
Forcing explicit trustStore paths in the code doesn't make any difference, and in all cases what I end up seeing when I turn on debugging (via a setProperty of javax.net.debug to "all") is:
在代码中强制使用显式 trustStore 路径没有任何区别,在所有情况下,当我打开调试(通过 javax.net.debug 的 setProperty 为“all”)时,我最终看到的是:
main, SEND TLSv1 ALERT: fatal, description = certificate_unknown
main, WRITE: TLSv1 Alert, length = 2 [Raw write]: length = 7 0000: 15
03 01 00 02 02 2E ....... main, called
closeSocket() main, handling exception:
javax.net.ssl.SSLHandshakeException:
sun.security.validator.ValidatorException: PKIX path building failed:
sun.security.provider.certpath.SunCertPathBuilderException: unable to
find valid certification path to requested target
Unfortunately I can't allow overriding the check by implementing my own TrustManager - it has to actually check.
不幸的是,我不能允许通过实现我自己的 TrustManager 来覆盖检查 - 它必须实际检查。
The certificate I get from the host has a number of extensions (9, to be exact), which makes me wonder if they're somehow part of this issue.
我从主机获得的证书有许多扩展名(准确地说是 9 个),这让我想知道它们是否是这个问题的一部分。
What else can I check/try? Change over to a different JRE version?
我还能检查/尝试什么?切换到不同的 JRE 版本?
采纳答案by Vivin Paliath
You can still check the certificate by implementing your own trust manager. I ran into a similar issue here. I also tried adding the certificate to cacerts
but to no avail.
您仍然可以通过实现自己的信任管理器来检查证书。我在这里遇到了类似的问题。我也尝试将证书添加到cacerts
但无济于事。
In your trust manager, you need to explicitly load up the certificates. Essentially what I had to do was something like this:
在您的信任管理器中,您需要明确加载证书。基本上我必须做的是这样的:
First I create a trust manager that uses the actual certificate files:
首先,我创建一个使用实际证书文件的信任管理器:
public class ValicertX509TrustManager implements X509TrustManager {
X509TrustManager pkixTrustManager;
ValicertX509TrustManager() throws Exception {
String valicertFile = "/certificates/ValicertRSAPublicRootCAv1.cer";
String commwebDRFile = "/certificates/DR_10570.migs.mastercard.com.au.crt";
String commwebPRODFile = "/certificates/PROD_10549.migs.mastercard.com.au.new.crt";
Certificate valicert = CertificateFactory.getInstance("X509").generateCertificate(this.getClass().getResourceAsStream(valicertFile));
Certificate commwebDR = CertificateFactory.getInstance("X509").generateCertificate(this.getClass().getResourceAsStream(commwebDRFile));
Certificate commwebPROD = CertificateFactory.getInstance("X509").generateCertificate(this.getClass().getResourceAsStream(commwebPRODFile));
KeyStore keyStore = KeyStore.getInstance("JKS");
keyStore.load(null, "".toCharArray());
keyStore.setCertificateEntry("valicert", valicert);
keyStore.setCertificateEntry("commwebDR", commwebDR);
keyStore.setCertificateEntry("commwebPROD", commwebPROD);
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance("PKIX");
trustManagerFactory.init(keyStore);
TrustManager trustManagers[] = trustManagerFactory.getTrustManagers();
for(TrustManager trustManager : trustManagers) {
if(trustManager instanceof X509TrustManager) {
pkixTrustManager = (X509TrustManager) trustManager;
return;
}
}
throw new Exception("Couldn't initialize");
}
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
pkixTrustManager.checkServerTrusted(chain, authType);
}
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
pkixTrustManager.checkServerTrusted(chain, authType);
}
public X509Certificate[] getAcceptedIssuers() {
return pkixTrustManager.getAcceptedIssuers();
}
}
Now, using this trust manager, I had to create a socket factory:
现在,使用这个信任管理器,我必须创建一个套接字工厂:
public class ValicertSSLProtocolSocketFactory implements ProtocolSocketFactory {
private SSLContext sslContext = null;
public ValicertSSLProtocolSocketFactory() {
super();
}
private static SSLContext createValicertSSLContext() {
try {
ValicertX509TrustManager valicertX509TrustManager = new ValicertX509TrustManager();
SSLContext context = SSLContext.getInstance("TLS");
context.init(null, new ValicertX509TrustManager[] { valicertX509TrustManager}, null);
return context;
}
catch(Exception e) {
Log.error(Log.Context.Net, e);
return null;
}
}
private SSLContext getSSLContext() {
if(this.sslContext == null) {
this.sslContext = createValicertSSLContext();
}
return this.sslContext;
}
public Socket createSocket(String host, int port, InetAddress clientHost, int clientPort) throws IOException {
return getSSLContext().getSocketFactory().createSocket(host, port, clientHost, clientPort);
}
public Socket createSocket(final String host, final int port, final InetAddress localAddress, final int localPort, final HttpConnectionParams params) throws IOException {
if(params == null) {
throw new IllegalArgumentException("Parameters may not be null");
}
int timeout = params.getConnectionTimeout();
SocketFactory socketFactory = getSSLContext().getSocketFactory();
if(timeout == 0) {
return socketFactory.createSocket(host, port, localAddress, localPort);
}
else {
Socket socket = socketFactory.createSocket();
SocketAddress localAddr = new InetSocketAddress(localAddress, localPort);
SocketAddress remoteAddr = new InetSocketAddress(host, port);
socket.bind(localAddr);
socket.connect(remoteAddr, timeout);
return socket;
}
}
public Socket createSocket(String host, int port) throws IOException {
return getSSLContext().getSocketFactory().createSocket(host, port);
}
public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException {
return getSSLContext().getSocketFactory().createSocket(socket, host, port, autoClose);
}
public boolean equals(Object obj) {
return ((obj != null) && obj.getClass().equals(ValicertSSLProtocolSocketFactory.class));
}
public int hashCode() {
return ValicertSSLProtocolSocketFactory.class.hashCode();
}
}
Then I just registered a new protocol:
然后我刚刚注册了一个新协议:
Protocol.registerProtocol("vhttps", new Protocol("vhttps", new ValicertSSLProtocolSocketFactory(), 443));
PostMethod postMethod = new PostMethod(url);
for (Map.Entry<String, String> entry : params.entrySet()) {
postMethod.addParameter(entry.getKey(), StringUtils.Nz(entry.getValue()));
}
HttpClient client = new HttpClient();
int status = client.executeMethod(postMethod);
if (status == 200) {
StringBuilder resultBuffer = new StringBuilder();
resultBuffer.append(postMethod.getResponseBodyAsString());
return new HttpResponse(resultBuffer.toString(), "");
} else {
throw new IOException("Invalid response code: " + status);
}
The only disadvantage is that I had to create a specific protocol (vhttps
) for this particular certificate.
唯一的缺点是我必须vhttps
为这个特定的证书创建一个特定的协议 ( )。
回答by emboss
My guess is either of these things happened:
我的猜测是发生了以下两种情况:
a) You run your code on a web server. They often use their own trust store - so are you really sure that it's cacerts
that's being used when your code is executed?
a) 您在 Web 服务器上运行您的代码。他们经常使用他们自己的信任库——所以你真的确定cacerts
在你的代码执行时它正在被使用吗?
b) By default, Java will try to check the validity of the certificates by downloading and interpreting CRLs. If you are behind a proxy, the download fails, and as a consequence the whole PKIX check would fail.
b) 默认情况下,Java 将尝试通过下载和解释 CRL 来检查证书的有效性。如果您在代理后面,下载将失败,因此整个 PKIX 检查将失败。
回答by user207421
The SSL debug trace will show which cacerts file you are using, as long as you don't manually load it yourself. Clearly you aren't using the one you think you are.
SSL 调试跟踪将显示您正在使用哪个 cacerts 文件,只要您不自己手动加载它。很明显,您使用的并不是您认为的那种。