1

I have a working stunnel setup and am trying to use the same pem file containing the client certificate and client key and the server trust X509Certificate in a .crt file.

I consistently get this exception when it tries to write the first heartbeat packet.

javax.net.ssl.SSLHandshakeException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
        at java.base/sun.security.ssl.Alert.createSSLException(Alert.java:131)
        at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:353)
        at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:296)
        at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:291)
        at java.base/sun.security.ssl.CertificateMessage$T12CertificateConsumer.checkServerCerts(CertificateMessage.java:654)
        at java.base/sun.security.ssl.CertificateMessage$T12CertificateConsumer.onCertificate(CertificateMessage.java:473)
        at java.base/sun.security.ssl.CertificateMessage$T12CertificateConsumer.consume(CertificateMessage.java:369)
        at java.base/sun.security.ssl.SSLHandshake.consume(SSLHandshake.java:392)
        at java.base/sun.security.ssl.HandshakeContext.dispatch(HandshakeContext.java:443)
        at java.base/sun.security.ssl.SSLEngineImpl$DelegatedTask$DelegatedAction.run(SSLEngineImpl.java:1074)
        at java.base/sun.security.ssl.SSLEngineImpl$DelegatedTask$DelegatedAction.run(SSLEngineImpl.java:1061)
        at java.base/java.security.AccessController.doPrivileged(Native Method)
        at java.base/sun.security.ssl.SSLEngineImpl$DelegatedTask.run(SSLEngineImpl.java:1008)
        at tlschannel.impl.TlsChannelImpl.handleTask(TlsChannelImpl.java:232)
        at tlschannel.impl.TlsChannelImpl.handshakeLoop(TlsChannelImpl.java:570)
        at tlschannel.impl.TlsChannelImpl.writeAndHandshake(TlsChannelImpl.java:540)
        at tlschannel.impl.TlsChannelImpl.doHandshake(TlsChannelImpl.java:511)
        at tlschannel.impl.TlsChannelImpl.handshake(TlsChannelImpl.java:493)
        at tlschannel.impl.TlsChannelImpl.write(TlsChannelImpl.java:362)
        at tlschannel.ClientTlsChannel.write(ClientTlsChannel.java:164)
        at tlschannel.ClientTlsChannel.write(ClientTlsChannel.java:169)
        at tlschannel.ClientTlsChannel.write(ClientTlsChannel.java:174)

Note I am certain this is the same .crt file used by stunnel successfully.

Here is the code that is used to construct the SSLContext with trust and client certificate and key.

            // initialize the SSLContext, a configuration holder, reusable object
            SSLContext sslContext = SSLContext.getInstance("TLSv1.2");

            // Get the location of the key and cert file and read it in
            HashMap<String, List<?>> keys = this.readPem(pemPath);

            // initialize the key and trust managers
            PrivateKey key = (PrivateKey) keys.get("keys").get(0);
            List<X509Certificate> certs = (List<X509Certificate>)keys.get("certs");
            X509Certificate[] certsArray = new X509Certificate[certs.size()];
            certs.toArray(certsArray);
            // We use the pemPath as the keystore alias for this key and certs
            KeyManager[] km = createKeyStore(key, certsArray, pemPath);
            TrustManager[] tm = createTrustStore(trustPath);
            sslContext.init(km, tm, null);
            final InetSocketAddress isa = new InetSocketAddress(InetAddress.getByName(host), port);
            socketChannel = SocketChannel.open();
            socketChannel.setOption(StandardSocketOptions.TCP_NODELAY, true);
            socketChannel.connect(isa);
            socketChannel.setOption(StandardSocketOptions.SO_KEEPALIVE, true);
            socketChannel.configureBlocking(true);
            // create TlsChannel builder, combining the raw channel 
            // and the SSLEngine, using minimal options
            ClientTlsChannel.Builder builder = ClientTlsChannel.newBuilder(socketChannel, sslContext);
            tlsChannel = builder.build();

Here is the readPem method:

    private HashMap<String, List<?>> readPem(String filePath) throws FileNotFoundException, IOException, 
                                                                CertificateException, KeyStoreException, NoSuchAlgorithmException, InvalidKeySpecException {
        HashMap<String, List<?>> keys = new HashMap<String, List<?>>();
        File file = new File(filePath);
        try (Reader reader = new InputStreamReader(new FileInputStream(file), UTF_8)) {
            StringBuilder stringBuilder = new StringBuilder();

            CharBuffer buffer = CharBuffer.allocate(2048);
            while (reader.read(buffer) != -1) {
                buffer.flip();
                stringBuilder.append(buffer);
                buffer.clear();
            }

            Matcher certMatcher = CERT_PATTERN.matcher(stringBuilder);
            CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
            List<X509Certificate> certificates = new ArrayList<>();

            int start = 0;
            while (certMatcher.find(start)) {
                byte[] cert_buffer = Base64.getMimeDecoder().decode(certMatcher.group(1).getBytes(UTF_8));
                certificates.add((X509Certificate) certificateFactory.generateCertificate(new ByteArrayInputStream(cert_buffer)));
                start = certMatcher.end();
            }
            keys.put("certs", certificates);

            Matcher keyMatcher = KEY_PATTERN.matcher(stringBuilder);
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            if (!keyMatcher.find()) {
                throw new KeyStoreException("found no private key: " + keyMatcher);
            }
            byte[] encodedKey = Base64.getMimeDecoder().decode(keyMatcher.group(1).getBytes(UTF_8));
            List<PrivateKey> keyList = new ArrayList<>();
            PKCS8EncodedKeySpec eKey = new PKCS8EncodedKeySpec(encodedKey);
            PrivateKey pKey = keyFactory.generatePrivate(eKey);
            keyList.add(pKey);
            keys.put("keys", keyList);
        }
        return keys;
    }

Here are the 2 methods for creating the keystore and truststore:

    private TrustManager[] createTrustStore(String trustPath) throws KeyStoreException, 
                                            NoSuchAlgorithmException, CertificateException, 
                                            IOException {
        InputStream fis = new FileInputStream(new File(trustPath));
        KeyStore ksTrust = KeyStore.getInstance("jks");
        ksTrust.load(null);
        BufferedInputStream bis = new BufferedInputStream(fis);

        CertificateFactory cf = CertificateFactory.getInstance("X.509");

        while (bis.available() > 0) {
            X509Certificate cert = (X509Certificate) cf.generateCertificate(bis);
            ksTrust.setCertificateEntry("tradepoint"+bis.available(), cert);
        }
        
        // TrustManagers decide whether to allow connections
        TrustManagerFactory tmf = TrustManagerFactory.getInstance("PKIX");
        tmf.init(ksTrust);

        return tmf.getTrustManagers();
    }

    /**
     * Create a KeyStore from standard PEM files
     * 
     * @param key the private key from the PEM file
     * @param certs the certificate(s) from the PEM file
     * @param name the alias used in the keystore
     */
    public static KeyManager[] createKeyStore(PrivateKey key, X509Certificate[] certs, String name)
            throws Exception, KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException {
        final KeyStore keystore = KeyStore.getInstance("PKCS12");
        keystore.load(null);
        // Import private key
        char[] pwdChar = "password".toCharArray();
        keystore.setKeyEntry(name, key, pwdChar, certs);
        KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
        kmf.init(keystore, pwdChar);
        final KeyManager[] km = kmf.getKeyManagers();
        return km;
    }

Does anyone have an idea what might cause this exception given it is a correct certificate?

Olddave
  • 397
  • 1
  • 2
  • 12

0 Answers0