PKIX path building failed: (IBMCertPathBuilderException) unable to find valid certification path to requested target
❗ please do not add sensitive information in issues, you can provide extra information via email using issue number as reference ❗
Describe the issue At random, the HttpSender runs into the following error, whilst the certificate is present in the store:
PKIX path building failed: (IBMCertPathBuilderException) unable to find valid certification path to requested target
The certificate is 100% present, here is a screenshot from WAS:

Once again verified with a custom pipe to list all certs:

I decided to create a custom component which should send HTTPS in the most basic form possible. Since last evening I am running a test where every minute I test three implementations:
- Basic HttpSender (name of job = Test connection login.microsoftonline.com proxy)
- Extended HttpSenderBase as plugin for MSAL (name of job = receivemails) see #3368
- Custom HTTPS component (name of job = Test connection SSL)
Their results:
-
Basic HttpSender

-
Extended HttpSenderBase

-
Custom HTTPS

Ladybug logging show all jobs, as you can see the HTTP sender based job does 'restore' it self after a bunch of errors.

Confirmed by test a pipeline that the basic HTTPS version kept working whilst the HttpSender broke:

Sender configuration:
<pipe className="nl.nn.adapterframework.pipes.SenderPipe" name="Test">
<sender authAlias="some-alias" className="nl.nn.adapterframework.http.HttpSender" methodType="GET" name="test" proxyAuthAlias="some-alias" proxyHost="some-host" proxyPort="some-port">
<param name="url" sessionKey="originalMessage"/>
</sender>
</pipe>
Custom list certificates pipe (quick n dirty)
package nl.nn.adapterframework.pipes;
import lombok.Getter;
import lombok.Setter;
import nl.nn.adapterframework.core.PipeLineSession;
import nl.nn.adapterframework.core.PipeRunException;
import nl.nn.adapterframework.core.PipeRunResult;
import nl.nn.adapterframework.stream.Message;
import nl.nn.adapterframework.xml.SaxElementBuilder;
import java.io.*;
import java.security.KeyStore;
import java.security.cert.Certificate;
import java.util.Enumeration;
public class CertificatePipe extends FixedForwardPipe {
private @Getter @Setter String password;
private @Getter @Setter String location;
@Override
public PipeRunResult doPipe(Message message, PipeLineSession pipeLineSession) throws PipeRunException {
InputStream keystoreLocation = null;
try {
keystoreLocation = getLocationAsInputStream();
KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType());
keystore.load(keystoreLocation, getPassword().toCharArray());
Enumeration<String> enumeration = keystore.aliases();
SaxElementBuilder certificates = new SaxElementBuilder("certificates");
while(enumeration.hasMoreElements()) {
String alias = enumeration.nextElement();
if(log.isDebugEnabled()) log.debug("alias name: " + alias);
Certificate certificate = keystore.getCertificate(alias);
if(log.isDebugEnabled()) log.debug(certificate.toString());
certificates.addElement("certificate", "name", alias, certificate.toString());
}
Message result = new Message(certificates.toString());
return new PipeRunResult(getSuccessForward(), result);
} catch (Exception e) {
throw new PipeRunException(this, "Could not list certificates because of exception", e);
} finally {
if(keystoreLocation != null){
try {
keystoreLocation.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
private InputStream getLocationAsInputStream() throws FileNotFoundException {
File file = new File(getLocation());
return new FileInputStream(file);
}
}
Custom HTTPS impl (quick n dirty)
package nl.nn.adapterframework.pipes;
import lombok.Getter;
import lombok.Setter;
import nl.nn.adapterframework.core.PipeLineSession;
import nl.nn.adapterframework.core.PipeRunException;
import nl.nn.adapterframework.core.PipeRunResult;
import nl.nn.adapterframework.stream.Message;
import nl.nn.adapterframework.util.CredentialFactory;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpHost;
import org.apache.http.HttpResponse;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.Credentials;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.SSLContexts;
import javax.net.ssl.SSLContext;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.security.KeyStore;
public class MSALPipe extends FixedForwardPipe {
private @Getter @Setter String password;
private @Getter @Setter String location;
private @Getter @Setter String proxyHost;
private @Getter @Setter String proxyAuthAlias;
private @Getter @Setter String proxyUsername;
private @Getter @Setter String proxyPassword;
private @Getter @Setter String authAlias;
private @Getter @Setter int proxyPort;
@Override
public PipeRunResult doPipe(Message message, PipeLineSession pipeLineSession) throws PipeRunException {
Message result = null;
try {
KeyStore keyStore = getKeyStore();
SSLContext sslContext = SSLContexts.custom()
.loadKeyMaterial(keyStore, getPassword().toCharArray())
.build();
CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
CredentialFactory auth = new CredentialFactory(getAuthAlias(), null, getPassword());
credentialsProvider.setCredentials(new AuthScope(AuthScope.ANY_HOST, AuthScope.ANY_PORT), new UsernamePasswordCredentials(auth.getUsername(), auth.getPassword()) );
CredentialFactory pcf = new CredentialFactory(getProxyAuthAlias(), getProxyUsername(), getProxyPassword());
HttpHost proxy = new HttpHost(getProxyHost(), getProxyPort());
AuthScope scope = new AuthScope(proxy, null, AuthScope.ANY_SCHEME);
if (StringUtils.isNotEmpty(pcf.getUsername())) {
Credentials credentials = new UsernamePasswordCredentials(pcf.getUsername(), pcf.getPassword());
credentialsProvider.setCredentials(scope, credentials);
}
HttpClient httpClient = HttpClients.custom().setSSLContext(sslContext).setProxy(proxy).setDefaultCredentialsProvider(credentialsProvider).build();
HttpRequestBase requestBase = new HttpGet(message.asString());
HttpResponse response = httpClient.execute(requestBase);
result = new Message(response.getEntity().getContent());
return new PipeRunResult(getSuccessForward(), result);
} catch (Exception e){
throw new PipeRunException(this, "Could not connect to microsoft!", e);
}
}
private KeyStore getKeyStore() throws PipeRunException {
InputStream keystoreLocation = null;
try {
keystoreLocation = getLocationAsInputStream();
KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType());
keystore.load(keystoreLocation, getPassword().toCharArray());
return keystore;
} catch (Exception e){
throw new PipeRunException(this, "Could not get keystore", e);
}
}
private InputStream getLocationAsInputStream() throws FileNotFoundException {
File file = new File(getLocation());
return new FileInputStream(file);
}
}
Reporter Laurens
Environment
FF! 7.7: Ibis4Input
Running on XX.XXXX. using IBM WebSphere Application Server/9.0.5.10
Java Version: Java(TM) SE Runtime Environment (8.0.7.0 - pxa6480sr7-20211025_01(SR7))
Heap size: 1157M, total JVM memory: 4096M
Free disk space: 9GB, total disk space: 12GB
Up since: 2022-06-16 23:20:08 (15h)
After the weekend:
HttpSender:

Simple impl:

Extended httpSender (MSAL):

Another pic sharing results, in 2 days it happened only 20 times...

Only 1% of the time this happened the past few days, where as the simple impl has a 0% error rate.
Its very interesting to observe that the connection was working, after reloading the config, it broke:

I can imagine that the connection to the login server is less reliable than to the 'production' server. It would be good to run the application or the tests with custom property javax.net.debug=true. This allows to analyse where in the certificate chain the problem really occurs.
I'm closing this issue as it's been stale for a while now.