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?