FULL PRODUCT VERSION :
java version "1.6.0_06"
Java(TM) SE Runtime Environment (build 1.6.0_06-b02)
Java HotSpot(TM) Client VM (build 10.0-b22, mixed mode, sharing)
ADDITIONAL OS VERSION INFORMATION :
Microsoft Windows XP [Version 5.1.2600]
A DESCRIPTION OF THE PROBLEM :
When the CertPathBuilder is requested to create a CertPath leading to an End Entity certificate (specified by BasicConstraints extension), it fails to include the actual End Entity certificate in the chain.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
See the code sample.
REPRODUCIBILITY :
This bug can be reproduced always.
---------- BEGIN SOURCE ----------
import java.math.BigInteger;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.Provider;
import java.security.PublicKey;
import java.security.Security;
import java.security.cert.CertPathBuilder;
import java.security.cert.CertStore;
import java.security.cert.CollectionCertStoreParameters;
import java.security.cert.PKIXBuilderParameters;
import java.security.cert.PKIXCertPathBuilderResult;
import java.security.cert.TrustAnchor;
import java.security.cert.X509CertSelector;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import javax.security.auth.x500.X500Principal;
import org.bouncycastle.asn1.x509.BasicConstraints;
import org.bouncycastle.asn1.x509.X509Extensions;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.x509.X509V3CertificateGenerator;
import sun.security.provider.Sun;
public class PKIXBugReport {
static Provider bcProvider = new BouncyCastleProvider();
static Provider sunProvider = new Sun();
static {
Security.addProvider(bcProvider);
Security.addProvider(sunProvider);
}
static KeyPairGenerator kg;
public static void main(String[] args) throws Exception {
kg = KeyPairGenerator.getInstance("RSA", bcProvider);
KeyPair keyPair = kg.generateKeyPair();
PrivateKey taSigningKey = keyPair.getPrivate();
X509Certificate ta = makeCert("TrustAnchor", null, keyPair.getPublic(), taSigningKey, true);
keyPair = kg.generateKeyPair();
PrivateKey caSigningKey = keyPair.getPrivate();
X509Certificate ca = makeCert("CA", ta, keyPair.getPublic(), taSigningKey, true);
keyPair = kg.generateKeyPair();
X509Certificate endEntity = makeCert("EndEntity", ca, keyPair.getPublic(), caSigningKey, false);
CertStore certStore;
{
ArrayList<X509Certificate> certs = new ArrayList<X509Certificate>(2);
certs.add(ca);
certs.add(endEntity);
CollectionCertStoreParameters certStoreParams = new CollectionCertStoreParameters(certs);
// doesn't make a difference
//certStore = CertStore.getInstance("Collection", certStoreParams, bcProvider);
certStore = CertStore.getInstance("Collection", certStoreParams, sunProvider);
}
X509CertSelector certSelector = new X509CertSelector();
certSelector.setBasicConstraints(-2); // end entity only
PKIXBuilderParameters builderParams = new PKIXBuilderParameters(
Collections.singleton(new TrustAnchor(ta, null)), certSelector);
builderParams.setRevocationEnabled(false);
builderParams.addCertStore(certStore);
{
CertPathBuilder builder = CertPathBuilder.getInstance("PKIX", bcProvider);
PKIXCertPathBuilderResult buildResult = (PKIXCertPathBuilderResult) builder.build(builderParams);
// correctly prints cert path length = 2
System.out.println(buildResult.getCertPath());
}
{
CertPathBuilder builder = CertPathBuilder.getInstance("PKIX", sunProvider);
PKIXCertPathBuilderResult buildResult = (PKIXCertPathBuilderResult) builder.build(builderParams);
// incorrectly prints cert path length = 1
System.out.println(buildResult.getCertPath());
}
}
static long serial = 1L;
private static X509Certificate makeCert(
String name, X509Certificate parent,
PublicKey certKey, PrivateKey signingKey, boolean isCA) throws Exception
{
X509V3CertificateGenerator cg = new X509V3CertificateGenerator();
X500Principal subjectDN = new X500Principal("CN=" + name);
cg.setSubjectDN(subjectDN);
if(parent == null) {
cg.setIssuerDN(subjectDN);
} else {
cg.setIssuerDN(parent.getSubjectX500Principal());
}
cg.setPublicKey(certKey);
Date notBefore = new Date();
cg.setNotBefore(notBefore);
Calendar calendar = Calendar.getInstance();
calendar.setTime(notBefore);
calendar.add(Calendar.YEAR, 1);
cg.setNotAfter(calendar.getTime());
cg.setSerialNumber(BigInteger.valueOf(serial++));
cg.setSignatureAlgorithm("SHA1WithRSA");
cg.addExtension(X509Extensions.BasicConstraints, true, new BasicConstraints(isCA));
X509Certificate cert = cg.generate(signingKey, bcProvider.getName());
return cert;
}
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
Use BouncyCastle implementation of CertPathBuilder, as per the code sample.