Question

I am trying to generate X509 certificates with bouncycastle 1.46, with the code below. The issue I have is that when a certificate is written in a JKS and then reread, the DNs are reversed. For instance, if I run the code below, I get the following output:

CN=test,O=gina
CN=test,O=gina
CN=test,O=gina
O=gina, CN=test

Does anybody know the reason for this? how to avoid it? Thanks in advance.

Code:

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

        KeyPair pair = generateKeyPair("RSA", 1024);
        X500Name principal = new X500Name("cn=test,o=gina");
        System.out.println(principal);
        BigInteger sn = BigInteger.valueOf(1234);
        Date start = today();
        Date end = addYears(start, 2);
        X509Certificate cert = generateCert(principal, pair, sn, start, end,
                "SHA1withRSA");
        cert.verify(pair.getPublic());
        System.out.println(cert.getSubjectDN());

        // Store the certificate in the JKS
        KeyStore ks = KeyStore.getInstance("JKS");
        ks.load(null, null);
        ks.setKeyEntry("alias", pair.getPrivate(), KEY_PWD,
                new X509Certificate[] {cert});
        X509Certificate c
                = (X509Certificate)ks.getCertificateChain("alias")[0];
        System.out.println(c.getSubjectDN());
        OutputStream out = new FileOutputStream("text.jks");
        try {
            ks.store(out, KEYSTORE_PWD);
        } finally {
            out.close();
        }

        // Reread the JKS
        ks = KeyStore.getInstance("JKS");
        InputStream in = new FileInputStream("text.jks");
        try {
            ks.load(in, KEYSTORE_PWD);
        } finally {
            in.close();
        }
        c = (X509Certificate)ks.getCertificateChain("alias")[0];
        c.verify(pair.getPublic());
        System.out.println(c.getSubjectDN());
    } catch (Exception e) {
        e.printStackTrace();
    }
}

private static X509Certificate generateCert(X500Name principal,
        KeyPair pair, BigInteger sn, Date start, Date end, String sigalg)
        throws OperatorCreationException, CertificateException {
    JcaX509v3CertificateBuilder certGen
            = new JcaX509v3CertificateBuilder(principal, sn, start, end,
                    principal, pair.getPublic());
    JcaContentSignerBuilder builder
            = new JcaContentSignerBuilder(sigalg);
    builder.setProvider("BC");
    ContentSigner signr = builder.build(pair.getPrivate());
    X509CertificateHolder certHolder = certGen.build(signr);
    JcaX509CertificateConverter conv
            = new JcaX509CertificateConverter();
    conv.setProvider("BC");
    return conv.getCertificate(certHolder);
}

private static KeyPair generateKeyPair(String algorithm, int keySize)
        throws NoSuchAlgorithmException {
    KeyPairGenerator gen = KeyPairGenerator.getInstance(algorithm);
    gen.initialize(keySize);
    return gen.generateKeyPair();
}

private static Date today() {
    Calendar cal = Calendar.getInstance();
    cal.set(Calendar.HOUR_OF_DAY, 0);
    cal.set(Calendar.MINUTE, 0);
    cal.set(Calendar.SECOND, 0);
    cal.set(Calendar.MILLISECOND, 0);
    return cal.getTime();
}

private static Date addYears(Date date, int count) {
    Calendar cal = Calendar.getInstance();
    cal.setTime(date);
    cal.add(Calendar.YEAR, count);
    return cal.getTime();
}
Was it helpful?

Solution

I ran into the same issue and resolved it quickly with the following:

//CREATES AN X500 CA SUBJECT FOR ISSUER

X500Name issuerName = new JcaX509CertificateHolder((X509Certificate) caCert).getSubject();

I then used it with the following:

//CONSTRUCTS THE X509 CERTIFIFATE OBJECT

X509v3CertificateBuilder v3CertGen = new X509v3CertificateBuilder(
issuerName, 
serialNumber, 
startDate, endDate, 
DevCsr.getSubject(), 
DevCsr.getSubjectPublicKeyInfo());

The issuer name in the Java Keystore end entity certificate now shows up in the correct order.

Cheers!

OTHER TIPS

This may be a bit simpler. At least in BC 1.48+, you can construct the X500Name thusly, and the OIDs will be ordered in the expected way (or at least, the way you specify them):

final X500Name subject = new X500Name(RFC4519Style.INSTANCE, "CN=test,O=gina");

I had the same problem with bouncy 1.47.

First you must be careful with the classes X500Name and X500Principal. There are the SUN classes and the bouncy classes. They are totally different !!

X500Name (bouncy) should be created using X500NameBuilder. But If you need to create it using a String then your attributes must be in the reverse order of RFC2253, this means your attributes must be in this order : " CN, L, ST, O, OU, C, STREET, DC, UID ".

This is not convenient because, for example, in my case, I had to create a X500Name (bouncy) from a X500Principal (SUN) and the only way to do it was to use the X500Principal:getName() method which print the attributes according to RFC2253 order. So I created this method :

private org.bouncycastle.asn1.x500.X500Name toBouncyX500Name( javax.security.auth.x500.X500Principal principal) {

    String name = principal.getName();

    String[] RDN = name.split(",");

    StringBuffer buf = new StringBuffer(name.length());
    for(int i = RDN.length - 1; i >= 0; i--){
        if(i != RDN.length - 1)
            buf.append(',');

        buf.append(RDN[i]);
    }

    return new X500Name(buf.toString());
}

Hope it'll be useful to someone :)

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top