I'm writing some unit tests for a pre-existing / functioning method in an application that gathers the user principal name from a given certificate via
var upnName = currentUserCert.GetNameInfo(X509NameType.UpnName, false);
This works at run time however, I'm so far unable to retrieve this value from a BouncyCastle X509V3CertificateGenerator generated certificate. While I can get most of the way there I've been unable to generate a certificate that contains a upnName. I'm simply unsure how to set the user principal such that it may be retrieved via GetNameInfo when testing.
The test certificate itself is being generated as such
private X509Certificate2 GenerateCert(KeyPurposeID keyPurposeId, string subjectPrefix = "OID.1.2.3.4=", string validCertOriginFragment = "OU=SOME ORG")
{
const int keyStrength = 2048;
const string subject = "98876543210123";
// Generating Random Numbers
var randomGenerator = new CryptoApiRandomGenerator();
var random = new SecureRandom(randomGenerator);
var kpgen = new RsaKeyPairGenerator();
kpgen.Init(new KeyGenerationParameters(new SecureRandom(new CryptoApiRandomGenerator()), keyStrength));
var certificateGenerator = new X509V3CertificateGenerator();
var certName = new X509Name($"{subjectPrefix}{subject} + CN=JAMES BOND (Affiliate), {validCertOriginFragment}, O=SOME ORG, C=US");
var issuer = new X509Name("OU=CERT AUTH CA, OU=Certification Authorities, O=MYCERTAUTH, C=US");
var serialNo = BigInteger.ProbablePrime(120, new Random());
certificateGenerator.SetSerialNumber(serialNo);
certificateGenerator.SetSubjectDN(certName);
certificateGenerator.SetIssuerDN(issuer);
certificateGenerator.SetNotAfter(DateTime.Now.AddYears(50));
certificateGenerator.SetNotBefore(DateTime.Now);
// TODO setup upn name
var keyGenerationParameters = new KeyGenerationParameters(random, keyStrength);
var keyPairGenerator = new RsaKeyPairGenerator();
keyPairGenerator.Init(keyGenerationParameters);
var subjectKeyPair = keyPairGenerator.GenerateKeyPair();
certificateGenerator.SetPublicKey(subjectKeyPair.Public);
if (keyPurposeId != null)
{
certificateGenerator.AddExtension(
X509Extensions.ExtendedKeyUsage.Id,
false,
new ExtendedKeyUsage(keyPurposeId));
}
// Generating the Certificate
var issuerKeyPair = subjectKeyPair;
ISignatureFactory signatureFactory = new Asn1SignatureFactory("SHA256WITHRSA", issuerKeyPair.Private, random);
// selfsign certificate
var certificate = certificateGenerator.Generate(signatureFactory);
var x509 = new X509Certificate2(certificate.GetEncoded());
return x509;
}
As per the suggestion by @peop adding the following to the TODO of setup upn name
certificateGenerator.AddExtension("2.5.29.17", true,
new GeneralNames(
new GeneralName(GeneralName.OtherName,
new DerSequence(new DerObjectIdentifier("1.3.6.1.4.1.311.20.2.3"), new DerUtf8String($"{subject}@SOMEWHERE.COM")))
));
Has gotten me closer, I think, upon investigating the certificate I have an extra extension which is good, however the expected information is still not exposed via the GetNameInfo method. Is there something more that must happen to the certificate in order for the magical Microsoft GetNameInfo method to light up and return the expected value?
How do I create a certificate via the BouncyCastle X509V3CertificateGenerator such that I can retrieve the upnName for the sake of testing?