javacertificatebouncycastleproviderjscep

Bouncycastle provider and Java SUN provider interopability issue


I am using jscep in a mobile device management project. Jscep uses bouncy castle as the security provider and I have done the same in my project. I have created a few simple static methods to create certificates using BC. These have been tested and work as expected. My problem is related to the Java security provider. In the example below I create two certificates, a CA and end point.

On a successful enrollment the jscep client returns a CertStore but the provider is set to "SUN". The store contains the certificate chain for the two certificates mentioned above. If I verify certificates in the CertStore and against the originals it passes, however if I verify against the certificates in the CertStore it fails. More strange is the fact it does not always fail - it sometimes works.

However, if I set the provide to "BC" it always works. The original certificates always verify correctly as shown in the code below. This code does not use jscep but reproduces the issue. I set the provider in two locations and have added some comments in the code to illustrate the behavior with different provider settings.

package com.mdm.utils.test;

import java.io.ByteArrayInputStream;
import java.math.BigInteger;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Security;
import java.security.cert.CertStore;
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
import java.security.cert.CollectionCertStoreParameters;
import java.security.cert.X509Certificate;
import java.security.spec.RSAPrivateCrtKeySpec;
import java.security.spec.RSAPublicKeySpec;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
import java.util.Iterator;
import javax.security.auth.x500.X500Principal;
import org.bouncycastle.asn1.x509.BasicConstraints;
import org.bouncycastle.asn1.x509.KeyUsage;
import org.bouncycastle.asn1.x509.X509Extension;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
import org.bouncycastle.cert.jcajce.JcaX509ExtensionUtils;
import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import static org.junit.Assert.fail;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class ProviderIssueTest {
    private static final Logger LOG = LoggerFactory.getLogger(X509CertificateGeneratorTest.class);
    private static final String BC = BouncyCastleProvider.PROVIDER_NAME;
    private static long serialNum = 1;

    @Before
    public void setUp() {
        Security.addProvider(new BouncyCastleProvider());       
    }

    @After
    public void tearDown() throws Exception {
        Security.removeProvider(BC); 
    }

    /**
     * Create a v3 self signed root certificate.
     */
    public static X509Certificate createV3RootCA(PublicKey pubKey, PrivateKey  privKey, 
                int durationInDays,
                String subject, String issuer) throws Exception {

        if (issuer == null)
            issuer = subject;

        // Mandatory
        Calendar calendar = Calendar.getInstance();
        Date notBefore = calendar.getTime();
        calendar.add(Calendar.DATE, durationInDays);
        Date notAfter = calendar.getTime();
        BigInteger issuerSerialNumber = BigInteger.valueOf(serialNum++);
        JcaX509v3CertificateBuilder certBuilder = new JcaX509v3CertificateBuilder(
                new X500Principal(issuer),
                issuerSerialNumber,
                notBefore, notAfter,
                new X500Principal(subject),
                pubKey);

        // Optional extensions
        certBuilder.addExtension(X509Extension.basicConstraints, true, new BasicConstraints(true));
        certBuilder.addExtension(X509Extension.keyUsage, true, new KeyUsage(KeyUsage.keyCertSign|KeyUsage.cRLSign|KeyUsage.digitalSignature));
        // Signing
        ContentSigner certSigner = new JcaContentSignerBuilder("SHA1WithRSA")
                                    .setProvider(BC)
                                    .build(privKey);
        X509CertificateHolder certHolder = certBuilder.build(certSigner);


        // Extract a JCA-compatible certificate
        X509Certificate cert = new JcaX509CertificateConverter()
                        .setProvider(BC).getCertificate(certHolder);

        cert.checkValidity(new Date());
        cert.verify(pubKey);
        return cert;
    }

    /**
     * Generate a leaf certificate signed by a CA
     */
    public static X509Certificate createCert(PublicKey pubKey, X509Certificate caCert, PrivateKey caPrivKey,
                int durationInDays,
                String subject) throws Exception {

        // Mandatory
        Calendar calendar = Calendar.getInstance();
        Date notBefore = calendar.getTime();
        calendar.add(Calendar.DATE, durationInDays);
        Date notAfter = calendar.getTime();
        JcaX509v3CertificateBuilder certBuilder = new JcaX509v3CertificateBuilder(
                caCert.getSubjectX500Principal(),
                BigInteger.valueOf(serialNum++),
                notBefore, notAfter,
                new X500Principal(subject),
                pubKey);

        // Optional extensions
        JcaX509ExtensionUtils extUtils = new JcaX509ExtensionUtils();
        certBuilder.addExtension(X509Extension.basicConstraints, false, new BasicConstraints(false));
        certBuilder.addExtension(X509Extension.keyUsage, true, new KeyUsage(KeyUsage.digitalSignature|KeyUsage.keyEncipherment));
        certBuilder.addExtension(X509Extension.subjectKeyIdentifier, false, extUtils.createSubjectKeyIdentifier(pubKey));
        certBuilder.addExtension(X509Extension.authorityKeyIdentifier, false, extUtils.createAuthorityKeyIdentifier(caCert));

        // Signing
        ContentSigner certSigner = new JcaContentSignerBuilder("SHA1WithRSA")
                    .setProvider(BC).build(caPrivKey);

        X509CertificateHolder certHolder = certBuilder.build(certSigner);

        // Extract a JCA-compatible certificate
        X509Certificate cert = new JcaX509CertificateConverter()
                    .setProvider(BC).getCertificate(certHolder);

        cert.checkValidity(new Date());
        cert.verify(caCert.getPublicKey());
        return cert;
    }

    @Test
    public void test() throws Exception {

        KeyFactory fact = KeyFactory.getInstance("RSA", BC);

        PrivateKey caPriv = fact.generatePrivate(
                new RSAPrivateCrtKeySpec(
                    new BigInteger("a5226e241a19f5b796ef2326f4f580b1e5cbc05360a7fd94fd8d59013115e077a422beb4904c5e57f0d9827a0da98b337ab8d47a2b24f77d83f9689e9b43af6b23bf39a1e4e87d8ce9f7d68b8dd50ffec1d34b25833848325ed035d3a1ddeaf62fe5a184dec918d7c2e8b89b17b057a9af359280956dc2a393be6e9a04517b25", 16),
                    new BigInteger("10001", 16),
                    new BigInteger("6ff223507e11532e1e380750858758b340e11b846a65f7d664fcc975b15cef4aac0e91d1be70c7143ec6755960a1ab283eedc5bcfc3a973c9397248141286565d479dd57d9bc01d4dec645dd1ae01590671315ec6f9bcde606707255382fcb363744a8bcda3c7a3c2e4015d450ed4aafb675ae277ddcf0e779165125a84f6681", 16),
                    new BigInteger("f8e745cf5388418a0f038b425095aa8ce3cae42764c15d6f91021a0b6fe0746653428ac95c88ce127deae745521805b6a53da780b56c3f4d15f0c88a85a19609", 16),
                    new BigInteger("a9d7bc0903893d8116ad8df22e425df382f895d47c0a47d7ea182e9a6221f3d1b27cdfd278960d8cc65699a5c1e5e17197805c9954ff6c37c19a0d9e2241a33d", 16),
                    new BigInteger("88181ca9a228ec7d0a7c8b9674ed80d58c701194209941f790b82f797570aaf4902de028fdb9a7c3a0a9e24e9af69b99247cb3abc2872f8d7ca3ad636071dbd1", 16),
                    new BigInteger("5f024cb0aa26ba9e1cc68772238882aff6e30245b401b840c33635d3acf39b4601d7b30934e593bcdd32928ed411b97466b0aa9c279d1eb76df8b48772584f6d", 16),
                    new BigInteger("e9774efb165c4309e7c7f32603d882d2e8b728887ddb50ee2c2e89591d192b64058699d3251e01348ee24dd23669aec43f1b4e16266950f6268e632242b7d500", 16)));
        PublicKey caPub = fact.generatePublic(
                new RSAPublicKeySpec(
                    new BigInteger("a5226e241a19f5b796ef2326f4f580b1e5cbc05360a7fd94fd8d59013115e077a422beb4904c5e57f0d9827a0da98b337ab8d47a2b24f77d83f9689e9b43af6b23bf39a1e4e87d8ce9f7d68b8dd50ffec1d34b25833848325ed035d3a1ddeaf62fe5a184dec918d7c2e8b89b17b057a9af359280956dc2a393be6e9a04517b25", 16),
                    new BigInteger("10001", 16)));

        PublicKey usrPub = fact.generatePublic(
                new RSAPublicKeySpec(
                    new BigInteger("84d4269505c38ba8c5fee8619cf0442eb55c31ae76ec430c1bbe3c82e48a1b56c6f2a3449edf044bcb7151b5df289182b685456f60f819ff7307478fe24f322c6afd4beae7bb4ad50c8bb26c9d0bd505cd91afb144003bea1d2c7fd743178d0141789aca69a5a97918dfccf7d82b25b1bf952cf06f9f432b338ddb773f79583dbbbeaf9fc4cf0878154fdcdfff160b3b5c1ed713990264ab97a3c0a5c617fe123395c03bf94ab24e3f7120ab7d95d06aa83ec9481566b1b6c2dcc9047a46abbf8ee43b32b5589edca36b3342073eb6bf8838a397363bf567640c1d0536961c125b81c0d31d09bd08171b1b6ca9343e09cfa7e3a6010e98d46da7cb6adccf52d5", 16),
                    new BigInteger("10001", 16)));

        // Create self signed CA
        X509Certificate caCert = createV3RootCA(
                caPub,
                caPriv,
                365,
                "CN=Root Test, C=US, ST=California, L=Woodside ,O=Acme Inc,OU=Root Certificate", 
                null);  // set issuer=subject
        X509Certificate usrCert = createCert(
                usrPub,
                caCert,
                caPriv,
                365,
                "CN=Pablobill, C=US, ST=California, L=Woodside,O=Acme Inc.,OU=EndEntity Certificate");

        // Always passes
        caCert.verify(caCert.getPublicKey());
        usrCert.verify(caCert.getPublicKey());

        try {
            // PROVIDER 1
            CertificateFactory cf = CertificateFactory.getInstance("X.509", "SUN"); 
            Certificate CA = cf.generateCertificate(new ByteArrayInputStream(caCert.getEncoded()));         
            Certificate UA = cf.generateCertificate(new ByteArrayInputStream(usrCert.getEncoded()));            
            CA.verify(CA.getPublicKey());   // This always works irrespective of the provider
            UA.verify(CA.getPublicKey());   // This always works irrespective of the provider
            ArrayList<Certificate> alist = new ArrayList<Certificate>(2);
            alist.add(UA);
            alist.add(CA);
            // PROVIDER 2
            CertStore certStore = CertStore.getInstance("Collection", 
                        new CollectionCertStoreParameters(alist), "SUN");
            Collection<?> certs = certStore.getCertificates(null);
            String provider = certStore.getProvider().getName();
            LOG.debug("Provider is {}", provider);

            // Get chain from cert store
            Iterator<?> iter = certs.iterator();
            Certificate UB = (Certificate)iter.next();
            Certificate CB = (Certificate)iter.next();

            LOG.debug("UB.length={}, UA.length={}, UB ={}", UB.getEncoded().length, UA.getEncoded().length, UB);
            LOG.debug("CB.length={}, CA.length={}, CB ={}", CB.getEncoded().length, CA.getEncoded().length, CB);

            // This always works if provider 2 is "BC", provider 1 can be either "SUN" or "BC".
            // Fails if provider 2 is "SUN" and provider 1 is "SUN" 
            UA.verify(CB.getPublicKey());
            CA.verify(CB.getPublicKey());

            // Works sometimes if provider 2 is "SUN", Always works if provider 2 is "BC"
            UB.verify(CB.getPublicKey());
            CB.verify(CB.getPublicKey());

            LOG.debug("SUCCESS");

        } catch (Exception e) {
            e.printStackTrace();
            fail();
        }
    }
}

