Bouncy Castle: Add a Subject Alternative Name when creating a Certificate

Bouncy Castle provides a way to assign a Subject Alternative Name while generating a certificate. Like most of the APIs, it can take a little getting used to.

This article shows a few ways not to generate SANs as well as some 'correct' code that helped me generate Alternate DNS Names for my test certificates




I had a devil of a time trying to figure out how to add a DNS Name Subject Alternative Name using Bouncy Castle. Like most things the solution to my problem ended up being deceptively simple. While going along I learned that you can have several different types of SAN:

  • DNSName
  • DirectoryEntry
  • Email address
  • Other (scroll down to see the options)



Here's a quick high-level overview of what goes into creating a certificate with a SAN:


Working Code Sample

I'll start off with the code that worked for me. That way you can skip all the other crazy stuff I tried and save time:

GeneralName altName = new GeneralName(GeneralName.DnsName, "");
GeneralNames subjectAltName = new GeneralNames(altName);
cGenerator.AddExtension(X509Extensions.SubjectAlternativeName, false, subjectAltName); 



  • The first line sets up the GeneralName object as a DNS Name
  • Line 2 encapsulates this in a GeneralNames object
  • Line 3 invokes the AddExtension method on the cert generator I had previously setup and:
    • Sets the OID to (Subject Alt Name) using the X509Extensions class
    • Sets the criticality to False (I don't know what this means)
    • Passes in the GeneralNames object with the Alternate DNS Name

(I'm looking for a code highlighting plugin for concrete5, if anyone knows of one...)


Here is what the working solution looks like:

public static byte[] Generate(string certCN, string signerCN, int bitStrength, String hType, String cType, DateTime validFrom, DateTime validTo)
            // Create a keypair
            var kpGenerator = new RsaKeyPairGenerator();
            kpGenerator.Init(new KeyGenerationParameters(new SecureRandom(), bitStrength));
            var kp = kpGenerator.GenerateKeyPair();

            // Create a certificate
            var cGenerator = new X509V3CertificateGenerator();
            var cCN = new X509Name("CN=" + certCN);
            var sCN = new X509Name("CN=" + signerCN);
            var serial = BigInteger.ProbablePrime(120, new Random());


            //------ Add a Subject Alternative Name -----
            GeneralNames subjectAltName = new GeneralNames(new GeneralName(GeneralName.DnsName, "*"));
            //subjectAltName = new GeneralNames(new GeneralName(GeneralName.Rfc822Name, ""));
            //subjectAltName = new GeneralNames(new GeneralName(GeneralName.DnsName, "*"));
            cGenerator.AddExtension(X509Extensions.SubjectAlternativeName, false, subjectAltName);

            // ---- generate the cert & return the encoded bytes -----
            var cert = cGenerator.Generate(kp.Private); // Self-signed for now

            return cert.GetEncoded();


Doesn't look to bad, does it? There were a few things I had to figure out before everything fell into place:

  1. Figure out that I should NOT pass in an X509Name into the constructor of GeneralName
    1. Finding out the correct constructor took longer than it should have. I could have found it quicker if I had narrowed my search earlier on
  2. Find out that the GeneralName object has to be encapsulated by a GeneralNames object.
    1. If you don't do this you get weirdness (more on that below)

Note: I left a line commented out in the 'complete' example so you can see the format for adding an Email address

Note 2: Here is how Windows displays the correct Subject Alt Names:



Some methods that didn't work

I tried a few crazy things before arriving at the correct method described above. Here are some of the more educational methods:


Method 1: Using Issuer Alternative Name instead of Subject Alternative Name


     GeneralNames issuerAltName = new GeneralNames(new GeneralName(new X509Name("CN=somedomain.tld")));
     cGenerator.AddExtension(X509Extensions.IssuerAlternativeName, false, issuerAltName);

This method was doomed from the start as I wasn't even using the correct OID in the AddExtension method. I did get a nice Issuer alt name, though.

Here's what the Windows Cert Browser showed when I examined the cert (slightly different cert shown- has multiples issuer alt names):



Method 2: Use Subject Alt Name along with a Directory Entry


     GeneralNames subjectAltName = new GeneralNames(new GeneralName(new X509Name("CN=somedomain.tld")));
     cGenerator.AddExtension(X509Extensions.IssuerAlternativeName, false, subjectAltName);

I did get closer to my goal with this one: It was at least populating the Subject alt Name field. Unfortunately it populated it with a bunch of Directory Entries which didn't work for me. I needed DNS Names!




Method 3: Add a DNSName typed GeneralName object directly to the certificate generator


cGenerator.AddExtension(X509Extensions.SubjectAlternativeName, false, new GeneralName(GeneralName.DnsName, ""));

I was pretty confident that this one would work as it had the correct type (DnsName) and the GeneralName object type is ASN1Encodable. Unfortunately it didn't work out quite how I expected. When viewed within windows the Subject Alt Name entry looks goofy:



Method 4: Try an Asn1Object instead of a GeneralName


     byte[] sanbyte = Encoding.ASCII.GetBytes("*");
     cGenerator.AddExtension(X509Extensions.SubjectAlternativeName, false, new GeneralName(GeneralName.DnsName, Asn1Object.FromByteArray(sanbyte)));

My thinking here was to bypass 'GeneralName' and see if I can't just get some arbitrary bytes encoded. That didn't work out so well, either. Clearly I wasn't supposed to do that:
(EndofStreamException was unhandled. DEF length 46 object truncated by 36) 


At this point I was pretty tired. Nothing that I had tried was working and it seemed less and less likely that I would be able to get a SAN generated. I finally tried encapsulating the GeneralName object inside of a GeneralNames before using AddExtension, which did the trick (See the top of the article for that solution).