package org.bouncycastle.jce.provider.test;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.math.BigInteger;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.Principal;
import java.security.Provider;
import java.security.PublicKey;
import java.security.Security;
import java.security.Signature;
import java.security.SignatureException;
import java.security.cert.CertPath;
import java.security.cert.CertPathValidator;
import java.security.cert.CertPathValidatorException;
import java.security.cert.CertStore;
import java.security.cert.CertStoreParameters;
import java.security.cert.Certificate;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;
import java.security.cert.CertificateExpiredException;
import java.security.cert.CertificateFactory;
import java.security.cert.CertificateNotYetValidException;
import java.security.cert.CertificateParsingException;
import java.security.cert.CollectionCertStoreParameters;
import java.security.cert.PKIXBuilderParameters;
import java.security.cert.PKIXCertPathChecker;
import java.security.cert.PKIXCertPathValidatorResult;
import java.security.cert.PKIXParameters;
import java.security.cert.PolicyNode;
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.Enumeration;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.List;
import java.util.Set;
import java.util.Vector;

import javax.security.auth.x500.X500Principal;

import org.bouncycastle.asn1.ASN1BitString;
import org.bouncycastle.asn1.ASN1Encodable;
import org.bouncycastle.asn1.ASN1EncodableVector;
import org.bouncycastle.asn1.ASN1Encoding;
import org.bouncycastle.asn1.ASN1IA5String;
import org.bouncycastle.asn1.ASN1InputStream;
import org.bouncycastle.asn1.ASN1Integer;
import org.bouncycastle.asn1.ASN1Object;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.ASN1Primitive;
import org.bouncycastle.asn1.ASN1Sequence;
import org.bouncycastle.asn1.ASN1String;
import org.bouncycastle.asn1.ASN1TaggedObject;
import org.bouncycastle.asn1.DERNull;
import org.bouncycastle.asn1.DEROctetString;
import org.bouncycastle.asn1.DERSequence;
import org.bouncycastle.asn1.DERTaggedObject;
import org.bouncycastle.asn1.util.ASN1Dump;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x500.style.RFC4519Style;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.asn1.x509.BasicConstraints;
import org.bouncycastle.asn1.x509.Extension;
import org.bouncycastle.asn1.x509.GeneralName;
import org.bouncycastle.asn1.x509.KeyUsage;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.asn1.x509.Time;
import org.bouncycastle.internal.asn1.misc.MiscObjectIdentifiers;
import org.bouncycastle.internal.asn1.misc.NetscapeCertType;
import org.bouncycastle.internal.asn1.misc.NetscapeRevocationURL;
import org.bouncycastle.internal.asn1.misc.VerisignCzagExtension;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.util.Arrays;
import org.bouncycastle.util.Integers;
import org.bouncycastle.util.Properties;
import org.bouncycastle.util.Strings;
import org.bouncycastle.util.encoders.Base64;
import org.bouncycastle.util.encoders.Hex;
import org.bouncycastle.util.test.SimpleTest;