I would have thought calling Certificate.getEncoded() would remove any provider dependence. The problem I have is Jscep returns a CertStore with "SUN" as the provider and the certificate chain cannot be validated with store entries.

Please read the comments below; I now understand the exact nature of this issue. When SUN is the provider the order in the CertStore is not consistent across runs of the test.

Is there a way to guarantee ordering?

Any help much appreciated.


DEBUG OUTPUT RUN 1 (SUCCESS)

0    [main] DEBUG com.mdm.utils.test.X509CertificateGeneratorTest  - Provider is SUN
8    [main] DEBUG com.mdm.utils.test.X509CertificateGeneratorTest  - UB.length=980, UA.length=980, UB =[
[
  Version: V3
  Subject: CN=Pablobill, C=US, ST=California, L=Woodside, O=Acme Inc., OU=EndEntity Certificate
  Signature Algorithm: SHA1withRSA, OID = 1.2.840.113549.1.1.5

  Key:  Sun RSA public key, 2048 bits
  modulus: 16768071670382525923108417071558186448345300080234882539261215442293489118744766516458998304300255531972593921338621155617570519295701287200918722384776808663209526068422690015419314730171909240597223060319385279562945357640946436608123561191766398078873400927250173773856327184499822450130773761828557700657093608566092380977746721299494533830480993371754648531497509681985981605034166444610796945239938191965934961812439864940235377352655354266302700246535173514307468185584585631338844720965267698182627932436044699321382289418637996460855280178953787469375899336175332362249696925445675694303599937295996964262613
  public exponent: 65537
  Validity: [From: Tue Nov 12 01:34:27 PST 2013,
               To: Wed Nov 12 01:34:27 PST 2014]
  Issuer: CN=Root Test, C=US, ST=California, L=Woodside, O=Acme Inc, OU=Root Certificate
  SerialNumber: [    02]

Certificate Extensions: 4
[1]: ObjectId: 2.5.29.35 Criticality=false
AuthorityKeyIdentifier [
KeyIdentifier [
0000: 12 05 9F B8 84 CA BC B6   1F 55 25 37 F4 4E 13 AC  .........U%7.N..
0010: E1 DA AC C8                                        ....
]
[CN=Root Test, C=US, ST=California, L=Woodside, O=Acme Inc, OU=Root Certificate]
SerialNumber: [    01]
]

[2]: ObjectId: 2.5.29.19 Criticality=false
BasicConstraints:[
  CA:false
  PathLen: undefined
]

[3]: ObjectId: 2.5.29.15 Criticality=true
KeyUsage [
  DigitalSignature
  Key_Encipherment
]

[4]: ObjectId: 2.5.29.14 Criticality=false
SubjectKeyIdentifier [
KeyIdentifier [
0000: F3 3D 00 4E D4 1D 72 F7   19 49 61 0A 91 7C AB 18  .=.N..r..Ia.....
0010: 54 56 F9 6F                                        TV.o
]
]

]
  Algorithm: [SHA1withRSA]
  Signature:
0000: 2F 07 E3 59 5A E3 B6 9E   51 2C 1F 66 BA C1 A2 DE  /..YZ...Q,.f....
0010: 11 D9 91 93 CD E1 E5 CC   B1 CE 0C D2 42 93 E7 08  ............B...
0020: C1 AB 3C 50 43 D5 2D AB   4D C4 87 23 00 FB 92 7E  ..<PC.-.M..#....
0030: EC DC B3 88 CB C3 9E 56   E2 DE 38 B9 01 E7 40 71  .......V..8...@q
0040: 4D 1F D6 F9 49 B6 09 4E   D5 37 31 3D 33 70 B1 0D  M...I..N.71=3p..
0050: F7 95 57 69 22 4A F1 71   1C 32 4C 11 8F C6 86 0C  ..Wi"J.q.2L.....
0060: 3B B6 36 9A EA 86 35 1B   30 3A F5 9D C8 0C 17 81  ;.6...5.0:......
0070: 16 AE 9E 71 25 EC FB 29   28 14 68 23 CB 32 E9 BE  ...q%..)(.h#.2..

]
11   [main] DEBUG com.mdm.utils.test.X509CertificateGeneratorTest  - CB.length=651, CA.length=651, CB =[
[
  Version: V3
  Subject: CN=Root Test, C=US, ST=California, L=Woodside, O=Acme Inc, OU=Root Certificate
  Signature Algorithm: SHA1withRSA, OID = 1.2.840.113549.1.1.5

  Key:  Sun RSA public key, 1024 bits
  modulus: 115961384612636641377515620217869320729793441040343476575562226776614089017051695237661046678469318513262011978862355272647361696676995692401162556817667242798889708372407550431983869833459964470284125551037400515858953182420212692027848881609761511002714010033447993093218955676653913874882198503919201647397
  public exponent: 65537
  Validity: [From: Tue Nov 12 01:34:27 PST 2013,
               To: Wed Nov 12 01:34:27 PST 2014]
  Issuer: CN=Root Test, C=US, ST=California, L=Woodside, O=Acme Inc, OU=Root Certificate
  SerialNumber: [    01]

Certificate Extensions: 2
[1]: ObjectId: 2.5.29.19 Criticality=true
BasicConstraints:[
  CA:true
  PathLen:2147483647
]

[2]: ObjectId: 2.5.29.15 Criticality=true
KeyUsage [
  DigitalSignature
  Key_CertSign
  Crl_Sign
]

]
  Algorithm: [SHA1withRSA]
  Signature:
0000: 93 0B BF B6 72 89 00 A8   03 A2 B1 2A 88 F9 BB 6B  ....r......*...k
0010: F5 69 F5 2D 80 C2 16 40   08 ED 7D 7F B8 AD 69 E7  .i.-...@......i.
0020: 93 1B EC B8 F4 6A 18 99   31 55 46 3D 2F E6 20 D3  .....j..1UF=/. .
0030: A1 69 FC 58 FA 9B 97 63   4B 74 C9 24 36 F8 32 E1  .i.X...cKt.$6.2.
0040: BA E2 5B 75 44 8E 11 74   BF 87 79 9D 5A 91 CB 8E  ..[uD..t..y.Z...
0050: B4 2E 02 FF D4 C0 F5 8E   79 37 21 B2 28 86 CD 29  ........y7!.(..)
0060: E2 C7 43 85 52 69 6C F6   1D B7 EE C4 91 87 6A 7B  ..C.Ril.......j.
0070: 0D 60 1C EB F6 E2 7D 31   43 21 43 34 7B FC BF 4E  .`.....1C!C4...N

]
11   [main] DEBUG com.mdm.utils.test.X509CertificateGeneratorTest  - SUCCESS

DEBUG OUTPUT RUN 2 (FAILED)

0    [main] DEBUG com.mdm.utils.test.X509CertificateGeneratorTest  - Provider is SUN
6    [main] DEBUG com.mdm.utils.test.X509CertificateGeneratorTest  - UB.length=651, UA.length=980, UB =[
[
  Version: V3
  Subject: CN=Root Test, C=US, ST=California, L=Woodside, O=Acme Inc, OU=Root Certificate
  Signature Algorithm: SHA1withRSA, OID = 1.2.840.113549.1.1.5

  Key:  Sun RSA public key, 1024 bits
  modulus: 115961384612636641377515620217869320729793441040343476575562226776614089017051695237661046678469318513262011978862355272647361696676995692401162556817667242798889708372407550431983869833459964470284125551037400515858953182420212692027848881609761511002714010033447993093218955676653913874882198503919201647397
  public exponent: 65537
  Validity: [From: Tue Nov 12 01:55:14 PST 2013,
               To: Wed Nov 12 01:55:14 PST 2014]
  Issuer: CN=Root Test, C=US, ST=California, L=Woodside, O=Acme Inc, OU=Root Certificate
  SerialNumber: [    01]

Certificate Extensions: 2
[1]: ObjectId: 2.5.29.19 Criticality=true
BasicConstraints:[
  CA:true
  PathLen:2147483647
]

[2]: ObjectId: 2.5.29.15 Criticality=true
KeyUsage [
  DigitalSignature
  Key_CertSign
  Crl_Sign
]

]
  Algorithm: [SHA1withRSA]
  Signature:
0000: 84 9C FA 11 12 90 61 D6   E6 71 5E 5B D8 72 30 1B  ......a..q^[.r0.
0010: D5 21 E9 7E 2D 25 59 13   98 A7 00 A5 5A F8 DD 46  .!..-%Y.....Z..F
0020: 2A 0F A0 7B 98 2A E2 4C   D8 36 46 52 F4 B3 9E A2  *....*.L.6FR....
0030: 0B C3 C1 79 B7 01 CC 3B   AC E1 B5 17 9A AC 95 F3  ...y...;........
0040: DA 2C 08 8D 77 F3 91 DD   2F E9 3C A4 D2 94 24 08  .,..w.../.<...$.
0050: 5A 59 54 0F AA 14 6C 0E   22 37 D3 80 78 03 1E D5  ZYT...l."7..x...
0060: C6 7F 3F 42 5E A9 28 49   31 07 6F 0B C3 A6 E2 0F  ..?B^.(I1.o.....
0070: D2 48 5D 6C 50 27 30 E7   4A B3 31 9A 83 E7 88 C9  .H]lP'0.J.1.....

]
10   [main] DEBUG com.mdm.utils.test.X509CertificateGeneratorTest  - CB.length=980, CA.length=651, CB =[
[
  Version: V3
  Subject: CN=Pablobill, C=US, ST=California, L=Woodside, O=Acme Inc., OU=EndEntity Certificate
  Signature Algorithm: SHA1withRSA, OID = 1.2.840.113549.1.1.5

  Key:  Sun RSA public key, 2048 bits
  modulus: 16768071670382525923108417071558186448345300080234882539261215442293489118744766516458998304300255531972593921338621155617570519295701287200918722384776808663209526068422690015419314730171909240597223060319385279562945357640946436608123561191766398078873400927250173773856327184499822450130773761828557700657093608566092380977746721299494533830480993371754648531497509681985981605034166444610796945239938191965934961812439864940235377352655354266302700246535173514307468185584585631338844720965267698182627932436044699321382289418637996460855280178953787469375899336175332362249696925445675694303599937295996964262613
  public exponent: 65537
  Validity: [From: Tue Nov 12 01:55:14 PST 2013,
               To: Wed Nov 12 01:55:14 PST 2014]
  Issuer: CN=Root Test, C=US, ST=California, L=Woodside, O=Acme Inc, OU=Root Certificate
  SerialNumber: [    02]

Certificate Extensions: 4
[1]: ObjectId: 2.5.29.35 Criticality=false
AuthorityKeyIdentifier [
KeyIdentifier [
0000: 12 05 9F B8 84 CA BC B6   1F 55 25 37 F4 4E 13 AC  .........U%7.N..
0010: E1 DA AC C8                                        ....
]
[CN=Root Test, C=US, ST=California, L=Woodside, O=Acme Inc, OU=Root Certificate]
SerialNumber: [    01]
]

[2]: ObjectId: 2.5.29.19 Criticality=false
BasicConstraints:[
  CA:false
  PathLen: undefined
]

[3]: ObjectId: 2.5.29.15 Criticality=true
KeyUsage [
  DigitalSignature
  Key_Encipherment
]

[4]: ObjectId: 2.5.29.14 Criticality=false
SubjectKeyIdentifier [
KeyIdentifier [
0000: F3 3D 00 4E D4 1D 72 F7   19 49 61 0A 91 7C AB 18  .=.N..r..Ia.....
0010: 54 56 F9 6F                                        TV.o
]
]

]
  Algorithm: [SHA1withRSA]
  Signature:
0000: 75 22 44 D4 AD 00 2D 32   70 EA EF 68 2B E5 3D 18  u"D...-2p..h+.=.
0010: 62 94 8F 90 C6 FD 0F E9   3B A3 1E 18 02 FA 2F A7  b.......;...../.
0020: 68 5F 1E 97 AF AF FB 2E   10 30 44 BB 79 28 F8 E3  h_.......0D.y(..
0030: 59 25 64 1C 59 51 C5 F3   E6 0F E2 92 66 1B 4A 28  Y%d.YQ......f.J(
0040: 18 68 10 65 31 C5 B4 67   87 90 DD 79 47 EB 00 91  .h.e1..g...yG...
0050: 4E 73 5B F3 6B CB 6B 20   E6 9A DC 4F 57 CD ED 30  Ns[.k.k ...OW..0
0060: D0 A0 BB DA 73 BE 78 E2   08 BD 66 D2 F0 08 B7 D3  ....s.x...f.....
0070: ED 6E 93 29 36 C1 60 2E   E0 08 51 2B 4C C8 57 85  .n.)6.`...Q+L.W.

]
java.security.SignatureException: Signature length not correct: got 128 but was expecting 256
    at sun.security.rsa.RSASignature.engineVerify(RSASignature.java:189)
    at java.security.Signature$Delegate.engineVerify(Signature.java:1172)
    at java.security.Signature.verify(Signature.java:623)
    at sun.security.x509.X509CertImpl.verify(X509CertImpl.java:446)
    at sun.security.x509.X509CertImpl.verify(X509CertImpl.java:394)
    at com.mdm.utils.test.ProviderIssueTest.test(ProviderIssueTest.java:207)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:606)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
    at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:27)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:271)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:70)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)
    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)

