Frage

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)
War es hilfreich?

Lösung

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!

Andere Tipps

It's been referenced in the bug raised by OP on GitHub, but jscep allows you to use a CertStoreInspector like so:

import org.jscep.client.inspect.*;

...

CertStoreInspector inspector = CertStoreInspectorFactory.getInstance(certStore);
X509Certificate signer = inspector.getSigner(); // RA
X509Certificate recipient = inspector.getRecipient(); // RA
X509Certificate issuer = inspector.getIssuer(); // CA
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top