Bouncy Castle - Missing Certificate Attributes
Over the last couple of days, we’ve seen how to use Bouncy Castle to generate certificates from C#. However, if you compare the certificates we’ve been generating with those generated by (e.g.)
makecert, you’ll see that we’re missing a few things.
To generate a self-signed certificate using
makecert, you can just run the following command from a command prompt (make sure that
makecert is in your path):
makecert -r -pe -n "CN=makecert" -ss My -sr CurrentUser -a sha256 -cy end -sky signature
This creates and installs a certificate. If you compare it to the certificates we’ve been generating with Bouncy Castle, you’ll see that we’re missing the following attributes:
- “Basic Constraints”
- “Authority Key Identifier”
Authority Key Identifier
According to this page, the “Authority Key Identifier” is used to identify “the public key to be used to verify the signature on this certificate”. That is: it should basically be the issuer’s public key.
The Bouncy Castle documentation, here, shows how to create this attribute in Java. To do the same in .NET, we need something like the following:
var authorityKeyIdentifier = new AuthorityKeyIdentifierStructure(issuerCertificate); certificateGenerator.AddExtension( X509Extensions.AuthorityKeyIdentifier.Id, false, authorityKeyIdentifier);
..but, unfortunately, this is a self-signed certificate, so we don’t have
issuerCertificate. By looking at the source code (or by using Reflector), we can see that this does (more-or-less) the following:
var issuerPublicKeyInfo = SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(issuerCertificate.GetPublicKey()); var issuerGeneralNames = new GeneralNames(issuerCertificate.IssuerDN); var issuerSerialNumber = issuerCertificate.SerialNumber; var authorityKeyIdentifier = new AuthorityKeyIdentifier(issuerPublicKeyInfo, issuerGeneralNames, issuerSerialNumber);
But: we don’t have an issuer certificate, remember. We’re creating a self-signed certificate here. We need something like the following:
// Self-signed, so it's all the same. var issuerKeyPair = subjectKeyPair; var issuerDN = subjectDN; var issuerSerialNumber = serialNumber; var authorityKeyIdentifier = new AuthorityKeyIdentifier( SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(issuerKeyPair.Public), new GeneralNames(new GeneralName(issuerDN)), issuerSerialNumber); certificateGenerator.AddExtension( X509Extensions.AuthorityKeyIdentifier.Id, false, authorityKeyIdentifier);
Subject Key Identifier
If you look at a certificate issued by Active Directory, it also has a “Subject Key Identifier” (see here). We might as well add this attribute as well.
This is simpler:
var subjectKeyIdentifier = new SubjectKeyIdentifier( SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(subjectKeyPair.Public)); certificateGenerator.AddExtension( X509Extensions.SubjectKeyIdentifier.Id, false, subjectKeyIdentifier);
The “Basic Constraints” attribute corresponds to the
-cy end or
-cy authority switch passed to
makecert. To add it:
certificateGenerator.AddExtension( X509Extensions.BasicConstraints.Id, true, new BasicConstraints(false));
The boolean value (
false here) corresponds to whether this is a CA certificate or not.
false corresponds to
-cy end, and
For the “Authority Key Identifier” and “Subject Key Identifier” extensions, we passed
false as the second parameter to
AddExtension. For the “Basic Constraints” extension, we passed
true. What’s that all about?
This boolean controls whether the extension is considered “critical” or not. According to RFC 5280:
A certificate-using system MUST reject the certificate if it encounters a critical extension it does not recognize or a critical extension that contains information that it cannot process. A non-critical extension MAY be ignored if it is not recognized, but MUST be processed if it is recognized.
That’s it for now. In my next article, I’ll show how to add extended key usage (EKU) flags.