Java SSL - InstallCert recognizes a certificate but still cannot find the correct certification path?

I think that I faced the same problem as other people, I came across many similar problems and potential solutions, but no luck.

The trust store I use is cacerts located in the lib / security Java 1.6.0 JRE (build 1.6.0_20-b02 ... could this be the cause of the problem?). I also tried using jssecacerts.

Using InstallCert (for other similar releases), I see that my certificate is actually installed and valid (and I deleted it, re-imported it, etc., to make sure that I see the correct data):

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 certificate (I tried to generate from openssl with -showcert, export from browsers and scp'ing it, etc.) gives me the "This already exists under this other alias here" message type. Thus, there is no problem with how the certificate gets into the tool (s).

Forcing explicit trustStore paths in the code does not matter, and in all cases what I end up seeing when I turn on debugging (via setProperty from javax.net.debug for "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 cannot afford to override the validation by running my own TrustManager - it should really validate.

The certificate that I receive from the host has several extensions (more precisely, 9), which makes me wonder if they are part of this problem.

What else can I check / try? Upgrade to a different version of JRE?

+6
source share
3 answers

You can still verify the certificate by running your own trust manager. I ran into a similar problem here . I also tried adding a certificate to cacerts , but to no avail.

In your trusted manager, you need to explicitly upload the certificates. Essentially, I needed 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 factory socket:

 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 drawback is that I had to create a specific protocol ( vhttps ) for this particular certificate.

0
source

My hunch about what happened:

a) You run your code on a web server. They often use their own trust store - so are you really sure that it cacerts used when executing your code?

b) By default, Java will attempt to validate certificates by loading and interpreting CRLs. If you are behind a proxy server, the download will fail, and as a result, the entire PKIX check will fail.

0
source

An SSL debug trace will show which cacerts file you are using if you do not manually download it yourself. Obviously, you are not using the one you think.

0
source

Source: https://habr.com/ru/post/918382/


All Articles