package org.bouncycastle.cert.test; import java.math.BigInteger; import java.security.InvalidAlgorithmParameterException; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.NoSuchAlgorithmException; import java.security.PublicKey; import java.security.Security; import java.security.cert.CertPathBuilder; import java.security.cert.CertPathBuilderException; import java.security.cert.CertPathBuilderResult; import java.security.cert.CertStore; import java.security.cert.CertStoreParameters; import java.security.cert.CollectionCertStoreParameters; import java.security.cert.PKIXBuilderParameters; import java.security.cert.TrustAnchor; import java.security.cert.X509CRL; import java.security.cert.X509CertSelector; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Date; import java.util.GregorianCalendar; import java.util.HashSet; import java.util.List; import java.util.Set; import org.bouncycastle.asn1.x500.X500Name; import org.bouncycastle.asn1.x509.BasicConstraints; import org.bouncycastle.asn1.x509.Extension; import org.bouncycastle.asn1.x509.KeyUsage; import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; import org.bouncycastle.cert.X509CRLHolder; import org.bouncycastle.cert.X509CertificateHolder; import org.bouncycastle.cert.X509v2CRLBuilder; import org.bouncycastle.cert.X509v3CertificateBuilder; import org.bouncycastle.cert.jcajce.JcaX509CRLConverter; import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.bouncycastle.operator.ContentSigner; import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder; import org.bouncycastle.util.test.SimpleTest; /** * BC bug test case. */ public class CertPathLoopTest extends SimpleTest { /** * List of trust anchors */ private static Set<TrustAnchor> taSet; /** * List of certificates and CRLs */ private static List<Object> otherList; /** * Asks the user about the configuration he want's to test * * @param caA * @param caB */ private static void checkUseDistinctCAs(CA caA, CA caB) { //Standard configuration : everything in caA taSet = new HashSet<TrustAnchor>(); taSet.add(caA.ta); otherList = new ArrayList<Object>(); otherList.add(caA.acCertCrl); otherList.add(caA.crl); //User specified configuration : parts of caB taSet.add(caB.ta); otherList.add(caB.acCertCrl); otherList.add(caB.crl); } /** * Creates a collection cert store */ static CertStore getStore(Collection col) throws InvalidAlgorithmParameterException, NoSuchAlgorithmException { CertStoreParameters csp = new CollectionCertStoreParameters(col); return CertStore.getInstance("Collection", csp); } public String getName() { return "CertPath Loop Test"; } public void performTest() throws Exception { //Add the provider Security.addProvider(new BouncyCastleProvider()); //Generate two Cert authorities CA caA = new CA(); CA caB = new CA(); //Ask the user the conf he want's to test checkUseDistinctCAs(caA, caB); //Let's create a target cert under caA X509CertSelector target = new X509CertSelector(); target.setCertificate(caA.makeNewCert()); //create control parameters PKIXBuilderParameters params = new PKIXBuilderParameters(taSet, target); params.addCertStore(getStore(Collections.singleton(target.getCertificate()))); params.addCertStore(getStore(otherList)); //enable revocation check params.setRevocationEnabled(true); //Lets Build the path try { CertPathBuilderResult cpbr = CertPathBuilder.getInstance("PKIX", "BC").build(params); fail("invalid path build"); } catch (CertPathBuilderException e) { if (!e.getCause().getMessage().equals("CertPath for CRL signer failed to validate.")) { fail("Exception thrown, but wrong one", e.getCause()); } } } /** * Class simulating a certification authority */ private static class CA { /** * key pair generator */ final static KeyPairGenerator kpg; static { try { kpg = KeyPairGenerator.getInstance("RSA"); //Key size doesn't matter, smaller == Faster kpg.initialize(512); } catch (NoSuchAlgorithmException e) { throw new RuntimeException(e); } } /** * KeyPair signing certificates */ private KeyPair caCertKp; /** * KeyPair signing CRLs */ private KeyPair caCrlKp; TrustAnchor ta; /** * Subject of this CA */ X500Name acSubject; /** * Certificate signing certificates */ X509Certificate acCertAc; /** * Certificate signing CRLs */ X509Certificate acCertCrl; /** * the CRL */ X509CRL crl; /** * Signers */ private ContentSigner caCrlSigner, caCertSigner; /** * Serial number counter */ private int counter = 1; /** * Constructor */ public CA() throws Exception { //Init both keypairs caCertKp = kpg.generateKeyPair(); caCrlKp = kpg.generateKeyPair(); //subject acSubject = new X500Name("CN=AC_0"); //validity GregorianCalendar gc = new GregorianCalendar(); Date notBefore = gc.getTime(); gc.add(GregorianCalendar.DAY_OF_YEAR, 1); Date notAfter = gc.getTime(); //first signer caCertSigner = new JcaContentSignerBuilder("SHA1withRSA").build(caCertKp.getPrivate()); //top level : issuer is self X500Name issuer = acSubject; //reserved for future use (another test case) ContentSigner thisAcSigner = caCertSigner; //reserved for future use (another test case) //First certificate: Certificate authority (BasicConstraints=true) but not CRLSigner X509CertificateHolder certH = new X509v3CertificateBuilder( issuer, BigInteger.valueOf(counter++), notBefore, notAfter, acSubject, getPublicKeyInfo(caCertKp.getPublic())) .addExtension(Extension.basicConstraints, true, new BasicConstraints(true)) .addExtension(Extension.keyUsage, true, new KeyUsage(KeyUsage.keyCertSign)) .build(thisAcSigner); //lets convert to X509Certificate acCertAc = convert(certH); //and build a trust Anchor ta = new TrustAnchor(acCertAc, null); //Second signer caCrlSigner = new JcaContentSignerBuilder("SHA1withRSA").build(caCrlKp.getPrivate()); //second certificate: CRLSigner but not Certificate authority (BasicConstraints=false) certH = new X509v3CertificateBuilder( issuer, BigInteger.valueOf(counter++), notBefore, notAfter, acSubject, getPublicKeyInfo(caCrlKp.getPublic())) .addExtension(Extension.basicConstraints, false, new BasicConstraints(false)) .addExtension(Extension.keyUsage, true, new KeyUsage(KeyUsage.cRLSign)) .build(thisAcSigner); //lets convert to X509Certificate acCertCrl = convert(certH); //And create the CRL X509CRLHolder crlH = new X509v2CRLBuilder(acSubject, notBefore).setNextUpdate(notAfter).build(caCrlSigner); //lets convert to X509CRL crl = convert(crlH); } /** * Creates a child certificate */ public X509Certificate makeNewCert() throws Exception { //private key doesn't matter for the test PublicKey publicKey = kpg.generateKeyPair().getPublic(); //Validity GregorianCalendar gc = new GregorianCalendar(); Date notBefore = gc.getTime(); gc.add(GregorianCalendar.DAY_OF_YEAR, 1); Date notAfter = gc.getTime(); //serial BigInteger certSerial = BigInteger.valueOf(counter++); //Distinct name based on the serial X500Name subject = new X500Name("CN=EU_" + certSerial.toString()); //End user certificate, not allowed to do anything X509CertificateHolder enUserCertH = new X509v3CertificateBuilder( acSubject, certSerial, notBefore, notAfter, subject, getPublicKeyInfo(publicKey)) .addExtension(Extension.basicConstraints, false, new BasicConstraints(false)) .addExtension(Extension.keyUsage, true, new KeyUsage(0)) .build(caCertSigner); //lets convert to X509Certificate return convert(enUserCertH); } /** * convert to X509Certificate */ static X509Certificate convert(X509CertificateHolder h) throws Exception { return new JcaX509CertificateConverter().getCertificate(h); } /** * convert to X509CRL */ static X509CRL convert(X509CRLHolder h) throws Exception { return new JcaX509CRLConverter().getCRL(h); } /** * convert to SubjectPublicKeyInfo */ static SubjectPublicKeyInfo getPublicKeyInfo(PublicKey k) throws Exception { return SubjectPublicKeyInfo.getInstance(k.getEncoded()); } } public static void main(String[] args) { runTest(new CertPathLoopTest()); } }