JDK-6714842 : CertPathBuilder returns incorrect CertPath for BasicConstraints in builderParams
  • Type: Bug
  • Component: security-libs
  • Sub-Component: java.security
  • Affected Version: 6
  • Priority: P4
  • Status: Closed
  • Resolution: Fixed
  • OS: windows_xp
  • CPU: x86
  • Submitted: 2008-06-16
  • Updated: 2011-03-07
  • Resolved: 2011-03-07
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 6 JDK 7
6u10Fixed 7 b31Fixed
Description
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.

Comments
EVALUATION Yes, this is a bug. The problem was that the implementation was ignoring the basic constraints selector parameter and always considering CA certificates as a potential target certificate.
19-06-2008