Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Create PKCS10 request with subject alternatives using Bouncy Castle in Java

I am currently using bouncy castle to create a PKCS10 request with a single subject as such:

    X500Principal subject = new X500Principal("CN=foo.bar.com");
    PKCS10CertificationRequestBuilder builder = new JcaPKCS10CertificationRequestBuilder(
            subject, publicKey);

I now need to add subject alternatives to the PKCS10 request. I have been unable to figure out how to do this. Any suggestions?

SOLUTION:

Based on the great info provided in the 2nd answer I was able to figure this out. In the working code below XName is a simple class holding the subject name and name type (DNS, RFC822, etc).

        String signerAlgo = "SHA256withRSA";
        ContentSigner signGen = new JcaContentSignerBuilder(signerAlgo).build(privateKey);

        X500Principal subject = new X500Principal(csr.getSubjectAsX500NameString());

        PKCS10CertificationRequestBuilder builder = 
                new JcaPKCS10CertificationRequestBuilder(subject, publicKey);

        /*
         * Add SubjectAlternativeNames (SANs)
         */
        if (csr.getSubjectAlternatives() != null && csr.getSubjectAlternatives().size() > 0) {
            List<GeneralName> namesList = new ArrayList<>();
            for (XName subjectAlt : csr.getSubjectAlternatives()) {
                log.debug(m, d+2, "Adding SubjectAltName: %s", subjectAlt);
                namesList.add(GeneralNameTool.toGeneralName(subjectAlt));
            }

            /*
             * Use ExtensionsGenerator to add individual extensions.
             */
            ExtensionsGenerator extGen = new ExtensionsGenerator();

            GeneralNames subjectAltNames = new GeneralNames(namesList.toArray(new GeneralName [] {}));
            extGen.addExtension(Extension.subjectAlternativeName, false, subjectAltNames);
            builder.addAttribute(PKCSObjectIdentifiers.pkcs_9_at_extensionRequest, extGen.generate());
        }

        PKCS10CertificationRequest request = builder.build(signGen);

        StringWriter writer = new StringWriter();
        JcaPEMWriter pem = new JcaPEMWriter(writer);
        pem.writeObject(request);
        pem.close();
like image 669
Mike Cooper Avatar asked Dec 09 '15 02:12

Mike Cooper


1 Answers

I had the same issue Mike, and I think your problem relates to trying to do this using a JcaPKCS10CertificationRequestBuilder (from the version 2 APIs) instead of by using the deprecated V1 APIs.

If you go to the BC wiki pages and look for "X.509 Public Key Certificate and Certificate request generation" there is a reasonable description of what to do with the Version 1 APIs, which is very similar to the listing on page 212 of the Wrox book by David Hook, "Beginning Cryptography with Java".

The documentation on the wiki for the version 2 APIs is really poor when describing how to create a CSR though.

To summarise how to use the v2 APIs, here's some code I have which works which is based on their V2 tests case (the class to look for is below this code listing):

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.math.BigInteger;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.Security;
import org.bouncycastle.asn1.DEROctetString;

import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x500.X500NameBuilder;
import org.bouncycastle.asn1.x500.style.BCStyle;
import org.bouncycastle.asn1.x509.BasicConstraints;
import org.bouncycastle.asn1.x509.Extension;
import org.bouncycastle.asn1.x509.Extensions;
import org.bouncycastle.asn1.x509.KeyUsage;
import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
import org.bouncycastle.cert.jcajce.JcaX509ExtensionUtils;

import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.jce.spec.ECParameterSpec;
import org.bouncycastle.jce.spec.ECPrivateKeySpec;
import org.bouncycastle.jce.spec.ECPublicKeySpec;
import org.bouncycastle.math.ec.ECCurve;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import org.bouncycastle.pkcs.PKCS10CertificationRequest;
import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequestBuilder;
import org.bouncycastle.util.encoders.Base64;
import org.bouncycastle.util.encoders.Hex;

...

X500NameBuilder x500NameBld = new X500NameBuilder(BCStyle.INSTANCE);

// See e.g. http://javadox.com/org.bouncycastle/\
// bcprov-jdk15on/1.51/org/bouncycastle/asn1/x500/style/BCStyle.html
// for a description of the available RDNs

x500NameBld.addRDN(BCStyle.CN, commonName);
x500NameBld.addRDN(BCStyle.OU, orgCode);
x500NameBld.addRDN(BCStyle.UNIQUE_IDENTIFIER, "64 bit EUID goes here");

X500Name    subject = x500NameBld.build();

/**
 *  My application needs to set the Key Usage section of the CSR 
 * (which for my app has a Criticality of "true" and a value of
 * "digital signature" or "key agreement").
 */

 Extension[] extSigning = new Extension[] {
        new Extension(Extension.basicConstraints, true, 
           new DEROctetString(new BasicConstraints(true))),
           new Extension(Extension.keyUsage, true,
           new DEROctetString(new KeyUsage(KeyUsage.keyCertSign))),
  };

  Extension[] extKeyAgreement = new Extension[] {
        new Extension(Extension.basicConstraints, true, 
           new DEROctetString(new BasicConstraints(true))),
           new Extension(Extension.keyUsage, true, 
           new DEROctetString(new KeyUsage(KeyUsage.keyCertSign))),
   };

   PKCS10CertificationRequest req = 
     new JcaPKCS10CertificationRequestBuilder(
         subject,
         pair.getPublic())
         .addAttribute(PKCSObjectIdentifiers.pkcs_9_at_extensionRequest,
         new Extensions(isKaFlag==true?extKeyAgreement:extSigning))
         .build(new JcaContentSignerBuilder("SHA256withECDSA")
         .setProvider(BC)                         
         .build(pair.getPrivate()));

    return req;  // The PKCS10 certificate signing request

I'd suggest looking closely at their wiki pages specifically for the v2 API.

Critically, once you have found the source code for cert.test.PKCS10Test for V2 it all starts to make sense. Finally, I use this JavaScript hex dumper for ASN1 to check that it is coming out correctly.

like image 144
webbje Avatar answered Sep 29 '22 10:09

webbje