FULL PRODUCT VERSION :
A DESCRIPTION OF THE PROBLEM :
sun.security.provider.certpath.PKIXCertPathValidator fails certificate chain validation on CRL check using sun.security.provider.certpath.ldap.LDAPCertStore if LDAP server closes idle connection.
Please see:
http://stackoverflow.com/questions/25991400/how-to-amend-ldap-connection-properties-in-ldapcertstore-for-x509-cert-chain-val
https://stackoverflow.com/questions/8787577/how-to-reconnect-when-the-ldap-server-is-restarted
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
1. Initialize PKIXCertPathValidator with LDAPCertStore
2. Wait server to close connection (sleep required time)
3. Try to validate valid certificate chain
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
no exception
ACTUAL -
exception
ERROR MESSAGES/STACK TRACES THAT OCCUR :
java.security.cert.CertPathValidatorException: java.security.cert.CertStoreException: javax.naming.CommunicationException: connection closed [Root exception is java.io.IOException: connection closed]; remaining name ...
REPRODUCIBILITY :
This bug can be reproduced always.
---------- BEGIN SOURCE ----------
@Test
public void testRevocationListValidation() throws Exception {
String trustStoreFile = "trustStoreFilePath";
KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
InputStream is = getClass().getResourceAsStream(trustStoreFile);
if (is == null) {
throw new FileNotFoundException(String.format("KeyStore file '%s' is not found on classpath", trustStoreFile));
}
trustStore.load(is, "password".toCharArray());
Set<TrustAnchor> trustedAnchors = new HashSet<TrustAnchor>();
for (String caCertificateAlias : new String[]{"ca"}) {
X509Certificate certificate = (X509Certificate) trustStore.getCertificate(caCertificateAlias);
trustedAnchors.add(new TrustAnchor(certificate, null));
}
PKIXParameters parameters = new PKIXParameters(trustedAnchors);
CertStore certStore = CertStore.getInstance("LDAP", new LDAPCertStoreParameters("ldapHost", 389));
parameters.setCertStores(Collections.singletonList(certStore));
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(getClass().getResourceAsStream("keystore.jks"), "password".toCharArray());
String keyStoreAlias = "dev-test";
Certificate[] userCertificateChain = keyStore.getCertificateChain(keyStoreAlias);
for (int i = 0; i < 3; i++) {
System.out.println("Starting validation " + i);
CertPath userCertificatePath = CertificateFactory.getInstance("X.509").generateCertPath(Arrays.asList(userCertificateChain));
CertPathValidator.getInstance("PKIX").validate(userCertificatePath, parameters);
System.out.println("Validation " + i + " succeeded");
if (i == 1) {
System.out.println("Sleeping after second validation");
TimeUnit.SECONDS.sleep(90); // Server connection timeout ~ 60 sec
}
}
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
1. Init fresh LDAPCertStore for each validation (or catch exception and search for connection closed message and than reinit)
2. Make dummy LDAP requests with period less then idle connection timeout (but it won't help if server restarts)