public class CertPathValidatorTest
    extends SimpleTest
{
    private byte[] AC_PR = Base64.decode(
        "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tDQpNSUlFU1RDQ0F6R2dBd0lC"
            + "QWdJQkJUQU5CZ2txaGtpRzl3MEJBUVVGQURDQnRERUxNQWtHQTFVRUJoTUNR"
            + "bEl4DQpFekFSQmdOVkJBb1RDa2xEVUMxQ2NtRnphV3d4UFRBN0JnTlZCQXNU"
            + "TkVsdWMzUnBkSFYwYnlCT1lXTnBiMjVoDQpiQ0JrWlNCVVpXTnViMnh2WjJs"
            + "aElHUmhJRWx1Wm05eWJXRmpZVzhnTFNCSlZFa3hFVEFQQmdOVkJBY1RDRUp5"
            + "DQpZWE5wYkdsaE1Rc3dDUVlEVlFRSUV3SkVSakV4TUM4R0ExVUVBeE1vUVhW"
            + "MGIzSnBaR0ZrWlNCRFpYSjBhV1pwDQpZMkZrYjNKaElGSmhhWG9nUW5KaGMy"
            + "bHNaV2x5WVRBZUZ3MHdNakEwTURReE9UTTVNREJhRncwd05UQTBNRFF5DQpN"
            + "elU1TURCYU1HRXhDekFKQmdOVkJBWVRBa0pTTVJNd0VRWURWUVFLRXdwSlEx"
            + "QXRRbkpoYzJsc01UMHdPd1lEDQpWUVFERXpSQmRYUnZjbWxrWVdSbElFTmxj"
            + "blJwWm1sallXUnZjbUVnWkdFZ1VISmxjMmxrWlc1amFXRWdaR0VnDQpVbVZ3"
            + "ZFdKc2FXTmhNSUlCSWpBTkJna3Foa2lHOXcwQkFRRUZBQU9DQVE4QU1JSUJD"
            + "Z0tDQVFFQXMwc0t5NGsrDQp6b016aldyMTQxeTVYQ045UGJMZERFQXN2cjZ4"
            + "Z0NCN1l5bEhIQ1NBYmpGR3dOQ0R5NlVxN1h0VjZ6UHdIMXpGDQpFWENlS3Jm"
            + "UUl5YXBXSEZ4V1VKajBMblFrY1RZM1FOR1huK0JuVk9EVTZDV3M1c3NoZktH"
            + "RXZyVlQ1Z214V1NmDQp4OFlsdDgzY1dwUE1QZzg3VDlCaHVIbHQzazh2M2Ev"
            + "NmRPbmF2dytOYTAyZExBaDBlNzZqcCtQUS9LK0pHZlBuDQphQjVVWURrZkd0"
            + "em5uTTNBV01tY3VJK0o0ek5OMDZaa3ZnbDFsdEo2UU1qcnZEUFlSak9ndDlT"
            + "cklpY1NmbEo4DQptVDdHWGRRaXJnQUNXc3g1QURBSklRK253TU1vNHlyTUtx"
            + "SlFhNFFDMHhhT0QvdkdVcG9SaDQzT0FTZFp3c3YvDQpPWFlybmVJeVAwVCs4"
            + "UUlEQVFBQm80RzNNSUcwTUQwR0ExVWRId1EyTURRd01xQXdvQzZHTEdoMGRI"
            + "QTZMeTloDQpZM0poYVhvdWFXTndZbkpoYzJsc0xtZHZkaTVpY2k5TVExSmhZ"
            + "M0poYVhvdVkzSnNNQklHQTFVZElBUUxNQWt3DQpCd1lGWUV3QkFRRXdIUVlE"
            + "VlIwT0JCWUVGREpUVFlKNE9TWVB5T09KZkVMZXhDaHppK2hiTUI4R0ExVWRJ"
            + "d1FZDQpNQmFBRklyNjhWZUVFUk0xa0VMNlYwbFVhUTJreFBBM01BNEdBMVVk"
            + "RHdFQi93UUVBd0lCQmpBUEJnTlZIUk1CDQpBZjhFQlRBREFRSC9NQTBHQ1Nx"
            + "R1NJYjNEUUVCQlFVQUE0SUJBUUJRUFNoZ1lidnFjaWV2SDVVb3ZMeXhkbkYr"
            + "DQpFcjlOeXF1SWNkMnZ3Y0N1SnpKMkQ3WDBUcWhHQ0JmUEpVVkdBVWorS0NP"
            + "SDFCVkgva1l1OUhsVHB1MGtKWFBwDQpBQlZkb2hJUERqRHhkbjhXcFFSL0Yr"
            + "ejFDaWtVcldIMDR4eTd1N1p6UUpLSlBuR0loY1FpOElyRm1PYkllMEc3DQpY"
            + "WTZPTjdPRUZxY21KTFFHWWdtRzFXMklXcytQd1JwWTdENGhLVEFoVjFSNkVv"
            + "amE1L3BPcmVDL09kZXlQWmVxDQo1SUZTOUZZZk02U0Npd2hrK3l2Q1FHbVo0"
            + "YzE5SjM0ZjVFYkRrK1NQR2tEK25EQ0E3L3VMUWNUMlJURE14SzBaDQpuZlo2"
            + "Nm1Sc0ZjcXRGaWdScjVFcmtKZDdoUVV6eHNOV0VrNzJEVUFIcVgvNlNjeWtt"
            + "SkR2V0plSUpqZlcNCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0NCg==");

    private byte[] AC_RAIZ_ICPBRASIL = Base64.decode(
        "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tDQpNSUlFdURDQ0E2Q2dBd0lC"
            + "QWdJQkJEQU5CZ2txaGtpRzl3MEJBUVVGQURDQnRERUxNQWtHQTFVRUJoTUNR"
            + "bEl4DQpFekFSQmdOVkJBb1RDa2xEVUMxQ2NtRnphV3d4UFRBN0JnTlZCQXNU"
            + "TkVsdWMzUnBkSFYwYnlCT1lXTnBiMjVoDQpiQ0JrWlNCVVpXTnViMnh2WjJs"
            + "aElHUmhJRWx1Wm05eWJXRmpZVzhnTFNCSlZFa3hFVEFQQmdOVkJBY1RDRUp5"
            + "DQpZWE5wYkdsaE1Rc3dDUVlEVlFRSUV3SkVSakV4TUM4R0ExVUVBeE1vUVhW"
            + "MGIzSnBaR0ZrWlNCRFpYSjBhV1pwDQpZMkZrYjNKaElGSmhhWG9nUW5KaGMy"
            + "bHNaV2x5WVRBZUZ3MHdNVEV4TXpBeE1qVTRNREJhRncweE1URXhNekF5DQpN"
            + "elU1TURCYU1JRzBNUXN3Q1FZRFZRUUdFd0pDVWpFVE1CRUdBMVVFQ2hNS1NV"
            + "TlFMVUp5WVhOcGJERTlNRHNHDQpBMVVFQ3hNMFNXNXpkR2wwZFhSdklFNWhZ"
            + "Mmx2Ym1Gc0lHUmxJRlJsWTI1dmJHOW5hV0VnWkdFZ1NXNW1iM0p0DQpZV05o"
            + "YnlBdElFbFVTVEVSTUE4R0ExVUVCeE1JUW5KaGMybHNhV0V4Q3pBSkJnTlZC"
            + "QWdUQWtSR01URXdMd1lEDQpWUVFERXloQmRYUnZjbWxrWVdSbElFTmxjblJw"
            + "Wm1sallXUnZjbUVnVW1GcGVpQkNjbUZ6YVd4bGFYSmhNSUlCDQpJakFOQmdr"
            + "cWhraUc5dzBCQVFFRkFBT0NBUThBTUlJQkNnS0NBUUVBd1BNdWR3WC9odm0r"
            + "VWgyYi9sUUFjSFZBDQppc2FtYUxrV2Rrd1A5L1MvdE9LSWdSckw2T3krWklH"
            + "bE9VZGQ2dVl0azlNYS8zcFVwZ2NmTkFqMHZZbTVnc3lqDQpRbzllbXNjK3g2"
            + "bTRWV3drOWlxTVpTQ0s1RVFrQXEvVXQ0bjdLdUxFMStnZGZ0d2RJZ3hmVXNQ"
            + "dDRDeU5yWTUwDQpRVjU3S00yVVQ4eDVycm16RWpyN1RJQ0dwU1VBbDJnVnFl"
            + "NnhhaWkrYm1ZUjFRcm1XYUJTQUc1OUxya3Jqcll0DQpiUmhGYm9VRGUxREsr"
            + "NlQ4czVMNms4Yzhva3BiSHBhOXZlTXp0RFZDOXNQSjYwTVdYaDZhblZLbzFV"
            + "Y0xjYlVSDQp5RWVOdlpuZVZSS0FBVTZvdXdkakR2d2xzYUt5ZEZLd2VkMFRv"
            + "UTQ3Ym1VS2djbSt3VjNlVFJrMzZVT25Ud0lEDQpBUUFCbzRIU01JSFBNRTRH"
            + "QTFVZElBUkhNRVV3UXdZRllFd0JBUUF3T2pBNEJnZ3JCZ0VGQlFjQ0FSWXNh"
            + "SFIwDQpjRG92TDJGamNtRnBlaTVwWTNCaWNtRnphV3d1WjI5MkxtSnlMMFJR"
            + "UTJGamNtRnBlaTV3WkdZd1BRWURWUjBmDQpCRFl3TkRBeW9EQ2dMb1lzYUhS"
            + "MGNEb3ZMMkZqY21GcGVpNXBZM0JpY21GemFXd3VaMjkyTG1KeUwweERVbUZq"
            + "DQpjbUZwZWk1amNtd3dIUVlEVlIwT0JCWUVGSXI2OFZlRUVSTTFrRUw2VjBs"
            + "VWFRMmt4UEEzTUE4R0ExVWRFd0VCDQovd1FGTUFNQkFmOHdEZ1lEVlIwUEFR"
            + "SC9CQVFEQWdFR01BMEdDU3FHU0liM0RRRUJCUVVBQTRJQkFRQVpBNWMxDQpV"
            + "L2hnSWg2T2NnTEFmaUpnRldwdm1EWldxbFYzMC9iSEZwajhpQm9iSlNtNXVE"
            + "cHQ3VGlyWWgxVXhlM2ZRYUdsDQpZakplKzl6ZCtpelBSYkJxWFBWUUEzNEVY"
            + "Y3drNHFwV3VmMWhIcmlXZmRyeDhBY3FTcXI2Q3VRRndTcjc1Rm9zDQpTemx3"
            + "REFEYTcwbVQ3d1pqQW1RaG5aeDJ4SjZ3ZldsVDlWUWZTLy9KWWVJYzdGdWUy"
            + "Sk5MZDAwVU9TTU1haUsvDQp0NzllbktOSEVBMmZ1cEgzdkVpZ2Y1RWg0YlZB"
            + "TjVWb2hyVG02TVk1M3g3WFFaWnIxTUU3YTU1bEZFblNlVDB1DQptbE9BalIy"
            + "bUFidlNNNVg1b1NaTnJtZXRkenlUajJmbENNOENDN01MYWIwa2tkbmdSSWxV"
            + "QkdIRjEvUzVubVBiDQpLKzlBNDZzZDMzb3FLOG44DQotLS0tLUVORCBDRVJU"
            + "SUZJQ0FURS0tLS0tDQo=");

    private byte[] schefer = Base64.decode(
        "MIIEnDCCBAWgAwIBAgICIPAwDQYJKoZIhvcNAQEEBQAwgcAxCzAJBgNVBAYT"
            + "AkRFMQ8wDQYDVQQIEwZIRVNTRU4xGDAWBgNVBAcTDzY1MDA4IFdpZXNiYWRl"
            + "bjEaMBgGA1UEChMRU0NIVUZBIEhPTERJTkcgQUcxGjAYBgNVBAsTEVNDSFVG"
            + "QSBIT0xESU5HIEFHMSIwIAYDVQQDExlJbnRlcm5ldCBCZW51dHplciBTZXJ2"
            + "aWNlMSowKAYJKoZIhvcNAQkBFht6ZXJ0aWZpa2F0QHNjaHVmYS1vbmxpbmUu"
            + "ZGUwHhcNMDQwMzMwMTEwODAzWhcNMDUwMzMwMTEwODAzWjCBnTELMAkGA1UE"
            + "BhMCREUxCjAIBgNVBAcTASAxIzAhBgNVBAoTGlNIUyBJbmZvcm1hdGlvbnNz"
            + "eXN0ZW1lIEFHMRwwGgYDVQQLExM2MDAvMDU5NDktNjAwLzA1OTQ5MRgwFgYD"
            + "VQQDEw9TY2hldHRlciBTdGVmYW4xJTAjBgkqhkiG9w0BCQEWFlN0ZWZhbi5T"
            + "Y2hldHRlckBzaHMuZGUwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAJD0"
            + "95Bi76fkAMjJNTGPDiLPHmZXNsmakngDeS0juzKMeJA+TjXFouhYh6QyE4Bl"
            + "Nf18fT4mInlgLefwf4t6meIWbiseeTo7VQdM+YrbXERMx2uHsRcgZMsiMYHM"
            + "kVfYMK3SMJ4nhCmZxrBkoTRed4gXzVA1AA8YjjTqMyyjvt4TAgMBAAGjggHE"
            + "MIIBwDAJBgNVHRMEAjAAMBEGCWCGSAGG+EIBAQQEAwIEsDALBgNVHQ8EBAMC"
            + "BNAwOQYJYIZIAYb4QgENBCwWKlplcnRpZmlrYXQgbnVyIGZ1ZXIgU0NIVUZB"
            + "LU9ubGluZSBndWVsdGlnLjAdBgNVHQ4EFgQUXReirhBfg0Yhf6MsBWoo/nPa"
            + "hGwwge0GA1UdIwSB5TCB4oAUf2UyCaBV9JUeG9lS1Yo6OFBUdEKhgcakgcMw"
            + "gcAxCzAJBgNVBAYTAkRFMQ8wDQYDVQQIEwZIRVNTRU4xGDAWBgNVBAcTDzY1"
            + "MDA4IFdpZXNiYWRlbjEaMBgGA1UEChMRU0NIVUZBIEhPTERJTkcgQUcxGjAY"
            + "BgNVBAsTEVNDSFVGQSBIT0xESU5HIEFHMSIwIAYDVQQDExlJbnRlcm5ldCBC"
            + "ZW51dHplciBTZXJ2aWNlMSowKAYJKoZIhvcNAQkBFht6ZXJ0aWZpa2F0QHNj"
            + "aHVmYS1vbmxpbmUuZGWCAQAwIQYDVR0RBBowGIEWU3RlZmFuLlNjaGV0dGVy"
            + "QHNocy5kZTAmBgNVHRIEHzAdgRt6ZXJ0aWZpa2F0QHNjaHVmYS1vbmxpbmUu"
            + "ZGUwDQYJKoZIhvcNAQEEBQADgYEAWzZtN9XQ9uyrFXqSy3hViYwV751+XZr0"
            + "YH5IFhIS+9ixNAu8orP3bxqTaMhpwoU7T/oSsyGGSkb3fhzclgUADbA2lrOI"
            + "GkeB/m+FArTwRbwpqhCNTwZywOp0eDosgPjCX1t53BB/m/2EYkRiYdDGsot0"
            + "kQPOVGSjQSQ4+/D+TM8=");

    // circular dependency certificates
    private static final byte[] circCA = Base64.decode(
        "MIIDTzCCAjegAwIBAgIDARAAMA0GCSqGSIb3DQEBBQUAMDkxCzAJBgNVBAYT"
            + "AkZSMRAwDgYDVQQKEwdHSVAtQ1BTMRgwFgYDVQQLEw9HSVAtQ1BTIEFOT05Z"
            + "TUUwHhcNMDQxMDExMDAwMDAxWhcNMTQxMjMxMjM1OTU5WjA5MQswCQYDVQQG"
            + "EwJGUjEQMA4GA1UEChMHR0lQLUNQUzEYMBYGA1UECxMPR0lQLUNQUyBBTk9O"
            + "WU1FMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3WyWDwcM58aU"
            + "hPX4ueI1mwETt3WdQtMfIdRiCXeBrjCkYCc7nIgCmGbnfTzXSplHRgKColWh"
            + "q/Z+1rHYayje1gjAEU2+4/r1P2pnBmPgquDuguktCIbDtCcGZu0ylyKeHh37"
            + "aeIKzkcmRSLRzvGf/eO3RdFksrvaPaSjqCVfGRXVDKK2uftE8rIFJE+bCqow"
            + "6+WiaAaDDiJaSJPuu5hC1NA5jw0/BFodlCuAvl1GJ8A+TICkYWcSpKS9bkSC"
            + "0i8xdGbSSk94shA1PdDvRdFMfFys8g4aupBXV8yqqEAUkBYmOtZSJckc3W4y"
            + "2Gx53y7vY07Xh63mcgtJs2T82WJICwIDAQABo2AwXjAdBgNVHQ4EFgQU8c/P"
            + "NNJaL0srd9SwHwgtvwPB/3cwDgYDVR0PAQH/BAQDAgIEMBkGA1UdIAQSMBAw"
            + "DgYMKoF6AUcDBwgAAAABMBIGA1UdEwEB/wQIMAYBAf8CAQEwDQYJKoZIhvcN"
            + "AQEFBQADggEBAHRjYDPJKlfUzID0YzajZpgR/i2ngJrJqYeaWCmwzBgNUPad"
            + "uBKSGHmPVg21sfULMSnirnR+e90i/D0EVzLwQzcbjPDD/85rp9QDCeMxqqPe"
            + "9ZCHGs2BpE/HOQMP0QfQ3/Kpk7SvOH/ZcpIf6+uE6lLBQYAGs5cxvtTGOzZk"
            + "jCVFG+TrAnF4V5sNkn3maCWiYLmyqcnxtKEFSONy2bYqqudx/dBBlRrDbRfZ"
            + "9XsCBdiXAHY1hFHldbfDs8rslmkXJi3fJC028HZYB6oiBX/JE7BbMk7bRnUf"
            + "HSpP7Sjxeso2SY7Yit+hQDVAlqTDGmh6kLt/hQMpsOMry4vgBL6XHKw=");

    private static final byte[] circCRLCA = Base64.decode(
        "MIIDXDCCAkSgAwIBAgIDASAAMA0GCSqGSIb3DQEBBQUAMDkxCzAJBgNVBAYT"
            + "AkZSMRAwDgYDVQQKEwdHSVAtQ1BTMRgwFgYDVQQLEw9HSVAtQ1BTIEFOT05Z"
            + "TUUwHhcNMDQxMDExMDAwMDAxWhcNMTQxMjMxMjM1OTU5WjA5MQswCQYDVQQG"
            + "EwJGUjEQMA4GA1UEChMHR0lQLUNQUzEYMBYGA1UECxMPR0lQLUNQUyBBTk9O"
            + "WU1FMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwfEcFK0g7Kfo"
            + "o5f2IBF7VEd/AG+RVGSds0Yg+u2kNYu4k04HR/+tOdBQtJvyr4W5jrQKsC5X"
            + "skeFWMyWaFKzAjZDWB52HWp/kiMivGcxnYDuYf5piukSC+d2+vL8YaAphDzV"
            + "HPnxEKqoM/J66uUussDTqfcL3JC/Bc7kBwn4srrsZOsamMWTQQtEqVQxNN7A"
            + "ROSRsdiTt3hMOKditc9/NBNmjZWxgc7Twr/SaZ8CfN5wf2wuOl23knWL0QsJ"
            + "0lSMBSBTzTcfAke4/jIT7d4nVMp3t7dsna8rt56pFK4wpRFGuCt+1P5gi51x"
            + "xVSdI+JoNXv6zGO4o8YVaRpC5rQeGQIDAQABo20wazAfBgNVHSMEGDAWgBTx"
            + "z8800lovSyt31LAfCC2/A8H/dzAdBgNVHQ4EFgQUGa3SbBrJx/wa2MQwhWPl"
            + "dwLw1+IwDgYDVR0PAQH/BAQDAgECMBkGA1UdIAQSMBAwDgYMKoF6AUcDBwgA"
            + "AAABMA0GCSqGSIb3DQEBBQUAA4IBAQAPDpYe2WPYnXTLsXSIUREBNMLmg+/7"
            + "4Yhq9uOm5Hb5LVkDuHoEHGfmpXXEvucx5Ehu69hw+F4YSrd9wPjOiG8G6GXi"
            + "RcrK8nE8XDvvV+E1HpJ7NKN4fSAoSb+0gliiq3aF15bvXP8nfespdd/x1xWQ"
            + "mpYCx/mJeuqONQv2/D/7hfRKYoDBaAkWGodenPFPVs6FxwnEuH2R+KWCUdA9"
            + "L04v8JBeL3kZiALkU7+DCCm7A0imUAgeeArbAbfIPu6eDygm+XndZ9qi7o4O"
            + "AntPxrqbeXFIbDrQ4GV1kpxnW+XpSGDd96SWKe715gxkkDBppR5IKYJwRb6O"
            + "1TRQIf2F+muQ");

    private static final byte[] circCRL = Base64.decode(
        "MIIB1DCBvQIBATANBgkqhkiG9w0BAQUFADA5MQswCQYDVQQGEwJGUjEQMA4G"
            + "A1UEChMHR0lQLUNQUzEYMBYGA1UECxMPR0lQLUNQUyBBTk9OWU1FFw0xMDAx"
            + "MDcwMzAwMTVaFw0xMDAxMTMwMzAwMTVaMACgTjBMMB8GA1UdIwQYMBaAFBmt"
            + "0mwaycf8GtjEMIVj5XcC8NfiMAsGA1UdFAQEAgILgzAcBgNVHRIEFTATgRFh"
            + "Yy1naXBAZ2lwLWNwcy5mcjANBgkqhkiG9w0BAQUFAAOCAQEAtF1DdFl1MQvf"
            + "vNkbrCPuppNYcHen4+za/ZDepKuwHsH/OpKuaDJc4LndRgd5IwzfpCHkQGzt"
            + "shK50bakN8oaYJgthKIOIJzR+fn6NMjftfR2a27Hdk2o3eQXRHQ360qMbpSy"
            + "qPb3WfuBhxO2/DlLChJP+OxZIHtT/rNYgE0tlIv7swYi81Gq+DafzaZ9+A5t"
            + "I0L2Gp/NUDsp5dF6PllAGiXQzl27qkcu+r50w+u0gul3nobXgbwPcMSYuWUz"
            + "1lhA+uDn/EUWV4RSiJciCGSS10WCkFh1/YPo++mV15KDB0m+8chscrSu/bAl"
            + "B19LxL/pCX3qr5iLE9ss3olVImyFZg==");

    private void checkCircProcessing()
        throws Exception
    {
        CertificateFactory cf = CertificateFactory.getInstance("X.509", "BC");

        X509Certificate caCert = (X509Certificate)cf.generateCertificate(new ByteArrayInputStream(circCA));
        X509Certificate crlCaCert = (X509Certificate)cf.generateCertificate(new ByteArrayInputStream(circCRLCA));
        X509CRL crl = (X509CRL)cf.generateCRL(new ByteArrayInputStream(circCRL));

        List list = new ArrayList();

        list.add(caCert);
        list.add(crlCaCert);
        list.add(crl);

        CertStoreParameters ccsp = new CollectionCertStoreParameters(list);
        CertStore store = CertStore.getInstance("Collection", ccsp);

        Date validDate = new Date(crl.getThisUpdate().getTime() + 60 * 60 * 1000);

        //validating path
        List certchain = new ArrayList();

        certchain.add(crlCaCert);
        CertPath cp = CertificateFactory.getInstance("X.509", "BC").generateCertPath(certchain);

        Set trust = new HashSet();
        trust.add(new TrustAnchor(caCert, null));

        CertPathValidator cpv = CertPathValidator.getInstance("PKIX", "BC");
        //PKIXParameters param = new PKIXParameters(trust);

        PKIXBuilderParameters param = new PKIXBuilderParameters(trust, null);
        X509CertSelector certSelector = new X509CertSelector();
        certSelector.setCertificate(crlCaCert);
        param.setTargetCertConstraints(certSelector);
        param.addCertStore(store);
        param.setRevocationEnabled(true);
        param.setDate(validDate);

        PKIXCertPathValidatorResult result = (PKIXCertPathValidatorResult)cpv.validate(cp, param);
    }

    private void checkPolicyProcessingAtDomainMatch()
        throws Exception
    {
        CertificateFactory cf = CertificateFactory.getInstance("X.509", "BC");

        X509Certificate root = (X509Certificate)cf.generateCertificate(this.getClass().getResourceAsStream("qvRooCa3.crt"));
        X509Certificate ca1 = (X509Certificate)cf.generateCertificate(this.getClass().getResourceAsStream("suvaRoot1.crt"));
        X509Certificate ca2 = (X509Certificate)cf.generateCertificate(this.getClass().getResourceAsStream("suvaEmail1.crt"));
        X509Certificate ee = (X509Certificate)cf.generateCertificate(this.getClass().getResourceAsStream("suvaEE.crt"));

        List certchain = new ArrayList();
        certchain.add(ee);
        certchain.add(ca2);
        certchain.add(ca1);

        Set trust = new HashSet();
        trust.add(new TrustAnchor(root, null));

        CertPathValidator cpv = CertPathValidator.getInstance("PKIX", "BC");
        PKIXParameters param = new PKIXParameters(trust);
        param.setRevocationEnabled(false);
        param.setDate(new Date(0x156445410b4L)); // around 1st August 2016

        CertPath cp = cf.generateCertPath(certchain);

        MyChecker checker = new MyChecker();
        param.addCertPathChecker(checker);

        PKIXCertPathValidatorResult result =
            (PKIXCertPathValidatorResult)cpv.validate(cp, param);
    }

    public void testEmptyPath()
        throws Exception
    {
        CertificateFactory cf = CertificateFactory.getInstance("X.509", "BC");
        X509Certificate rootCert = (X509Certificate)cf.generateCertificate(new ByteArrayInputStream(CertPathTest.rootCertBin));

        List list = new ArrayList();
        list.add(rootCert);
        CollectionCertStoreParameters ccsp = new CollectionCertStoreParameters(list);
        CertStore store = CertStore.getInstance("Collection", ccsp, "BC");

        List certchain = new ArrayList();
        CertPath cp = CertificateFactory.getInstance("X.509", "BC").generateCertPath(certchain);
        Set trust = new HashSet();
        trust.add(new TrustAnchor(rootCert, null));

        CertPathValidator cpv = CertPathValidator.getInstance("PKIX", "BC");
        PKIXParameters param = new PKIXParameters(trust);
        param.addCertStore(store);
        MyChecker checker = new MyChecker();
        param.addCertPathChecker(checker);

        try
        {
            cpv.validate(cp, param);
        }
        catch (CertPathValidatorException e)
        {
            if (!"Certification path is empty.".equals(e.getMessage()))
            {
                fail("message mismatch");
            }
        }
    }


    private void constraintTest()
        throws Exception
    {
        CertificateFactory cf = CertificateFactory.getInstance("X.509", "BC");

        X509Certificate rootCert = (X509Certificate)cf.generateCertificate(this.getClass().getResourceAsStream("CERT_CI_ECDSA_NIST.pem"));
        X509Certificate interCert = (X509Certificate)cf.generateCertificate(this.getClass().getResourceAsStream("CERT_EUM_ECDSA_NIST.pem"));
        X509Certificate finalCert = (X509Certificate)cf.generateCertificate(this.getClass().getResourceAsStream("CERT_EUICC_ECDSA_NIST.pem"));

        List list = new ArrayList();
        list.add(interCert);
        list.add(finalCert);

        CertPath certPath = cf.generateCertPath(list);

        Set trust = new HashSet();
        trust.add(new TrustAnchor(rootCert, null));

        CertPathValidator cpv = CertPathValidator.getInstance("PKIX", "BC");
        PKIXParameters param = new PKIXParameters(trust);
        param.setRevocationEnabled(false);

        cpv.validate(certPath, param);

    }

    private static byte[] crlFake = Base64.decode(
        "MIIBzTCBtgIBATANBgkqhkiG9w0BAQsFADAiMQswCQYDVQQGEwJYWDETMBEGA1UE" +
        "CgwKQ1JMcyAnciBVcxcNMjQwMzI1MTg0NzAwWhcNMjQwNDAxMTg0NzAwWqBgMF4w" +
        "CgYDVR0UBAMCAQEwHwYDVR0jBBgwFoAU/NE0t8uklbG2WeoLBWIe6JqPtDowLwYD" +
        "VR0cAQH/BCUwI6AeoByGGmh0dHA6Ly9mb28uZXhhbXBsZS9jcmwuZGxshAH/MA0G" +
        "CSqGSIb3DQEBCwUAA4IBAQAN8oDSvWsg3JvUJ4MkXvczaFb72VH0J/VL5PV2cBSm" +
        "MfaVBKnUsNr1IcxT06KF8gNrDTpKqJ9fetO290swZfcPt9sEVUBVQUpdlQc3tya1" +
        "jYWmFkA3tkpqH5rBCQa3CBm1Cg8cbFBtwWgWr70NsVvfD6etjAEP9Ze+MSXnGV0p" +
        "w9EeOV07HnSD/PGQwqCiaSn5DdIDVoH8eFSGmgNLw+b4SwUjmz8PqsZwvHxJvleV" +
        "1D8cj7zdR4ywgRMjEfJZ8Bp+Tdu64Gv0doDS0iEJIshLHYkcW1okpq/tPm8kKAbD" +
        "reparePNQwhScVcDiSL73eEBIPokgG3QhohiucP5MeF1");

    private static byte[] crlIssuer = Base64.decode(
        "MIIDMzCCAhugAwIBAgIUPOARSBZTC4SU8f/RrhdPXfZVh9EwDQYJKoZIhvcNAQEL\n" +
        "BQAwIzELMAkGA1UEBhMCWFgxFDASBgNVBAoMC0NlcnRzICdyIFVzMB4XDTI0MDMy\n" +
        "NTE4NDcwMFoXDTI1MDMyNTE4NDcwMFowIjELMAkGA1UEBhMCWFgxEzARBgNVBAoM\n" +
        "CkNSTHMgJ3IgVXMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCleY8S\n" +
        "gEwPfvfUcIuix5dC7MgFudzaJROINa3u7cW0Rh+mivfepuGl9I683qinDebmE1Sq\n" +
        "bVyHDi4RqpM+BCQ0EnW6idriL+13BqNU4QRd68gwF4eNXw9rtmixVGvcvcUngNnz\n" +
        "XPrJyWqarjFQ8ECH09I9q/Fv3OAWPmTbzAgWdXV7cx/pCHFNEU3qSWeXkbumKV5l\n" +
        "DqTs/J82/n5HZfRjUVIMbf4X6/9wA9BQX8aYbUMng49M5GVd/bg3RXGBLF4lXIUd\n" +
        "IPpGYrKT2V+EFq9yKqbnXawTXKw7mBNoIbaN950f1VMdf8czsPNxdeCHJzNtQV70\n" +
        "aOqa2hLzxAxzAz7DAgMBAAGjYDBeMB0GA1UdDgQWBBRdiKBrVfofgq1XL7AZu3Wk\n" +
        "t83qzjAfBgNVHSMEGDAWgBS04fYwVDNa70uNyIJtV75OHwEHmTAMBgNVHRMBAf8E\n" +
        "AjAAMA4GA1UdDwEB/wQEAwIBAjANBgkqhkiG9w0BAQsFAAOCAQEAF5XrOXxVfCFb\n" +
        "S5EXxpAk8iXMAOfcfYiWEUT9DdJ3ABeAFnhbiLdlKq8J3BGr1Iiveo2pE9fKz9s/\n" +
        "2tZjzbe9Kfg05mfyn9DS5AoWjieW5zaAZpDR9pKkq9/d7pDTbHwvDnNLoMMHRPZP\n" +
        "2tsBhjcPPay8zWKLz+8dfPyrGpbGfFg/zd3KBNefc12Sl0Iw6XQUaIpDxyJBvpIU\n" +
        "0Xo1R1F22gJ7oG1zI28mr6SGyBvJ8r1c0sQ1qQt+iA/0M5qXRjuLIhO8/ajlMQwP\n" +
        "Sdasa53HOErxWqsxNRpwJkaynSiKSwGeqLxdTYwWcWrsYB7RqKgjbQnhSBSd3TKm\n" +
        "H2P790A+oQ==");

    private static byte[] crlSecretary = Base64.decode(
        "MIIDejCCAmKgAwIBAgIUI4Xq9G+KWEr2NPfGbY4A2dfXp50wDQYJKoZIhvcNAQEL\n" +
        "BQAwIzELMAkGA1UEBhMCWFgxFDASBgNVBAoMC0NlcnRzICdyIFVzMB4XDTI0MDMy\n" +
        "NTE4NDcwMFoXDTI1MDMyNTE4NDcwMFowIjELMAkGA1UEBhMCWFgxEzARBgNVBAoM\n" +
        "CkNSTHMgJ3IgVXMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCkynb7\n" +
        "zm0ooFfVkkqj9ppBiTh0YGUqv7/jQoFMDJ/XVtYGUJdyPTXoD9cP1ZypzONmK07U\n" +
        "Rc0WMug47hv2tZgrVOxqrGQqDD7e4LM3luinwG5eW3XYT4eJr6Urbk8KSdKSYzqj\n" +
        "wjY217KQ8DDgioUInWBUyz5UWrG014QbcEgwX0JGpQrwaaPQtbUd58f5x/LCdsXC\n" +
        "p41ySSNsYoKhDawnNblLVxhr+Vp7eQ0wj7LaD/+k12ZDMQbkj3PsGBiWqm+e2uwV\n" +
        "n9cq9kK6ARN0svju5dpDw5hERRrQ1GR87WvHWHUtmnR7s7+xacRpZTUvJ5Xsi0Rf\n" +
        "Eq1SDPYPyT8ksrt7AgMBAAGjgaYwgaMwHQYDVR0OBBYEFPzRNLfLpJWxtlnqCwVi\n" +
        "Huiaj7Q6MB8GA1UdIwQYMBaAFLTh9jBUM1rvS43Igm1Xvk4fAQeZMAwGA1UdEwEB\n" +
        "/wQCMAAwUwYDVR0fBEwwSjBIoB6gHIYaaHR0cDovL2Zvby5leGFtcGxlL2NybC5k\n" +
        "bGyiJqQkMCIxCzAJBgNVBAYTAlhYMRMwEQYDVQQKDApDUkxzICdyIFVzMA0GCSqG\n" +
        "SIb3DQEBCwUAA4IBAQBY72Z1LwWsVbnYl6ZhWDAAuy0bwTMKwF8JwpG1PpFzC6p0\n" +
        "DJd36c3ZOzRYgjpmApi3X9lFx0oyuZOjBIlMtqnXgKjYBytF2jmf8DziIsCnvMI8\n" +
        "1IiFRjWjm56y0xaxBqv9yzvTqKG198vxakxPAUn8oONMtLvqHAvoQyHCBej5Xirg\n" +
        "joJkPeHeRwl9sgYZcqowNHGHiBX8KtXeatkHkpmxZO5cunGD+RcOnBpJEfZJhopX\n" +
        "GaW1DPRY0qqPFhnLcQsv8UZEyDxyYH/HuGaZy3u9lT1SqlOx2zzQnTK6EyIc92n3\n" +
        "suILIm4MBrqXYXUlHkMzLmpJGH9lg9xaFn3vCU7Q");

    private static byte[] crlRoot = Base64.decode(
        "MIIDFjCCAf6gAwIBAgIUF/hP3a/TkmHlfhYYUiFNw/H5lMwwDQYJKoZIhvcNAQEL\n" +
            "BQAwIzELMAkGA1UEBhMCWFgxFDASBgNVBAoMC0NlcnRzICdyIFVzMB4XDTI0MDMy\n" +
            "NTE4NDcwMFoXDTI1MDMyNTE4NDcwMFowIzELMAkGA1UEBhMCWFgxFDASBgNVBAoM\n" +
            "C0NlcnRzICdyIFVzMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAomfH\n" +
            "KuGQzqGkFGSsKLESgJbRRRQsIuJ19w/sumNHNPnbl93rEgdoF1y2yUFcY0ZipZCg\n" +
            "lIpfhOkp6I+WLtF59t8vLw30P1ZBwmbjC54EwGLH3WRDPS0j+33TfDjNdQRwY4u6\n" +
            "j2EK6drXPhBPsaG0map3VfWQelaStAoIC6evoYFzfO2E7Ik4xv06U47WHefseBue\n" +
            "ZcsFvfW3bf/E04PFc2YssUyqjiaa0sU/w7l9xj2P+vCqpM393ZWJX6GRcns/wUJ/\n" +
            "na7iXpIO82EV3/eExeXoHc912L+m0HoB86RYQat+wyhX6Z5i1ApU6zXqGU7D8cPD\n" +
            "DrbIjwLDMwKPbC9FjwIDAQABo0IwQDAdBgNVHQ4EFgQUtOH2MFQzWu9LjciCbVe+\n" +
            "Th8BB5kwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAgQwDQYJKoZIhvcN\n" +
            "AQELBQADggEBAJGeqkMrzOgesGaCHJJgX/qpG7bp4KPPL0bi7EYnT1cuy5ss053I\n" +
            "Ooh5APYn+GrufWjYn4mwSekvuRTB6VdR4YMeoYPMxWJRp3l7s0aHLo98BbW9WX+4\n" +
            "ju+K/Dndbrs1v7r4IB79hu4QtR7BVaEQ8UjqY+/I1VeYKtAd7scQGKpSNOPN3YVu\n" +
            "+QY3fXy+nfDhj7drUeAHVj+Qz/6RZOIhmIPj7adsZhDQwvMG3cAkAfVGncP7n+cN\n" +
            "nqZyYu8PPQp4g+QM42kXXBu5N8QwkCtcMe2nvKiQvEOZww70N3mTIK8CSxLla5pI\n" +
            "635lNPBZubGF6m35P7EArB0JuU2KYNgUxis=\n");

    private static byte[] crlVictim = Base64.decode(
        "MIIDjTCCAnWgAwIBAgIUW8wsCzJEg7WzpMvkUKyloeKqKLYwDQYJKoZIhvcNAQEL\n" +
        "BQAwIzELMAkGA1UEBhMCWFgxFDASBgNVBAoMC0NlcnRzICdyIFVzMB4XDTI0MDMy\n" +
        "NTE4NDcwMFoXDTI1MDMyNTE4NDcwMFowJTELMAkGA1UEBhMCWFgxFjAUBgNVBAoM\n" +
        "DVVubHVja3kgJ3IgV2UwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC6\n" +
        "erJm/+hf6IhoqCYfX+y6uiVSSF/J6VyENk+oXS2g71g1sapGCXRO8xlDqH1rhFzC\n" +
        "IJ56nC14K9w4r+6D3FUKw4G5sKMRTMX7U5brjd8wRd3XHAIUdSCP9SVrNz6bmcjf\n" +
        "B27vBT0ifIC7bQg7Y01BoqnBPObuwT7ufk951rFzCIagzSylzR/GRNhMYo4rO6jw\n" +
        "Ih84LpAxUQ1vFAaBb5GCVhXoUWecu+RtIaIDo9tn8PF16O6VW8zPmsoV9HELD8Sx\n" +
        "HuoSXXcsF2OW55XLeAO+l1tikAVqA6nUvQx03bb3TW7W+3v6nGzG308fHA32TdLk\n" +
        "ZLK9nPnF5hF4pFmWpjwHAgMBAAGjgbYwgbMwHQYDVR0OBBYEFMitbC8lM9mw/hc6\n" +
        "TnvL5vpAyfpZMB8GA1UdIwQYMBaAFLTh9jBUM1rvS43Igm1Xvk4fAQeZMAwGA1Ud\n" +
        "EwEB/wQCMAAwDgYDVR0PAQH/BAQDAgeAMFMGA1UdHwRMMEowSKAeoByGGmh0dHA6\n" +
        "Ly9mb28uZXhhbXBsZS9jcmwuZGxsoiakJDAiMQswCQYDVQQGEwJYWDETMBEGA1UE\n" +
        "CgwKQ1JMcyAnciBVczANBgkqhkiG9w0BAQsFAAOCAQEAmysx1oqEUDUpLg98K9Rw\n" +
        "AXTykVDjjG0ZKg7UtDcaIeBfomhXv+Sh2oz9zqqZQ5/4HGIwe2fAsbQZmlH//8Yb\n" +
        "ovEZCo3WmhJSyTDB2KLebPJLw5HOi7QrAjYJWKR+pkuQmxMPoSAdMXRkiBmzYjZL\n" +
        "lxHaT6Y2IMZ6kVtHCmcOFaHWJyPAUZ4ymO03cb/1M73ioecf9jMgIf7YBaopty2p\n" +
        "X2GVHaCE1m7u+2WU45b34PBRY/ZvhZvuJKi3TfuaLMJFPz6HY4XbHPnlBP4EwXpC\n" +
        "5VaJvOMXWZPWh/yrCVEKMzFxesbwHV/vyOUls0P4kIY383/78MvzchHLhwR7h2fy\n" +
        "Iw==");

    private void testNoKeyUsageCRLSigner()
        throws Exception
    {
        CertificateFactory cf = CertificateFactory.getInstance("X.509", "BC");

        X509Certificate root = (X509Certificate)cf.generateCertificate(new ByteArrayInputStream(crlRoot));
        X509Certificate crlIss = (X509Certificate)cf.generateCertificate(new ByteArrayInputStream(crlIssuer));
        X509Certificate secretary = (X509Certificate)cf.generateCertificate(new ByteArrayInputStream(crlSecretary));
        X509Certificate victim = (X509Certificate)cf.generateCertificate(new ByteArrayInputStream(crlVictim));

        X509CRL fakeCrl = (X509CRL)cf.generateCRL(new ByteArrayInputStream(crlFake));

        List list = new ArrayList();

//        list.add(root);
//        list.add(crlIss);
        list.add(secretary);
        list.add(victim);
        list.add(fakeCrl);

        System.setProperty("org.bouncycastle.x509.allow_ca_without_crl_sign", "false");

        CertPath cp = cf.generateCertPath(Collections.singletonList(victim));

        CollectionCertStoreParameters ccsp = new CollectionCertStoreParameters(list);
        CertStore store = CertStore.getInstance("Collection", ccsp, "BC");
        Date validDate = new Date(fakeCrl.getThisUpdate().getTime() + 60 * 60 * 1000);

            //Searching for rootCert by subjectDN without CRL
        Set trust = new HashSet();
        trust.add(new TrustAnchor(root, null));
                                                //
        CertPathValidator cpb = CertPathValidator.getInstance("PKIX", "BC");
        X509CertSelector targetConstraints = new X509CertSelector();
        targetConstraints.setSubject(victim.getSubjectX500Principal().getEncoded());
        PKIXParameters params = new PKIXParameters(trust);
        params.addCertStore(store);
        params.setDate(validDate);

        try
        {
            PKIXCertPathValidatorResult result = (PKIXCertPathValidatorResult)cpb.validate(cp, params);
            fail("path should have failed");
        }
        catch (CertPathValidatorException e)
        {                   
            isTrue("No CRLs found for issuer \"o=Certs 'r Us,c=XX\"".equals(e.getMessage()));
        }

        System.setProperty("org.bouncycastle.x509.allow_ca_without_crl_sign", "true");
    }

    public void performTest()
        throws Exception
    {
        constraintTest();
        testNoKeyUsageCRLSigner();
        CertificateFactory cf = CertificateFactory.getInstance("X.509", "BC");

        // initialise CertStore
        X509Certificate rootCert = (X509Certificate)cf.generateCertificate(new ByteArrayInputStream(CertPathTest.rootCertBin));
        X509Certificate interCert = (X509Certificate)cf.generateCertificate(new ByteArrayInputStream(CertPathTest.interCertBin));
        X509Certificate finalCert = (X509Certificate)cf.generateCertificate(new ByteArrayInputStream(CertPathTest.finalCertBin));
        X509CRL rootCrl = (X509CRL)cf.generateCRL(new ByteArrayInputStream(CertPathTest.rootCrlBin));
        X509CRL interCrl = (X509CRL)cf.generateCRL(new ByteArrayInputStream(CertPathTest.interCrlBin));
        List list = new ArrayList();
        list.add(rootCert);
        list.add(interCert);
        list.add(finalCert);
        list.add(rootCrl);
        list.add(interCrl);
        CollectionCertStoreParameters ccsp = new CollectionCertStoreParameters(list);
        CertStore store = CertStore.getInstance("Collection", ccsp, "BC");
        Date validDate = new Date(rootCrl.getThisUpdate().getTime() + 60 * 60 * 1000);
        //validating path
        List certchain = new ArrayList();
        certchain.add(finalCert);
        certchain.add(interCert);
      
        CertPath cp = CertificateFactory.getInstance("X.509", "BC").generateCertPath(certchain);
        Set trust = new HashSet();
        trust.add(new TrustAnchor(rootCert, null));

        CertPathValidator cpv = CertPathValidator.getInstance("PKIX", "BC");
        PKIXParameters param = new PKIXParameters(trust);
        param.addCertStore(store);
        param.setDate(validDate);
        MyChecker checker = new MyChecker();
        param.addCertPathChecker(checker);

        System.setProperty("org.bouncycastle.x509.allow_ca_without_crl_sign", "true");

        PKIXCertPathValidatorResult result =
            (PKIXCertPathValidatorResult)cpv.validate(cp, param);
        PolicyNode policyTree = result.getPolicyTree();
        PublicKey subjectPublicKey = result.getPublicKey();

        if (checker.getCount() != 2)
        {
            fail("checker not evaluated for each certificate");
        }

        if (!subjectPublicKey.equals(finalCert.getPublicKey()))
        {
            fail("wrong public key returned");
        }

        isTrue(result.getTrustAnchor().getTrustedCert().equals(rootCert));

        // try a path with trust anchor included.
        certchain.clear();
        certchain.add(finalCert);
        certchain.add(interCert);
        certchain.add(rootCert);

        cp = CertificateFactory.getInstance("X.509", "BC").generateCertPath(certchain);

        cpv = CertPathValidator.getInstance("PKIX", "BC");
        param = new PKIXParameters(trust);
        param.addCertStore(store);
        param.setDate(validDate);

        result = (PKIXCertPathValidatorResult)cpv.validate(cp, param);

        System.setProperty("org.bouncycastle.x509.allow_ca_without_crl_sign", "false");

        isTrue(result.getTrustAnchor().getTrustedCert().equals(rootCert));
        
        //
        // invalid path containing a valid one test
        //
        try
        {
            // initialise CertStore
            rootCert = (X509Certificate)cf.generateCertificate(new ByteArrayInputStream(AC_RAIZ_ICPBRASIL));
            interCert = (X509Certificate)cf.generateCertificate(new ByteArrayInputStream(AC_PR));
            finalCert = (X509Certificate)cf.generateCertificate(new ByteArrayInputStream(schefer));

            list = new ArrayList();
            list.add(rootCert);
            list.add(interCert);
            list.add(finalCert);

            ccsp = new CollectionCertStoreParameters(list);
            store = CertStore.getInstance("Collection", ccsp);
            validDate = new Date(finalCert.getNotBefore().getTime() + 60 * 60 * 1000);

            //validating path
            certchain = new ArrayList();
            certchain.add(finalCert);
            certchain.add(interCert);
     
            cp = CertificateFactory.getInstance("X.509", "BC").generateCertPath(certchain);
            trust = new HashSet();
            trust.add(new TrustAnchor(rootCert, null));

            cpv = CertPathValidator.getInstance("PKIX", "BC");
            param = new PKIXParameters(trust);
            param.addCertStore(store);
            param.setRevocationEnabled(false);
            param.setDate(validDate);

            result = (PKIXCertPathValidatorResult)cpv.validate(cp, param);
            policyTree = result.getPolicyTree();
            subjectPublicKey = result.getPublicKey();

            fail("Invalid path validated");
        }
        catch (Exception e)
        {
            if (!(e instanceof CertPathValidatorException
                && e.getMessage().startsWith("Could not validate certificate signature.")))
            {
                fail("unexpected exception", e);
            }
        }

        System.setProperty("org.bouncycastle.x509.allow_ca_without_crl_sign", "true");

        checkCircProcessing();
        checkPolicyProcessingAtDomainMatch();
        validateWithExtendedKeyUsage();
        testEmptyPath();
        checkInvalidCertPath();
    }

    // extended key usage chain
    static byte[] extEE = Base64.decode("MIICtDCCAh2gAwIBAgIBAzANBgkqhkiG9w0BAQUFADCBkjELMAkGA1UEBhMCQVUxKDAmBgNVBAoMH1RoZSBMZWdpb24gb2YgdGhlIEJvdW5jeSBDYXN0bGUxKDAmBgNVBAsMH0JvdW5jeSBJbnRlcm1lZGlhdGUgQ2VydGlmaWNhdGUxLzAtBgkqhkiG9w0BCQEWIGZlZWRiYWNrLWNyeXB0b0Bib3VuY3ljYXN0bGUub3JnMB4XDTE1MDMyNDAzNTEwOVoXDTE1MDUyMzAzNTEwOVowgZYxCzAJBgNVBAYTAkFVMSgwJgYDVQQKDB9UaGUgTGVnaW9uIG9mIHRoZSBCb3VuY3kgQ2FzdGxlMRIwEAYDVQQHDAlNZWxib3VybmUxGDAWBgNVBAMMD0VyaWMgSC4gRWNoaWRuYTEvMC0GCSqGSIb3DQEJARYgZmVlZGJhY2stY3J5cHRvQGJvdW5jeWNhc3RsZS5vcmcwWjANBgkqhkiG9w0BAQEFAANJADBGAkEAtKfkYXBXTxapcIKyK+WLaipil5hBm+EocqS9umJs+umQD3ar+xITnc5d5WVk+rK2VDFloEDGBoh0IOM9ke1+1wIBEaNaMFgwHQYDVR0OBBYEFNBs7G01g7xVEhsMyz7+1yamFmRoMB8GA1UdIwQYMBaAFJQIM28yQPeHN9rRIKrtLqduyckeMBYGA1UdJQEB/wQMMAoGCCsGAQUFBwMDMA0GCSqGSIb3DQEBBQUAA4GBAICrsNswvaXFMreUHHRHrhU4QqPOds8XJe0INx3v/5TfyjPPDMihMEm8WtWbVpFgFAqUQoZscf8cE/SO5375unYFgxrK+p2/je9E82VLF4Xb0cWizjQoWvvTmvFYjt43cGGXgySFLTrW87ju9uNFr/l4W9xvI0hoLI96vEW7Ccho");
    static byte[] extCA = Base64.decode("MIIDIzCCAoygAwIBAgIBAjANBgkqhkiG9w0BAQUFADBcMQswCQYDVQQGEwJBVTEoMCYGA1UECgwfVGhlIExlZ2lvbiBvZiB0aGUgQm91bmN5IENhc3RsZTEjMCEGA1UECwwaQm91bmN5IFByaW1hcnkgQ2VydGlmaWNhdGUwHhcNMTUwMzI0MDM1MTA5WhcNMTUwNTIzMDM1MTA5WjCBkjELMAkGA1UEBhMCQVUxKDAmBgNVBAoMH1RoZSBMZWdpb24gb2YgdGhlIEJvdW5jeSBDYXN0bGUxKDAmBgNVBAsMH0JvdW5jeSBJbnRlcm1lZGlhdGUgQ2VydGlmaWNhdGUxLzAtBgkqhkiG9w0BCQEWIGZlZWRiYWNrLWNyeXB0b0Bib3VuY3ljYXN0bGUub3JnMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCN4NETxec2lpyNKwR6JD+P4Y7a1kzenoQtNmkjDKSG98/d4fjuxU0ZBf/wSsyF5hCT4YDK3GzqQH8ZPUS7DpRJuNu0l4TNnjYmDDngapRymZeMbtgwByTohxmM/t4g8/veZY+ivQeL6Uajkr00nytJxIbiDEBViOMGcGyQFzCOaQIDAP//o4G9MIG6MB0GA1UdDgQWBBSUCDNvMkD3hzfa0SCq7S6nbsnJHjCBhAYDVR0jBH0we4AUwDYZB63EiJeoXnJvawnr5ebxKVyhYKReMFwxCzAJBgNVBAYTAkFVMSgwJgYDVQQKDB9UaGUgTGVnaW9uIG9mIHRoZSBCb3VuY3kgQ2FzdGxlMSMwIQYDVQQLDBpCb3VuY3kgUHJpbWFyeSBDZXJ0aWZpY2F0ZYIBATASBgNVHRMBAf8ECDAGAQH/AgEAMA0GCSqGSIb3DQEBBQUAA4GBAJqUlDjse7Og+7qkkFsiXHzQ8FxT82hzfcji8W7bPwZddCPBEluxCJiJBPYXWsLvwo6BEmCDzT9lLQZ+QZyL1fVbOVHiI24hAalbEBEIrEO4GXMD9spqRQ5yoTJ8CgZHTPo0rJkH/ebprp0YHtahVF440zBOvuLM0QTYpERgO2Oe");
    static byte[] extTrust = Base64.decode("MIICJTCCAY4CAQEwDQYJKoZIhvcNAQEFBQAwXDELMAkGA1UEBhMCQVUxKDAmBgNVBAoMH1RoZSBMZWdpb24gb2YgdGhlIEJvdW5jeSBDYXN0bGUxIzAhBgNVBAsMGkJvdW5jeSBQcmltYXJ5IENlcnRpZmljYXRlMB4XDTE1MDMyNDAzNTEwOVoXDTE1MDUyMzAzNTEwOVowXDELMAkGA1UEBhMCQVUxKDAmBgNVBAoMH1RoZSBMZWdpb24gb2YgdGhlIEJvdW5jeSBDYXN0bGUxIzAhBgNVBAsMGkJvdW5jeSBQcmltYXJ5IENlcnRpZmljYXRlMIGdMA0GCSqGSIb3DQEBAQUAA4GLADCBhwKBgQCyWdLW5ienaMlL42Fkwtn8edl6q5JTFA5b8XdRGXcx1vdUDSUJ57n/7gpwpuJtVuktLt1/hauoVgC2kInzX2vb88KY4FhCU12fBk5rA5HLfTBuCi0gxN+057SalkC96ibBCtacPwUAfOJRPO5Ez+AZmOYrbDY30/wDkQebJu421QIBETANBgkqhkiG9w0BAQUFAAOBgQCDNfqQnQbbmnGzZTl7ccWIyw7SPzWnijpKsQpuRNGkoXfkCcuQLZudytEFZGEL0cycNBnierjJWAn78zGpCQtab01r1GwytRMYz8qO5IIrhsJ4XNafNypYZbi0WtPa07UCQp8tipMbfQNLzSkvkIAaD5IfhdaWKLrSQJwmGg7YAg==");

    private void validateWithExtendedKeyUsage()
        throws Exception
    {
        CertificateFactory cf = CertificateFactory.getInstance("X.509", "BC");

        X509Certificate rootCert = (X509Certificate)cf.generateCertificate(new ByteArrayInputStream(extTrust));
        X509Certificate interCert = (X509Certificate)cf.generateCertificate(new ByteArrayInputStream(extCA));
        X509Certificate finalCert = (X509Certificate)cf.generateCertificate(new ByteArrayInputStream(extEE));

        List list = new ArrayList();
        list.add(rootCert);
        list.add(interCert);
        list.add(finalCert);

        CollectionCertStoreParameters ccsp = new CollectionCertStoreParameters(list);
        CertStore store = CertStore.getInstance("Collection", ccsp, "BC");
        Date validDate = new Date(rootCert.getNotBefore().getTime() + 60 * 60 * 1000);
        //validating path
        List certchain = new ArrayList();
        certchain.add(finalCert);
        certchain.add(interCert);
        CertPath cp = CertificateFactory.getInstance("X.509", "BC").generateCertPath(certchain);
        Set trust = new HashSet();
        trust.add(new TrustAnchor(rootCert, null));

        CertPathValidator cpv = CertPathValidator.getInstance("PKIX", "BC");
        PKIXParameters param = new PKIXParameters(trust);
        param.addCertStore(store);
        param.setDate(validDate);
        param.setRevocationEnabled(false);

        PKIXCertPathValidatorResult result = (PKIXCertPathValidatorResult)cpv.validate(cp, param);
    }

    // invalid EE certificate
    static byte[] extInvEE = Base64.decode("MIICJjCCAY+gAwIBAAIGAV3Y0TnDMA0GCSqGSIb3DQEBCwUAMBExDzANBgNVBAMMBktQMSBDQTAeFw0xNzA4MTIyMzM5MzJaFw0xNzA4MTMwMDA5MzdaMBExDzANBgNVBAMMBktQMSBFRTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAuOcqkp2+HBCuwRDwfR7kkUYXMdhScDG8m6A3Af6hpG86nAimNoVIQe3REaQ6IO0XSdd13rjjRwIXsUFLsrQhQJczF5JeyWXcaYqZyNNbUwFuLeSqOsLS63ltjOJYqOJRxY03Cr//baGWvxGXcRvHoZkg1nEXPcMZhgsy/9JxVoUCAwEAAaOBiDCBhTBABgNVHSMEOTA3gBSPMqzNmTdyjQmr9W1TSDW1h0ZzFaEXpBUwEzERMA8GA1UEAwwIS1AxIFJPT1SCBgFd2NE5wjAdBgNVHQ4EFgQUC1rtYrQdQkA3CLTeV1kbVIdysKQwEgYDVR0TAQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwDQYJKoZIhvcNAQELBQADgYEAGr841G7E84Ow9+fFGW1zzXeTRfxsafdT/bHXCS75bjF2YPitKLcRLkm92VPxANRXIpmt++3iU/oduWqkLsfXnfTGmCwtjj/XrCvkCBQ4GONwmegltJEThMud0XOEB1UN6tfTINfLYpbyfOdE/wLy4Rte0t43aOTTOBo+/SapYOE=");
    static byte[] extInvCA = Base64.decode("MIICKDCCAZGgAwIBAgIGAV3Y0TnCMA0GCSqGSIb3DQEBCwUAMBMxETAPBgNVBAMMCEtQMSBST09UMB4XDTE3MDgxMjIzMzkzMloXDTE3MDgxMzAwMDkzN1owETEPMA0GA1UEAwwGS1AxIENBMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC7Qd/cTP5S0GoPcomcZU5QlJcb1uWydvmQx3U6p4/KOZBhk6JXQeSzT8QZ/gd+9vfosA62SEX+dq7MvxxzeERxdIsVU0zZ1TrYNxlQjnYXiYRVXBczowsxseQ9oSGD94Y4buhrMAltmIHijdzGRVMY41FZmWqNXqsEwQXj6ULX+QIDAQABo4GIMIGFMEAGA1UdIwQ5MDeAFAbfd2S3aiwFww3/0ocLa6ULQjJMoRekFTATMREwDwYDVQQDDAhLUDEgUk9PVIIGAV3Y0TnBMB0GA1UdDgQWBBSPMqzNmTdyjQmr9W1TSDW1h0ZzFTASBgNVHRMBAf8ECDAGAQH/AgEAMA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQsFAAOBgQCnmxQYy6LnvRSMxkTsGIQa4LB51O8skbWc4KYVDfcvTYQuvn6rE/ZoYf82jKXJzXksffanfjn/b38l4l8hwAcBQ8we9yjCkjO8OVDUlYiSGYUhH2ZJrl2+K2Z6wpakZ9Lz3pZ/PSS1FIsVd4I1jkexAdAm1+uMlfWXVt/uTZx98w==");
    static byte[] extInvTrust = Base64.decode("MIIBmjCCAQMCBgFd2NE5wTANBgkqhkiG9w0BAQsFADATMREwDwYDVQQDDAhLUDEgUk9PVDAeFw0xNzA4MTIyMzM5MzJaFw0xNzA4MTMwMDA5MzdaMBMxETAPBgNVBAMMCEtQMSBST09UMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC8U3p6Y9ah0yQ58wpI3H6vQPMdhN6Hh+zuiNzwX3AIpEspUFTfqXJ6EIhqh/EraDnLnoFBajzihwS1y6a+ZyXYKa5pxbFsslmzms+ozcTaJ4mSMiC+DHbGYdOAEzwx2nsEt7UKyrlnl5h2kQFusUPmnXXEorIxhpS2Lul+zEBo1wIDAQABMA0GCSqGSIb3DQEBCwUAA4GBABClwXaJ8S66GmeySf1thOPc1GxIHuubezIjcBbECLZQqn7iwuzp+eft1vtnqNP7BWM1xBZkSe+/2xUsArc1rb1ffZHF3k92+WLbpDh3+NsQIod/91HRWUuu/S2g9oMK4b7BH8JrmBgy3ewtpNZwOaKF613GPCeGv3ya5Z24vBu+");

    static byte[] extInvV2Trust = Base64.decode("MIIBmjCCAQMCBgFd2NhVgTANBgkqhkiG9w0BAQsFADATMREwDwYDVQQDDAhLUDEgUk9PVDAeFw0xNzA4MTIyMzQ3MThaFw0xNzA4MTMwMDE3MjNaMBMxETAPBgNVBAMMCEtQMSBST09UMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCQaASWM5avAAJ57eHQ2zQ0k/mAiYSOkRKDLEzptDPYfzuQtTdAlBPn7tsYx+Ylge4YQwtx5bQZbc3apinBK9tn+c++go0kUF1cec2bacYyFokSP2r48j6ZhlY4MYGfrvfWHULrG2JL2BMeuZVP+wiqXktXCEKVG1fh1m6RY0TJPwIDAQABMA0GCSqGSIb3DQEBCwUAA4GBAC9mXO2i2vltBZZa7RMkizvhzhsehDHbEqvJd2aoWE9JG4sDo2tiIVN5vbq9EWLZVga3ejFzmQ+FI1Ty0xX3fwDgvUyxsveGTs40xwA9TEgVk1KNTQQs+sLE9rRB7L0giKn2DDmHFsOPL1KwxdzqD7vYhJr5av3eAsJpMxF+Anyg");
    static byte[] extInvV2CA = Base64.decode("MIICKDCCAZGgAwIBAgIGAV3Y2FWCMA0GCSqGSIb3DQEBCwUAMBMxETAPBgNVBAMMCEtQMSBST09UMB4XDTE3MDgxMjIzNDcxOFoXDTE3MDgxMzAwMTcyM1owETEPMA0GA1UEAwwGS1AxIENBMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCgb8h3h9d/FzhIc+PMbF0vwdiDKw7N3dNyY6TrmzCMC1mYDXSKmxxDwNKZCKj6VSNfbTDqxYKlZMoGVT8Cl/iE/+XEhOKYLv73rzTqzdMizqcQTCvwps1enGxI5wPBYKGCMWrpJui5RWV9wH6hMvmzSSZq7bdWTvc/pIltCpIj8wIDAQABo4GIMIGFMEAGA1UdIwQ5MDeAFMOcs/uWpVOkGRQJrVIp6cN6tCJQoRekFTATMREwDwYDVQQDDAhLUDEgUk9PVIIGAV3Y2FWBMB0GA1UdDgQWBBTsZ2B5JbgKm9/up2hOcYVyOaM1SjASBgNVHRMBAf8ECDAGAQH/AgEAMA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQsFAAOBgQBI8J1bKh/e+uEZtfngKMADS1PSHztAPFFKXgeIfYeJDRznnamtbheensdxrA+aoriJbJfHxmjecr4xA8+s0uN9GPtQ3+ad1K5Sg6mfzsXtNPf3xa9y0pIWOGZavr1s/QugoPLQxEiuHrvkHX5+sZlx47KoBQJ8LBRmJydeSvxz1g==");
    static byte[] extInvV2EE = Base64.decode("MIICJjCCAY+gAwIBAQIGAV3Y2FWDMA0GCSqGSIb3DQEBCwUAMBExDzANBgNVBAMMBktQMSBDQTAeFw0xNzA4MTIyMzQ3MThaFw0xNzA4MTMwMDE3MjNaMBExDzANBgNVBAMMBktQMSBFRTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAzWXxtMpnjz8Q1qTdwpB66W2D0vEHhqow2PTsvfQdENL4AFESE1C7Cj3lLBTei1vRHCnpM0jdNghBW8k/u2b2tqeeWLBqwul0tEGbjtUwkYV2WgtTGmiYZZFfMH35HIvqlZMwIIdZqz4lEdkPiAPEUOELvycpVDFnWjF0qah5LqsCAwEAAaOBiDCBhTBABgNVHSMEOTA3gBTsZ2B5JbgKm9/up2hOcYVyOaM1SqEXpBUwEzERMA8GA1UEAwwIS1AxIFJPT1SCBgFd2NhVgjAdBgNVHQ4EFgQUfeKdn63Gmlkub8m8bwjqJook5ywwEgYDVR0TAQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwDQYJKoZIhvcNAQELBQADgYEAco35KYLE683l53J6V1q2tcMV3EpM39tifkL7Kl38oX9d3SGiKkEO6YFeQekRyto0Z91mPq7Pe/oOfDrfsY3r9KX7oqnhOKBnnR/58atM9udVLvuLfCJpxiroAldSkhRKvHG5MrFwZyDcVkTZF4GDrP6bojp32wVfU5EYkfwcJN8=");

    static byte[] extInvVersionTrust = Base64.decode("MIIBmjCCAQMCBgFd2RZiPjANBgkqhkiG9w0BAQsFADATMREwDwYDVQQDDAhLUDEgUk9PVDAeFw0xNzA4MTMwMDU1MDRaFw0xNzA4MTMwMTI1MDlaMBMxETAPBgNVBAMMCEtQMSBST09UMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCPn6zObnrjGPUb0ozc3MCOOcHwtQABZmUHtB1jxRXWwYXKo+iTms2wJjDS5fhz2UmUptsbdFwPdvT2t7K8cpaZBcovC3jLvEAMmjO+nU3FQrdopZ6MhBjpgIezAvJ9LUhrYctqUJzfViqtLl0dL+YRjaVdfCz5z0iZn4rv2VSf3QIDAQABMA0GCSqGSIb3DQEBCwUAA4GBAHtS9RjMIRzeEpH9MKIaMLR7wVb55MYW7E2CVuIbsHmT+KyDBFsYbAylgc76cH1b8F53ECygS9jCpzfKtO61WVPPlUhsL13i2XbzCtj8DSPXaW5pgvpwClQZ+dpGFz8D/MYLSdjTdls8dbhJ5O08ckSKcrIGHcF90oeepVXOmiTw");
    static byte[] extInvVersionCA = Base64.decode(    "MIICKDCCAZGgAwIBAgIGAV3ZFmI/MA0GCSqGSIb3DQEBCwUAMBMxETAPBgNVBAMMCEtQMSBST09UMB4XDTE3MDgxMzAwNTUwNFoXDTE3MDgxMzAxMjUwOVowETEPMA0GA1UEAwwGS1AxIENBMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQChlaZhX9/eHmtfravHzs/E0g6ZhWTmD9aNNvuuz/GCBF9AMS6QQCGVhEzxESn0gLzs1bM/9M/EaylHS3Ecvi6QYdkrTKRDj38FDzrDhiPlM3TxY0XuUQ3Py590k8yZDcuEeVEQeoUx83qOnO7o/cL+vECfMj9ImYFFgY5sMcKkVQIDAQABo4GIMIGFMEAGA1UdIwQ5MDeAFAfTyJtmNkinVjfd7/2Giy6krDTpoRekFTATMREwDwYDVQQDDAhLUDEgUk9PVIIGAV3ZFmI+MB0GA1UdDgQWBBQkMq+wajXvKQaJtSdpvDJn77bU9zASBgNVHRMBAf8ECDAGAQH/AgEAMA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQsFAAOBgQAqmKtykmixemAvppo2tTmekLsL93+/DMR+oz1iK2rjhqYzEF1/pM9VUyG+Ni1924U8tzGbXv2lL3MiToRSyjO50HHfnE7PfOvNiTUj73PTn27tPl03eWO3CtsOTGxtE2vpNyXyFXm4SFZlSicOXE0o/kUrNGVYvnjs/jjcNlPiHQ==");
    static byte[] extInvVersionEE = Base64.decode(   "MIICJjCCAY+gAwIBBQIGAV3ZFmJAMA0GCSqGSIb3DQEBCwUAMBExDzANBgNVBAMMBktQMSBDQTAeFw0xNzA4MTMwMDU1MDRaFw0xNzA4MTMwMTI1MDlaMBExDzANBgNVBAMMBktQMSBFRTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA6PXdCOvE33+mMcal/rC+I7zdJqcc6OBhn+Lyku29TRcYplMA5mkh7WkjLtRYBUAzHukN/GXb1Mo+dFkvCnKO/l4gLWyVuf23rL6iELt8X1KVJdJlrDElCmTgl6lA0Omq7QhNrsv5Vdk7mK2mbJzl0bj4fcu5dc23nQXEskmGrZsCAwEAAaOBiDCBhTBABgNVHSMEOTA3gBQkMq+wajXvKQaJtSdpvDJn77bU96EXpBUwEzERMA8GA1UEAwwIS1AxIFJPT1SCBgFd2RZiPzAdBgNVHQ4EFgQU3Nw/DFxNqK1fRhc/W8W4o3mkCHQwEgYDVR0TAQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwDQYJKoZIhvcNAQELBQADgYEAjMTiKgLC2Kb5+elvfD/+CM8pNeLt5Y43sMSTpgIrebdWPA2hyvjvW/upsIYIquGrymOYBU/K0abQlkNUbBHpQCQMPQ6iPXuhTQj/P7rt7McLl6OXV/DQqgF+39y0xWAzoZbgMKrQaSr9oRmEVt6xzLM92JS67w8Xgbh39PGBfEg=");

    static byte[] extInvExtTrust = Base64.decode("MIIBmjCCAQMCBgFd2SFqKjANBgkqhkiG9w0BAQsFADATMREwDwYDVQQDDAhLUDEgUk9PVDAeFw0xNzA4MTMwMTA3MDdaFw0xNzA4MTMwMTM3MTJaMBMxETAPBgNVBAMMCEtQMSBST09UMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDEY3toxiphhoeoTd79/Uznb1YyKjYgxtXkYVQLZ+Q76bJFQftVVcUHw25/A/2qgSc8XPflGRpn82Qn/B7s3fxEglgeY0ekdYjea5+jZSJj70p1QcC60yH1NKGxE0ASBuv/22IoHhdu5dOTmiWegikKUXblBD1wAxbbvOcXFs2x/wIDAQABMA0GCSqGSIb3DQEBCwUAA4GBAJPG9wt9edpTaCc0z03xGNF/M6x5cLx5eLgZaBFt+FO3S1xWIVby+iU8Hw2mzHOc58Fghw1jEwLaslQYadx9667NedGu7dYyY318h+VhaDppQqkhJiQl5Q8aTvVNt60fDEVLjvB7E6Z+CafVGR1jNrXxLDe6zVf/BZJK7QrkTKh4");
    static byte[] extInvExtCA = Base64.decode("MIICKDCCAZGgAwIBAgIGAV3ZIWorMA0GCSqGSIb3DQEBCwUAMBMxETAPBgNVBAMMCEtQMSBST09UMB4XDTE3MDgxMzAxMDcwN1oXDTE3MDgxMzAxMzcxMlowETEPMA0GA1UEAwwGS1AxIENBMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCJKySmanEENpLJdPwpM/v0I7H0bW9ZlIxRpiL+/Z4uvF3j0r0O42Tm+dW8Ub42DzHcQ8pK/n/k2Wb4Jf7cP8+TGTAne3bgC24USW131XUZxaunGt4tCqZ0RNWpmBQUcUM0lgntDSfcvyv3QFB+nwLc93GYij9l3FaeUcHkwFiKsQIDAQABo4GIMIGFMEAGA1UdIwQ5MDeAFLnC9UF+JqEqboFH84ab9dEAkwEBoRekFTATMREwDwYDVQQDDAhLUDEgUk9PVIIGAV3ZIWoqMB0GA1UdDgQWBBQkr/0UP1MKPGQH7bkRNctHMsVQsjASBgNVHRMBAf8ECDAGAQH/AgEAMA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQsFAAOBgQCZxLwkAPif1H2P398MHK3NLf3mrmLsP41ZphdHnSLNROlY9PdO5I/dfhElzVXW2oxecIIKbOQsjZe0FOSGvZHEhLftQmOdfGc5QfGf5w9CSFCCBe5vHdMjglRLVhNB51jz6DB7Dp0MjFDgkQI4lBHaiMVkE+HUZjNLwBddHH58Sw==");
    static byte[] extInvExtEE = Base64.decode("MIICNjCCAZ+gAwIBAgIGAV3ZIWosMA0GCSqGSIb3DQEBCwUAMBExDzANBgNVBAMMBktQMSBDQTAeFw0xNzA4MTMwMTA3MDdaFw0xNzA4MTMwMTM3MTJaMBExDzANBgNVBAMMBktQMSBFRTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAj6WOoo8xHLLo+CT0l288xZDK3OsF64lPfNVkFnrRI65Ywl89M19nNF5Q24hF1FS6getO5oU+BhvRqft1/De22SME9SzKqs3G6uMxACKrMqgni1QBEOC/DdZ5Uaxh2s4lEgxbN0PQZIarAgLtAIgzRM4CrvofxFMwQy/neUuWmeMCAwEAAaOBmDCBlTBABgNVHSMEOTA3gBQkr/0UP1MKPGQH7bkRNctHMsVQsqEXpBUwEzERMA8GA1UEAwwIS1AxIFJPT1SCBgFd2SFqKzAdBgNVHQ4EFgQU/yuQXlvqXJQsbqB6whCPu5bwFCAwEgYDVR0TAQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEBCwUAA4GBABYUGar9s7wlM3Qlnja7uc7U8FqU+xH4e8/Jk64ku7DdwXelEbKo/FTFAzh464aiFP4eMDOH7YThXyTruPudEAvYyWY7eaEgRqA2MmL0uWHSrN+HR9aBeqrMCJK/E2e1egvk2whJHMimhDUFJ3cIPsFhazMvLTnVgWGMjOqQtuP+");

    private void checkInvalidCertPath()
        throws Exception
    {
        checkInvalidPath(extInvTrust, extInvCA, extInvEE, "version 1 certificate contains extra data");
        checkInvalidPath(extInvV2Trust, extInvV2CA, extInvV2EE, "version 2 certificate cannot contain extensions");
        checkInvalidPath(extInvVersionTrust, extInvVersionCA, extInvVersionEE, "version number not recognised");
        checkInvalidPath(extInvExtTrust, extInvExtCA, extInvExtEE, "repeated extension found: 2.5.29.15");
    }

    private void checkInvalidPath(byte[] root, byte[] inter, byte[] ee, String expected)
        throws Exception
    {
        X509Certificate rootCert = new X509CertificateObject(new DodgyCertificate(root));
        X509Certificate interCert = new X509CertificateObject(new DodgyCertificate(inter));
        X509Certificate finalCert = new X509CertificateObject(new DodgyCertificate(ee));
        List list = new ArrayList();
        list.add(rootCert);
        list.add(interCert);
        list.add(finalCert);

        CollectionCertStoreParameters ccsp = new CollectionCertStoreParameters(list);
        CertStore store = CertStore.getInstance("Collection", ccsp, "BC");
        Date validDate = new Date(rootCert.getNotBefore().getTime() + 60 * 1000);
        //validating path
        List certchain = new ArrayList();
        certchain.add(finalCert);
        certchain.add(interCert);
        CertPath cp = CertificateFactory.getInstance("X.509", "BC").generateCertPath(certchain);
        Set trust = new HashSet();
        trust.add(new TrustAnchor(rootCert, null));

        CertPathValidator cpv = CertPathValidator.getInstance("PKIX", "BC");
        PKIXParameters param = new PKIXParameters(trust);
        param.addCertStore(store);
        param.setDate(validDate);
        param.setRevocationEnabled(false);

        try
        {
            PKIXCertPathValidatorResult result = (PKIXCertPathValidatorResult)cpv.validate(cp, param);
            fail("valid path passed");
        }
        catch (CertPathValidatorException e)
        {
            isTrue(e.getMessage().equals(expected));
        }

        // check that our cert factory also rejects - the EE is always the invalid one
        CertificateFactory cf = CertificateFactory.getInstance("X.509", "BC");

        try
        {
            cf.generateCertificate(new ByteArrayInputStream(ee));
        }
        catch (CertificateException e)
        {
            isTrue(e.getMessage().equals("parsing issue: " + expected));
        }
    }

    public String getName()
    {
        return "CertPathValidator";
    }

    public static void main(
        String[] args)
    {
        Security.addProvider(new BouncyCastleProvider());

        runTest(new CertPathValidatorTest());
    }

    private static class MyChecker
        extends PKIXCertPathChecker
    {
        private static int count;

        public void init(boolean forward)
            throws CertPathValidatorException
        {
            //To change body of implemented methods use File | Settings | File Templates.
        }

        public boolean isForwardCheckingSupported()
        {
            return true;
        }

        public Set getSupportedExtensions()
        {
            return null;
        }

        public void check(Certificate cert, Collection unresolvedCritExts)
            throws CertPathValidatorException
        {
            count++;
        }

        public int getCount()
        {
            return count;
        }
    }

    public static class X509CertificateObject
        extends X509Certificate
    {
        static final String CERTIFICATE_POLICIES = Extension.certificatePolicies.getId();
        static final String POLICY_MAPPINGS = Extension.policyMappings.getId();
        static final String INHIBIT_ANY_POLICY = Extension.inhibitAnyPolicy.getId();
        static final String ISSUING_DISTRIBUTION_POINT = Extension.issuingDistributionPoint.getId();
        static final String FRESHEST_CRL = Extension.freshestCRL.getId();
        static final String DELTA_CRL_INDICATOR = Extension.deltaCRLIndicator.getId();
        static final String POLICY_CONSTRAINTS = Extension.policyConstraints.getId();
        static final String BASIC_CONSTRAINTS = Extension.basicConstraints.getId();
        static final String CRL_DISTRIBUTION_POINTS = Extension.cRLDistributionPoints.getId();
        static final String SUBJECT_ALTERNATIVE_NAME = Extension.subjectAlternativeName.getId();
        static final String NAME_CONSTRAINTS = Extension.nameConstraints.getId();
        static final String AUTHORITY_KEY_IDENTIFIER = Extension.authorityKeyIdentifier.getId();
        static final String KEY_USAGE = Extension.keyUsage.getId();
        static final String CRL_NUMBER = Extension.cRLNumber.getId();
        static final String ANY_POLICY = "2.5.29.32.0";

        private DodgyCertificate c;
        private BasicConstraints basicConstraints;
        private boolean[] keyUsage;
        private boolean hashValueSet;
        private int hashValue;

        public X509CertificateObject(
            DodgyCertificate c)
            throws CertificateParsingException
        {
            this.c = c;

            try
            {
                byte[] bytes = this.getExtensionBytes("2.5.29.19");

                if (bytes != null)
                {
                    basicConstraints = BasicConstraints.getInstance(ASN1Primitive.fromByteArray(bytes));
                }
            }
            catch (Exception e)
            {
                throw new CertificateParsingException("cannot construct BasicConstraints: " + e);
            }

            try
            {
                byte[] bytes = this.getExtensionBytes("2.5.29.15");
                if (bytes != null)
                {
                    ASN1BitString bits = ASN1BitString.getInstance(ASN1Primitive.fromByteArray(bytes));

                    bytes = bits.getBytes();
                    int length = (bytes.length * 8) - bits.getPadBits();

                    keyUsage = new boolean[(length < 9) ? 9 : length];

                    for (int i = 0; i != length; i++)
                    {
                        keyUsage[i] = (bytes[i / 8] & (0x80 >>> (i % 8))) != 0;
                    }
                }
                else
                {
                    keyUsage = null;
                }
            }
            catch (Exception e)
            {
                throw new CertificateParsingException("cannot construct KeyUsage: " + e);
            }
        }

        public void checkValidity()
            throws CertificateExpiredException, CertificateNotYetValidException
        {
            this.checkValidity(new Date());
        }

        public void checkValidity(
            Date date)
            throws CertificateExpiredException, CertificateNotYetValidException
        {
            if (date.getTime() > this.getNotAfter().getTime())  // for other VM compatibility
            {
                throw new CertificateExpiredException("certificate expired on " + c.getEndDate().getTime());
            }

            if (date.getTime() < this.getNotBefore().getTime())
            {
                throw new CertificateNotYetValidException("certificate not valid till " + c.getStartDate().getTime());
            }
        }

        public int getVersion()
        {
            return c.getVersion().intValueExact();
        }

        public BigInteger getSerialNumber()
        {
            return c.getSerialNumber().getValue();
        }

        public Principal getIssuerDN()
        {
            return getIssuerX500Principal();
        }

        public X500Principal getIssuerX500Principal()
        {
            try
            {
                return new X500Principal(c.getIssuer().getEncoded());
            }
            catch (IOException e)
            {
                throw new IllegalStateException("can't encode issuer DN");
            }
        }

        public Principal getSubjectDN()
        {
            return getSubjectX500Principal();
        }

        public X500Principal getSubjectX500Principal()
        {
            try
            {
                return new X500Principal(c.getSubject().getEncoded());
            }
            catch (IOException e)
            {
                throw new IllegalStateException("can't encode issuer DN");
            }
        }

        public Date getNotBefore()
        {
            return c.getStartDate().getDate();
        }

        public Date getNotAfter()
        {
            return c.getEndDate().getDate();
        }

        public byte[] getTBSCertificate()
            throws CertificateEncodingException
        {
            try
            {
                return c.getTBSCertificate().getEncoded(ASN1Encoding.DER);
            }
            catch (IOException e)
            {
                throw new CertificateEncodingException(e.toString());
            }
        }

        public byte[] getSignature()
        {
            return c.getSignature().getOctets();
        }

        /**
         * return a more "meaningful" representation for the signature algorithm used in
         * the certficate.
         */
        public String getSigAlgName()
        {
            Provider prov = Security.getProvider(BouncyCastleProvider.PROVIDER_NAME);

            if (prov != null)
            {
                String algName = prov.getProperty("Alg.Alias.Signature." + this.getSigAlgOID());

                if (algName != null)
                {
                    return algName;
                }
            }

            Provider[] provs = Security.getProviders();

            //
            // search every provider looking for a real algorithm
            //
            for (int i = 0; i != provs.length; i++)
            {
                String algName = provs[i].getProperty("Alg.Alias.Signature." + this.getSigAlgOID());
                if (algName != null)
                {
                    return algName;
                }
            }

            return this.getSigAlgOID();
        }

        /**
         * return the object identifier for the signature.
         */
        public String getSigAlgOID()
        {
            return c.getSignatureAlgorithm().getAlgorithm().getId();
        }

        /**
         * return the signature parameters, or null if there aren't any.
         */
        public byte[] getSigAlgParams()
        {
            if (c.getSignatureAlgorithm().getParameters() != null)
            {
                try
                {
                    return c.getSignatureAlgorithm().getParameters().toASN1Primitive().getEncoded(ASN1Encoding.DER);
                }
                catch (IOException e)
                {
                    return null;
                }
            }
            else
            {
                return null;
            }
        }

        public boolean[] getIssuerUniqueID()
        {
            ASN1BitString id = c.getTBSCertificate().getIssuerUniqueId();

            if (id != null)
            {
                byte[] bytes = id.getBytes();
                boolean[] boolId = new boolean[bytes.length * 8 - id.getPadBits()];

                for (int i = 0; i != boolId.length; i++)
                {
                    boolId[i] = (bytes[i / 8] & (0x80 >>> (i % 8))) != 0;
                }

                return boolId;
            }

            return null;
        }

        public boolean[] getSubjectUniqueID()
        {
            ASN1BitString id = c.getTBSCertificate().getSubjectUniqueId();

            if (id != null)
            {
                byte[] bytes = id.getBytes();
                boolean[] boolId = new boolean[bytes.length * 8 - id.getPadBits()];

                for (int i = 0; i != boolId.length; i++)
                {
                    boolId[i] = (bytes[i / 8] & (0x80 >>> (i % 8))) != 0;
                }

                return boolId;
            }

            return null;
        }

        public boolean[] getKeyUsage()
        {
            return keyUsage;
        }

        public List getExtendedKeyUsage()
            throws CertificateParsingException
        {
            byte[] bytes = this.getExtensionBytes("2.5.29.37");

            if (bytes != null)
            {
                try
                {
                    ASN1InputStream dIn = new ASN1InputStream(bytes);
                    ASN1Sequence seq = (ASN1Sequence)dIn.readObject();
                    List list = new ArrayList();

                    for (int i = 0; i != seq.size(); i++)
                    {
                        list.add(((ASN1ObjectIdentifier)seq.getObjectAt(i)).getId());
                    }

                    return Collections.unmodifiableList(list);
                }
                catch (Exception e)
                {
                    throw new CertificateParsingException("error processing extended key usage extension");
                }
            }

            return null;
        }

        public int getBasicConstraints()
        {
            if (basicConstraints == null || !basicConstraints.isCA())
            {
                return -1;
            }

            ASN1Integer pathLenConstraint = basicConstraints.getPathLenConstraintInteger();
            if (pathLenConstraint == null)
            {
                return Integer.MAX_VALUE;
            }

            return pathLenConstraint.intPositiveValueExact();
        }

        public Collection getSubjectAlternativeNames()
            throws CertificateParsingException
        {
            return getAlternativeNames(getExtensionBytes(Extension.subjectAlternativeName.getId()));
        }

        public Collection getIssuerAlternativeNames()
            throws CertificateParsingException
        {
            return getAlternativeNames(getExtensionBytes(Extension.issuerAlternativeName.getId()));
        }

        public Set getCriticalExtensionOIDs()
        {
            if (this.getVersion() == 3)
            {
                Set set = new HashSet();
                DodgyExtensions extensions = c.getTBSCertificate().getExtensions();

                if (extensions != null)
                {
                    Enumeration e = extensions.oids();

                    while (e.hasMoreElements())
                    {
                        ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier)e.nextElement();
                        Extension ext = extensions.getExtension(oid);

                        if (ext.isCritical())
                        {
                            set.add(oid.getId());
                        }
                    }

                    return set;
                }
            }

            return null;
        }

        private byte[] getExtensionBytes(String oid)
        {
            DodgyExtensions exts = c.getTBSCertificate().getExtensions();

            if (exts != null)
            {
                Extension ext = exts.getExtension(new ASN1ObjectIdentifier(oid));
                if (ext != null)
                {
                    return ext.getExtnValue().getOctets();
                }
            }

            return null;
        }

        public byte[] getExtensionValue(String oid)
        {
            DodgyExtensions  exts = c.getTBSCertificate().getExtensions();

            if (exts != null)
            {
                Extension ext = exts.getExtension(new ASN1ObjectIdentifier(oid));

                if (ext != null)
                {
                    try
                    {
                        return ext.getExtnValue().getEncoded();
                    }
                    catch (Exception e)
                    {
                        throw new IllegalStateException("error parsing " + e.toString());
                    }
                }
            }

            return null;
        }

        public Set getNonCriticalExtensionOIDs()
        {
            if (this.getVersion() == 3)
            {
                Set set = new HashSet();
                DodgyExtensions  extensions = c.getTBSCertificate().getExtensions();

                if (extensions != null)
                {
                    Enumeration e = extensions.oids();

                    while (e.hasMoreElements())
                    {
                        ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier)e.nextElement();
                        Extension ext = extensions.getExtension(oid);

                        if (!ext.isCritical())
                        {
                            set.add(oid.getId());
                        }
                    }

                    return set;
                }
            }

            return null;
        }

        public boolean hasUnsupportedCriticalExtension()
        {
            if (this.getVersion() == 3)
            {
                DodgyExtensions  extensions = c.getTBSCertificate().getExtensions();

                if (extensions != null)
                {
                    Enumeration e = extensions.oids();

                    while (e.hasMoreElements())
                    {
                        ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier)e.nextElement();
                        String oidId = oid.getId();

                        if (oidId.equals(KEY_USAGE)
                            || oidId.equals(CERTIFICATE_POLICIES)
                            || oidId.equals(POLICY_MAPPINGS)
                            || oidId.equals(INHIBIT_ANY_POLICY)
                            || oidId.equals(CRL_DISTRIBUTION_POINTS)
                            || oidId.equals(ISSUING_DISTRIBUTION_POINT)
                            || oidId.equals(DELTA_CRL_INDICATOR)
                            || oidId.equals(POLICY_CONSTRAINTS)
                            || oidId.equals(BASIC_CONSTRAINTS)
                            || oidId.equals(SUBJECT_ALTERNATIVE_NAME)
                            || oidId.equals(NAME_CONSTRAINTS))
                        {
                            continue;
                        }

                        Extension ext = extensions.getExtension(oid);

                        if (ext.isCritical())
                        {
                            return true;
                        }
                    }
                }
            }

            return false;
        }

        public PublicKey getPublicKey()
        {
            try
            {
                return BouncyCastleProvider.getPublicKey(c.getSubjectPublicKeyInfo());
            }
            catch (IOException e)
            {
                return null;   // should never happen...
            }
        }

        public byte[] getEncoded()
            throws CertificateEncodingException
        {
            try
            {
                return c.getEncoded(ASN1Encoding.DER);
            }
            catch (IOException e)
            {
                throw new CertificateEncodingException(e.toString());
            }
        }

        public boolean equals(
            Object o)
        {
            if (o == this)
            {
                return true;
            }

            if (!(o instanceof Certificate))
            {
                return false;
            }

            Certificate other = (Certificate)o;

            try
            {
                byte[] b1 = this.getEncoded();
                byte[] b2 = other.getEncoded();

                return Arrays.areEqual(b1, b2);
            }
            catch (CertificateEncodingException e)
            {
                return false;
            }
        }

        public synchronized int hashCode()
        {
            if (!hashValueSet)
            {
                hashValue = calculateHashCode();
                hashValueSet = true;
            }

            return hashValue;
        }

        private int calculateHashCode()
        {
            try
            {
                int hashCode = 0;
                byte[] certData = this.getEncoded();
                for (int i = 1; i < certData.length; i++)
                {
                    hashCode += certData[i] * i;
                }
                return hashCode;
            }
            catch (CertificateEncodingException e)
            {
                return 0;
            }
        }

        public String toString()
        {
            StringBuffer buf = new StringBuffer();
            String nl = Strings.lineSeparator();

            buf.append("  [0]         Version: ").append(this.getVersion()).append(nl);
            buf.append("         SerialNumber: ").append(this.getSerialNumber()).append(nl);
            buf.append("             IssuerDN: ").append(this.getIssuerDN()).append(nl);
            buf.append("           Start Date: ").append(this.getNotBefore()).append(nl);
            buf.append("           Final Date: ").append(this.getNotAfter()).append(nl);
            buf.append("            SubjectDN: ").append(this.getSubjectDN()).append(nl);
            buf.append("           Public Key: ").append(this.getPublicKey()).append(nl);
            buf.append("  Signature Algorithm: ").append(this.getSigAlgName()).append(nl);

            byte[] sig = this.getSignature();

            buf.append("            Signature: ").append(new String(Hex.encode(sig, 0, 20))).append(nl);
            for (int i = 20; i < sig.length; i += 20)
            {
                if (i < sig.length - 20)
                {
                    buf.append("                       ").append(new String(Hex.encode(sig, i, 20))).append(nl);
                }
                else
                {
                    buf.append("                       ").append(new String(Hex.encode(sig, i, sig.length - i))).append(nl);
                }
            }

            DodgyExtensions extensions = c.getTBSCertificate().getExtensions();

            if (extensions != null)
            {
                Enumeration e = extensions.oids();

                if (e.hasMoreElements())
                {
                    buf.append("       Extensions: \n");
                }

                while (e.hasMoreElements())
                {
                    ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier)e.nextElement();
                    Extension ext = ((DodgyExtensions)extensions).getExtension(oid);

                    if (ext.getExtnValue() != null)
                    {
                        byte[] octs = ext.getExtnValue().getOctets();
                        ASN1InputStream dIn = new ASN1InputStream(octs);
                        buf.append("                       critical(").append(ext.isCritical()).append(") ");
                        try
                        {
                            if (oid.equals(Extension.basicConstraints))
                            {
                                buf.append(BasicConstraints.getInstance(dIn.readObject())).append(nl);
                            }
                            else if (oid.equals(Extension.keyUsage))
                            {
                                buf.append(KeyUsage.getInstance(dIn.readObject())).append(nl);
                            }
                            else if (oid.equals(MiscObjectIdentifiers.netscapeCertType))
                            {
                                buf.append(new NetscapeCertType((ASN1BitString)dIn.readObject())).append(nl);
                            }
                            else if (oid.equals(MiscObjectIdentifiers.netscapeRevocationURL))
                            {
                                buf.append(new NetscapeRevocationURL((ASN1IA5String)dIn.readObject())).append(nl);
                            }
                            else if (oid.equals(MiscObjectIdentifiers.verisignCzagExtension))
                            {
                                buf.append(new VerisignCzagExtension((ASN1IA5String)dIn.readObject())).append(nl);
                            }
                            else
                            {
                                buf.append(oid.getId());
                                buf.append(" value = ").append(ASN1Dump.dumpAsString(dIn.readObject())).append(nl);
                                //buf.append(" value = ").append("*****").append(nl);
                            }
                        }
                        catch (Exception ex)
                        {
                            buf.append(oid.getId());
                            //     buf.append(" value = ").append(new String(Hex.encode(ext.getExtnValue().getOctets()))).append(nl);
                            buf.append(" value = ").append("*****").append(nl);
                        }
                    }
                    else
                    {
                        buf.append(nl);
                    }
                }
            }

            return buf.toString();
        }

        public final void verify(
            PublicKey key)
            throws CertificateException, NoSuchAlgorithmException,
            InvalidKeyException, NoSuchProviderException, SignatureException
        {
            Signature signature;
            String sigName = "SHA256withRSA";

            try
            {
                signature = Signature.getInstance(sigName, BouncyCastleProvider.PROVIDER_NAME);
            }
            catch (Exception e)
            {
                signature = Signature.getInstance(sigName);
            }

            checkSignature(key, signature);
        }

        public final void verify(
            PublicKey key,
            String sigProvider)
            throws CertificateException, NoSuchAlgorithmException,
            InvalidKeyException, NoSuchProviderException, SignatureException
        {
            String sigName = "SHA256withRSA";
            Signature signature;

            if (sigProvider != null)
            {
                signature = Signature.getInstance(sigName, sigProvider);
            }
            else
            {
                signature = Signature.getInstance(sigName);
            }

            checkSignature(key, signature);
        }

        public final void verify(
            PublicKey key,
            Provider sigProvider)
            throws CertificateException, NoSuchAlgorithmException,
            InvalidKeyException, SignatureException
        {
            String sigName = "SHA256withRSA";
            Signature signature;

            if (sigProvider != null)
            {
                signature = Signature.getInstance(sigName, sigProvider);
            }
            else
            {
                signature = Signature.getInstance(sigName);
            }

            checkSignature(key, signature);
        }

        private void checkSignature(
            PublicKey key,
            Signature signature)
            throws CertificateException, NoSuchAlgorithmException,
            SignatureException, InvalidKeyException
        {
            if (!isAlgIdEqual(c.getSignatureAlgorithm(), c.getTBSCertificate().getSignature()))
            {
                throw new CertificateException("signature algorithm in TBS cert not same as outer cert");
            }

            ASN1Encodable params = c.getSignatureAlgorithm().getParameters();

            signature.initVerify(key);

            signature.update(this.getTBSCertificate());

            if (!signature.verify(this.getSignature()))
            {
                throw new SignatureException("certificate does not verify with supplied key");
            }
        }

        private boolean isAlgIdEqual(AlgorithmIdentifier id1, AlgorithmIdentifier id2)
        {
            if (!id1.getAlgorithm().equals(id2.getAlgorithm()))
            {
                return false;
            }

            if (id1.getParameters() == null)
            {
                if (id2.getParameters() != null && !id2.getParameters().equals(DERNull.INSTANCE))
                {
                    return false;
                }

                return true;
            }

            if (id2.getParameters() == null)
            {
                if (id1.getParameters() != null && !id1.getParameters().equals(DERNull.INSTANCE))
                {
                    return false;
                }

                return true;
            }

            return id1.getParameters().equals(id2.getParameters());
        }

        private static Collection getAlternativeNames(byte[] extVal)
            throws CertificateParsingException
        {
            if (extVal == null)
            {
                return null;
            }
            try
            {
                Collection temp = new ArrayList();
                Enumeration it = ASN1Sequence.getInstance(extVal).getObjects();
                while (it.hasMoreElements())
                {
                    GeneralName genName = GeneralName.getInstance(it.nextElement());
                    List list = new ArrayList();
                    list.add(Integers.valueOf(genName.getTagNo()));
                    switch (genName.getTagNo())
                    {
                    case GeneralName.ediPartyName:
                    case GeneralName.x400Address:
                    case GeneralName.otherName:
                        list.add(genName.getEncoded());
                        break;
                    case GeneralName.directoryName:
                        list.add(X500Name.getInstance(RFC4519Style.INSTANCE, genName.getName()).toString());
                        break;
                    case GeneralName.dNSName:
                    case GeneralName.rfc822Name:
                    case GeneralName.uniformResourceIdentifier:
                        list.add(((ASN1String)genName.getName()).getString());
                        break;
                    case GeneralName.registeredID:
                        list.add(ASN1ObjectIdentifier.getInstance(genName.getName()).getId());
                        break;
                    case GeneralName.iPAddress:
                        byte[] addrBytes = DEROctetString.getInstance(genName.getName()).getOctets();
                        final String addr;
                        try
                        {
                            addr = InetAddress.getByAddress(addrBytes).getHostAddress();
                        }
                        catch (UnknownHostException e)
                        {
                            continue;
                        }
                        list.add(addr);
                        break;
                    default:
                        throw new IOException("Bad tag number: " + genName.getTagNo());
                    }

                    temp.add(Collections.unmodifiableList(list));
                }
                if (temp.size() == 0)
                {
                    return null;
                }
                return Collections.unmodifiableCollection(temp);
            }
            catch (Exception e)
            {
                throw new CertificateParsingException(e.getMessage());
            }
        }
    }

    private class DodgyCertificate
        extends ASN1Object
    {
        ASN1Sequence  seq;
        DodgyTBSCertificate tbsCert;
        AlgorithmIdentifier     sigAlgId;
        ASN1BitString            sig;

        DodgyCertificate(
            byte[] encoding)
        {
            this.seq = ASN1Sequence.getInstance(encoding);

            //
            // correct x509 certficate
            //
            if (seq.size() == 3)
            {
                tbsCert = new DodgyTBSCertificate(ASN1Sequence.getInstance(seq.getObjectAt(0)));
                sigAlgId = AlgorithmIdentifier.getInstance(seq.getObjectAt(1));

                sig = ASN1BitString.getInstance(seq.getObjectAt(2));
            }
            else
            {
                throw new IllegalArgumentException("sequence wrong size for a certificate");
            }
        }

        public DodgyTBSCertificate getTBSCertificate()
        {
            return tbsCert;
        }

        public ASN1Integer getVersion()
        {
            return tbsCert.getVersion();
        }

        public int getVersionNumber()
        {
            return tbsCert.getVersionNumber();
        }

        public ASN1Integer getSerialNumber()
        {
            return tbsCert.getSerialNumber();
        }

        public X500Name getIssuer()
        {
            return tbsCert.getIssuer();
        }

        public Time getStartDate()
        {
            return tbsCert.getStartDate();
        }

        public Time getEndDate()
        {
            return tbsCert.getEndDate();
        }

        public X500Name getSubject()
        {
            return tbsCert.getSubject();
        }

        public SubjectPublicKeyInfo getSubjectPublicKeyInfo()
        {
            return tbsCert.getSubjectPublicKeyInfo();
        }

        public AlgorithmIdentifier getSignatureAlgorithm()
        {
            return sigAlgId;
        }

        public ASN1BitString getSignature()
        {
            return sig;
        }

        public ASN1Primitive toASN1Primitive()
        {
            return seq;
        }
    }

    private class DodgyTBSCertificate
        extends ASN1Object
    {
        ASN1Sequence            seq;

        ASN1Integer             version;
        ASN1Integer             serialNumber;
        AlgorithmIdentifier     signature;
        X500Name                issuer;
        Time                    startDate, endDate;
        X500Name                subject;
        SubjectPublicKeyInfo    subjectPublicKeyInfo;
        ASN1BitString           issuerUniqueId;
        ASN1BitString           subjectUniqueId;
        DodgyExtensions         extensions;

        private DodgyTBSCertificate(
            ASN1Sequence seq)
        {
            int         seqStart = 0;

            this.seq = seq;

            //
            // some certficates don't include a version number - we assume v1
            //
            if (seq.getObjectAt(0) instanceof ASN1TaggedObject)
            {
                version = ASN1Integer.getInstance((ASN1TaggedObject)seq.getObjectAt(0), true);
            }
            else
            {
                seqStart = -1;          // field 0 is missing!
                version = new ASN1Integer(0);
            }

            boolean isV1 = false;
            boolean isV2 = false;

            if (version.hasValue(0))
            {
                isV1 = true;
            }
            else if (version.hasValue(1))
            {
                isV2 = true;
            }
//            else if (!version.hasValue(2))
//            {
//                throw new IllegalArgumentException("version number not recognised");
//            }

            serialNumber = ASN1Integer.getInstance(seq.getObjectAt(seqStart + 1));

            signature = AlgorithmIdentifier.getInstance(seq.getObjectAt(seqStart + 2));
            issuer = X500Name.getInstance(seq.getObjectAt(seqStart + 3));

            //
            // before and after dates
            //
            ASN1Sequence  dates = (ASN1Sequence)seq.getObjectAt(seqStart + 4);

            startDate = Time.getInstance(dates.getObjectAt(0));
            endDate = Time.getInstance(dates.getObjectAt(1));

            subject = X500Name.getInstance(seq.getObjectAt(seqStart + 5));

            //
            // public key info.
            //
            subjectPublicKeyInfo = SubjectPublicKeyInfo.getInstance(seq.getObjectAt(seqStart + 6));

            int extras = seq.size() - (seqStart + 6) - 1;
//            if (extras != 0 && isV1)
//            {
//                throw new IllegalArgumentException("version 1 certificate contains extra data");
//            }

            while (extras > 0)
            {
                ASN1TaggedObject extra = (ASN1TaggedObject)seq.getObjectAt(seqStart + 6 + extras);

                switch (extra.getTagNo())
                {
                case 1:
                    issuerUniqueId = ASN1BitString.getInstance(extra, false);
                    break;
                case 2:
                    subjectUniqueId = ASN1BitString.getInstance(extra, false);
                    break;
                case 3:
//                    if (isV2)
//                    {
//                        throw new IllegalArgumentException("version 2 certificate cannot contain extensions");
//                    }
                    extensions = new DodgyExtensions(ASN1Sequence.getInstance(extra, true));
                    break;
                default:
                    throw new IllegalArgumentException("Unknown tag encountered in structure: " + extra.getTagNo());
                }
                extras--;
            }
        }

        public int getVersionNumber()
        {
            return version.intValueExact() + 1;
        }

        public ASN1Integer getVersion()
        {
            return version;
        }

        public ASN1Integer getSerialNumber()
        {
            return serialNumber;
        }

        public AlgorithmIdentifier getSignature()
        {
            return signature;
        }

        public X500Name getIssuer()
        {
            return issuer;
        }

        public Time getStartDate()
        {
            return startDate;
        }

        public Time getEndDate()
        {
            return endDate;
        }

        public X500Name getSubject()
        {
            return subject;
        }

        public SubjectPublicKeyInfo getSubjectPublicKeyInfo()
        {
            return subjectPublicKeyInfo;
        }

        public ASN1BitString getIssuerUniqueId()
        {
            return issuerUniqueId;
        }

        public ASN1BitString getSubjectUniqueId()
        {
            return subjectUniqueId;
        }

        public DodgyExtensions getExtensions()
        {
            return extensions;
        }

        public ASN1Primitive toASN1Primitive()
        {
            if (Properties.getPropertyValue("org.bouncycastle.x509.allow_non-der_tbscert") != null)
            {
                if (Properties.isOverrideSet("org.bouncycastle.x509.allow_non-der_tbscert"))
                {
                    return seq;
                }
            }
            else
            {
                return seq;
            }

            ASN1EncodableVector v = new ASN1EncodableVector();

            // DEFAULT Zero
            if (!version.hasValue(0))
            {
                v.add(new DERTaggedObject(true, 0, version));
            }

            v.add(serialNumber);
            v.add(signature);
            v.add(issuer);

            //
            // before and after dates
            //
            v.add(new DERSequence(startDate, endDate));

            if (subject != null)
            {
                v.add(subject);
            }
            else
            {
                v.add(new DERSequence());
            }

            v.add(subjectPublicKeyInfo);

            // Note: implicit tag
            if (issuerUniqueId != null)
            {
                v.add(new DERTaggedObject(false, 1, issuerUniqueId));
            }

            // Note: implicit tag
            if (subjectUniqueId != null)
            {
                v.add(new DERTaggedObject(false, 2, subjectUniqueId));
            }

            if (extensions != null)
            {
                v.add(new DERTaggedObject(true, 3, extensions));
            }

            return new DERSequence(v);
        }
    }

    public static class DodgyExtensions
        extends ASN1Object
    {
        private Hashtable extensions = new Hashtable();
        private Vector ordering = new Vector();

        /**
         * Constructor from ASN1Sequence.
         * <p>
         * The extensions are a list of constructed sequences, either with (OID, OctetString) or (OID, Boolean, OctetString)
         * </p>
         */
        DodgyExtensions(
            ASN1Sequence seq)
        {
            Enumeration e = seq.getObjects();

            while (e.hasMoreElements())
            {
                Extension ext = Extension.getInstance(e.nextElement());

                extensions.put(ext.getExtnId(), ext);
                ordering.addElement(ext.getExtnId());
            }
        }

        /**
         * return an Enumeration of the extension field's object ids.
         */
        public Enumeration oids()
        {
            return ordering.elements();
        }

        /**
         * return the extension represented by the object identifier
         * passed in.
         *
         * @return the extension if it's present, null otherwise.
         */
        public Extension getExtension(
            ASN1ObjectIdentifier oid)
        {
            return (Extension)extensions.get(oid);
        }

        /**
         * return the parsed value of the extension represented by the object identifier
         * passed in.
         *
         * @return the parsed value of the extension if it's present, null otherwise.
         */
        public ASN1Encodable getExtensionParsedValue(ASN1ObjectIdentifier oid)
        {
            Extension ext = this.getExtension(oid);

            if (ext != null)
            {
                return ext.getParsedValue();
            }

            return null;
        }

        /**
         * <pre>
         *     Extensions        ::=   SEQUENCE SIZE (1..MAX) OF Extension
         *
         *     Extension         ::=   SEQUENCE {
         *        extnId            EXTENSION.&amp;id ({ExtensionSet}),
         *        critical          BOOLEAN DEFAULT FALSE,
         *        extnValue         OCTET STRING }
         * </pre>
         */
        public ASN1Primitive toASN1Primitive()
        {
            ASN1EncodableVector vec = new ASN1EncodableVector(ordering.size());

            Enumeration e = ordering.elements();
            while (e.hasMoreElements())
            {
                ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier)e.nextElement();
                Extension ext = (Extension)extensions.get(oid);

                vec.add(ext);
            }

            return new DERSequence(vec);
        }

        public boolean equivalent(
            DodgyExtensions other)
        {
            if (extensions.size() != other.extensions.size())
            {
                return false;
            }

            Enumeration e1 = extensions.keys();

            while (e1.hasMoreElements())
            {
                Object key = e1.nextElement();

                if (!extensions.get(key).equals(other.extensions.get(key)))
                {
                    return false;
                }
            }

            return true;
        }

        public ASN1ObjectIdentifier[] getExtensionOIDs()
        {
            return toOidArray(ordering);
        }

        public ASN1ObjectIdentifier[] getNonCriticalExtensionOIDs()
        {
            return getExtensionOIDs(false);
        }

        public ASN1ObjectIdentifier[] getCriticalExtensionOIDs()
        {
            return getExtensionOIDs(true);
        }

        private ASN1ObjectIdentifier[] getExtensionOIDs(boolean isCritical)
        {
            Vector oidVec = new Vector();

            for (int i = 0; i != ordering.size(); i++)
            {
                Object oid = ordering.elementAt(i);

                if (((Extension)extensions.get(oid)).isCritical() == isCritical)
                {
                    oidVec.addElement(oid);
                }
            }

            return toOidArray(oidVec);
        }

        private ASN1ObjectIdentifier[] toOidArray(Vector oidVec)
        {
            ASN1ObjectIdentifier[] oids = new ASN1ObjectIdentifier[oidVec.size()];

            for (int i = 0; i != oids.length; i++)
            {
                oids[i] = (ASN1ObjectIdentifier)oidVec.elementAt(i);
            }
            return oids;
        }
    }
}