Solution

  • The problem with your code is that SUN's provider implementation of CertStore.getCertificates() returns HashSet. And HashSet makes no guarantees as to the iteration order of the set; in particular, it does not guarantee that the order will remain constant over time.

    import java.security.cert.CertStore;
    import java.security.cert.Certificate;
    import java.security.cert.CertificateFactory;
    import java.security.cert.CollectionCertStoreParameters;
    import java.security.cert.X509Certificate;
    import java.security.spec.RSAPrivateCrtKeySpec;
    import java.security.spec.RSAPublicKeySpec;
    import java.util.ArrayList;
    import java.util.Calendar;
    import java.util.Collection;
    import java.util.Date;
    import java.util.Iterator;
    import javax.security.auth.x500.X500Principal;
    import org.bouncycastle.asn1.x509.BasicConstraints;
    import org.bouncycastle.asn1.x509.KeyUsage;
    import org.bouncycastle.asn1.x509.X509Extension;
    import org.bouncycastle.cert.X509CertificateHolder;
    import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
    import org.bouncycastle.cert.jcajce.JcaX509ExtensionUtils;
    import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder;
    import org.bouncycastle.jce.provider.BouncyCastleProvider;
    import org.bouncycastle.operator.ContentSigner;
    import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
    
    public class Test {
        private static long serialNum = 1;
    
        /**
         * Create a v3 self signed root certificate.
         */
        public static X509Certificate createV3RootCA(PublicKey pubKey, PrivateKey  privKey, 
                    int durationInDays,
                    String subject, String issuer) throws Exception {
    
            if (issuer == null)
                issuer = subject;
    
            // Mandatory
            Calendar calendar = Calendar.getInstance();
            Date notBefore = calendar.getTime();
            calendar.add(Calendar.DATE, durationInDays);
            Date notAfter = calendar.getTime();
            BigInteger issuerSerialNumber = BigInteger.valueOf(serialNum++);
            JcaX509v3CertificateBuilder certBuilder = new JcaX509v3CertificateBuilder(
                    new X500Principal(issuer),
                    issuerSerialNumber,
                    notBefore, notAfter,
                    new X500Principal(subject),
                    pubKey);
    
            // Optional extensions
            certBuilder.addExtension(X509Extension.basicConstraints, true, new BasicConstraints(true));
            certBuilder.addExtension(X509Extension.keyUsage, true, new KeyUsage(KeyUsage.keyCertSign|KeyUsage.cRLSign|KeyUsage.digitalSignature));
            // Signing
            ContentSigner certSigner = new JcaContentSignerBuilder("SHA1WithRSA")
                                        .setProvider(BouncyCastleProvider.PROVIDER_NAME)
                                        .build(privKey);
            X509CertificateHolder certHolder = certBuilder.build(certSigner);
    
            // Extract a JCA-compatible certificate
            X509Certificate cert = new JcaX509CertificateConverter()
                            .setProvider(BouncyCastleProvider.PROVIDER_NAME).getCertificate(certHolder);
    
            cert.checkValidity(new Date());
            cert.verify(pubKey);
            return cert;
        }
    
        /**
         * Generate a leaf certificate signed by a CA
         */
        public static X509Certificate createCert(PublicKey pubKey, X509Certificate caCert, PrivateKey caPrivKey,
                    int durationInDays,
                    String subject) throws Exception {
    
            // Mandatory
            Calendar calendar = Calendar.getInstance();
            Date notBefore = calendar.getTime();
            calendar.add(Calendar.DATE, durationInDays);
            Date notAfter = calendar.getTime();
            JcaX509v3CertificateBuilder certBuilder = new JcaX509v3CertificateBuilder(
                    caCert.getSubjectX500Principal(),
                    BigInteger.valueOf(serialNum++),
                    notBefore, notAfter,
                    new X500Principal(subject),
                    pubKey);
    
            // Optional extensions
            JcaX509ExtensionUtils extUtils = new JcaX509ExtensionUtils();
            certBuilder.addExtension(X509Extension.basicConstraints, false, new BasicConstraints(false));
            certBuilder.addExtension(X509Extension.keyUsage, true, new KeyUsage(KeyUsage.digitalSignature|KeyUsage.keyEncipherment));
            certBuilder.addExtension(X509Extension.subjectKeyIdentifier, false, extUtils.createSubjectKeyIdentifier(pubKey));
            certBuilder.addExtension(X509Extension.authorityKeyIdentifier, false, extUtils.createAuthorityKeyIdentifier(caCert));
    
            // Signing
            ContentSigner certSigner = new JcaContentSignerBuilder("SHA1WithRSA")
                        .setProvider(BouncyCastleProvider.PROVIDER_NAME).build(caPrivKey);
    
            X509CertificateHolder certHolder = certBuilder.build(certSigner);
    
            // Extract a JCA-compatible certificate
            X509Certificate cert = new JcaX509CertificateConverter()
                        .setProvider(BouncyCastleProvider.PROVIDER_NAME).getCertificate(certHolder);
    
            cert.checkValidity(new Date());
            cert.verify(caCert.getPublicKey());
            return cert;
        }
    
        private static String toHexStr(byte[] bytes) {
            return new BigInteger(1, bytes).toString(16);
        }
    
        public static void main(String [] args) {
    
            Security.addProvider(new BouncyCastleProvider());
    
            try {
                KeyFactory fact = KeyFactory.getInstance("RSA", BouncyCastleProvider.PROVIDER_NAME);
    
                PrivateKey caPriv = fact.generatePrivate(
                    new RSAPrivateCrtKeySpec(
                        new BigInteger("a5226e241a19f5b796ef2326f4f580b1e5cbc05360a7fd94fd8d59013115e077a422beb4904c5e57f0d9827a0da98b337ab8d47a2b24f77d83f9689e9b43af6b23bf39a1e4e87d8ce9f7d68b8dd50ffec1d34b25833848325ed035d3a1ddeaf62fe5a184dec918d7c2e8b89b17b057a9af359280956dc2a393be6e9a04517b25", 16),
                        new BigInteger("10001", 16),
                        new BigInteger("6ff223507e11532e1e380750858758b340e11b846a65f7d664fcc975b15cef4aac0e91d1be70c7143ec6755960a1ab283eedc5bcfc3a973c9397248141286565d479dd57d9bc01d4dec645dd1ae01590671315ec6f9bcde606707255382fcb363744a8bcda3c7a3c2e4015d450ed4aafb675ae277ddcf0e779165125a84f6681", 16),
                        new BigInteger("f8e745cf5388418a0f038b425095aa8ce3cae42764c15d6f91021a0b6fe0746653428ac95c88ce127deae745521805b6a53da780b56c3f4d15f0c88a85a19609", 16),
                        new BigInteger("a9d7bc0903893d8116ad8df22e425df382f895d47c0a47d7ea182e9a6221f3d1b27cdfd278960d8cc65699a5c1e5e17197805c9954ff6c37c19a0d9e2241a33d", 16),
                        new BigInteger("88181ca9a228ec7d0a7c8b9674ed80d58c701194209941f790b82f797570aaf4902de028fdb9a7c3a0a9e24e9af69b99247cb3abc2872f8d7ca3ad636071dbd1", 16),
                        new BigInteger("5f024cb0aa26ba9e1cc68772238882aff6e30245b401b840c33635d3acf39b4601d7b30934e593bcdd32928ed411b97466b0aa9c279d1eb76df8b48772584f6d", 16),
                        new BigInteger("e9774efb165c4309e7c7f32603d882d2e8b728887ddb50ee2c2e89591d192b64058699d3251e01348ee24dd23669aec43f1b4e16266950f6268e632242b7d500", 16)));
                PublicKey caPub = fact.generatePublic(
                    new RSAPublicKeySpec(
                        new BigInteger("a5226e241a19f5b796ef2326f4f580b1e5cbc05360a7fd94fd8d59013115e077a422beb4904c5e57f0d9827a0da98b337ab8d47a2b24f77d83f9689e9b43af6b23bf39a1e4e87d8ce9f7d68b8dd50ffec1d34b25833848325ed035d3a1ddeaf62fe5a184dec918d7c2e8b89b17b057a9af359280956dc2a393be6e9a04517b25", 16),
                        new BigInteger("10001", 16)));
    
                PublicKey usrPub = fact.generatePublic(
                    new RSAPublicKeySpec(
                        new BigInteger("84d4269505c38ba8c5fee8619cf0442eb55c31ae76ec430c1bbe3c82e48a1b56c6f2a3449edf044bcb7151b5df289182b685456f60f819ff7307478fe24f322c6afd4beae7bb4ad50c8bb26c9d0bd505cd91afb144003bea1d2c7fd743178d0141789aca69a5a97918dfccf7d82b25b1bf952cf06f9f432b338ddb773f79583dbbbeaf9fc4cf0878154fdcdfff160b3b5c1ed713990264ab97a3c0a5c617fe123395c03bf94ab24e3f7120ab7d95d06aa83ec9481566b1b6c2dcc9047a46abbf8ee43b32b5589edca36b3342073eb6bf8838a397363bf567640c1d0536961c125b81c0d31d09bd08171b1b6ca9343e09cfa7e3a6010e98d46da7cb6adccf52d5", 16),
                        new BigInteger("10001", 16)));
    
                // Create self signed CA
                X509Certificate caCert = createV3RootCA(
                    caPub,
                    caPriv,
                    365,
                    "CN=Root Test, C=US, ST=California, L=Woodside ,O=Acme Inc,OU=Root Certificate", 
                    null);  // set issuer=subject
                X509Certificate usrCert = createCert(
                    usrPub,
                    caCert,
                    caPriv,
                    365,
                    "CN=Pablobill, C=US, ST=California, L=Woodside,O=Acme Inc.,OU=EndEntity Certificate");
    
                System.out.println("CA key:\n" + toHexStr(caCert.getPublicKey().getEncoded()));
                System.out.println("USR key:\n" + toHexStr(usrCert.getPublicKey().getEncoded()));
    
                // Always passes
                caCert.verify(caCert.getPublicKey());
                usrCert.verify(caCert.getPublicKey());
    
                // PROVIDER 1
                CertificateFactory cf = CertificateFactory.getInstance("X.509", "SUN"); 
                Certificate CA = cf.generateCertificate(new ByteArrayInputStream(caCert.getEncoded()));
                Certificate UA = cf.generateCertificate(new ByteArrayInputStream(usrCert.getEncoded()));
    
                CA.verify(CA.getPublicKey());   // This always works irrespective of the provider
                UA.verify(CA.getPublicKey());   // This always works irrespective of the provider
    
                ArrayList<Certificate> alist = new ArrayList<Certificate>(2);
                alist.add(UA);
                alist.add(CA);
                // PROVIDER 2
                CertStore certStore = CertStore.getInstance("Collection", 
                            new CollectionCertStoreParameters(alist), "SUN");
                Collection<?> certs = certStore.getCertificates(null);
                System.out.println(String.format("Provider is %s and Collection is %s",
                    certStore.getProvider().getName(),
                    certs.getClass().getCanonicalName()));
    
                // Get chain from cert store
                Iterator<?> iter = certs.iterator();
                Certificate UB = (Certificate)iter.next();
                Certificate CB = (Certificate)iter.next();
                System.out.println("CA key:\n" + toHexStr(CB.getPublicKey().getEncoded()));
                System.out.println("USR key:\n" + toHexStr(UB.getPublicKey().getEncoded()));
    
                if (CB.getPublicKey().getEncoded().length != caCert.getPublicKey().getEncoded().length) {
                    System.out.println("Certificates were swapped in CertStore!");
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    

    Sample output:

     [java] CA key:
     [java] 30819f300d06092a864886f70d010101050003818d0030818902818100a5226e241a19f5b796ef2326f4f580b1e5cbc05360a7fd94fd8d59013115e077a422beb4904c5e57f0d9827a0da98b337ab8d47a2b24f77d83f9689e9b43af6b23bf39a1e4e87d8ce9f7d68b8dd50ffec1d34b25833848325ed035d3a1ddeaf62fe5a184dec918d7c2e8b89b17b057a9af359280956dc2a393be6e9a04517b250203010001
     [java] USR key:
     [java] 30820122300d06092a864886f70d01010105000382010f003082010a028201010084d4269505c38ba8c5fee8619cf0442eb55c31ae76ec430c1bbe3c82e48a1b56c6f2a3449edf044bcb7151b5df289182b685456f60f819ff7307478fe24f322c6afd4beae7bb4ad50c8bb26c9d0bd505cd91afb144003bea1d2c7fd743178d0141789aca69a5a97918dfccf7d82b25b1bf952cf06f9f432b338ddb773f79583dbbbeaf9fc4cf0878154fdcdfff160b3b5c1ed713990264ab97a3c0a5c617fe123395c03bf94ab24e3f7120ab7d95d06aa83ec9481566b1b6c2dcc9047a46abbf8ee43b32b5589edca36b3342073eb6bf8838a397363bf567640c1d0536961c125b81c0d31d09bd08171b1b6ca9343e09cfa7e3a6010e98d46da7cb6adccf52d50203010001
     [java] Provider is SUN and Collection is java.util.HashSet
     [java] CA key:
     [java] 30820122300d06092a864886f70d01010105000382010f003082010a028201010084d4269505c38ba8c5fee8619cf0442eb55c31ae76ec430c1bbe3c82e48a1b56c6f2a3449edf044bcb7151b5df289182b685456f60f819ff7307478fe24f322c6afd4beae7bb4ad50c8bb26c9d0bd505cd91afb144003bea1d2c7fd743178d0141789aca69a5a97918dfccf7d82b25b1bf952cf06f9f432b338ddb773f79583dbbbeaf9fc4cf0878154fdcdfff160b3b5c1ed713990264ab97a3c0a5c617fe123395c03bf94ab24e3f7120ab7d95d06aa83ec9481566b1b6c2dcc9047a46abbf8ee43b32b5589edca36b3342073eb6bf8838a397363bf567640c1d0536961c125b81c0d31d09bd08171b1b6ca9343e09cfa7e3a6010e98d46da7cb6adccf52d50203010001
     [java] USR key:
     [java] 30819f300d06092a864886f70d010101050003818d0030818902818100a5226e241a19f5b796ef2326f4f580b1e5cbc05360a7fd94fd8d59013115e077a422beb4904c5e57f0d9827a0da98b337ab8d47a2b24f77d83f9689e9b43af6b23bf39a1e4e87d8ce9f7d68b8dd50ffec1d34b25833848325ed035d3a1ddeaf62fe5a184dec918d7c2e8b89b17b057a9af359280956dc2a393be6e9a04517b250203010001
     [java] Certificates were swapped in CertStore!