我正在为应用程序中的预先存在/正常运行的方法编写一些单元测试,该应用程序通过
从给定证书收集用户主体名称var upnName = currentUserCert.GetNameInfo(X509NameType.UpnName, false);
这在运行时有效,但到目前为止,我还无法从BouncyCastle X509V3CertificateGenerator生成的证书中检索此值。虽然我可以在大部分时间内完成,但我无法生成包含 upnName 的证书。我只是不确定如何设置用户主体,以便在测试时可以通过 GetNameInfo 检索它。
测试证书本身正在生成
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;
}
根据@peop的建议,将以下内容添加到设置名称
的TODO中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")))
));
让我更接近,我认为,在调查证书后,我有一个额外的扩展,这是好的,但是预期的信息仍然没有通过GetNameInfo方法公开。为了让神奇的Microsoft GetNameInfo方法点亮并返回预期值,证书上是否还有其他必须发生的事情?
如何通过BouncyCastle X509V3CertificateGenerator创建证书,以便我可以检索 upnName 以进行测试?
答案 0 :(得分:1)
根据this page,UPN应该是SubjectAlternativeName(SAN)扩展的一部分。
主题备选名称=其他名称:主要名称=(UPN)。例如: UPN = user1@name.com UPN OtherName OID是:“1.3.6.1.4.1.311.20.2.3” UPN OtherName值:必须是ASN1编码的UTF8字符串
可以找到使用X509V3CertificateGenerator生成SAN扩展的示例here。
// example of adding email address to SAN
certGen.AddExtension("2.5.29.17", true,
new GeneralNames(new GeneralName(GeneralName.Rfc822Name, "test@test.test")));
如此接近,但我们需要GeneralName.OtherName
,其定义为here。
所以我应该做的伪代码(写在记事本中)看起来像这样:
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(upnName)))
));
希望我做的ASN.1结构正确 - 顺序 - >(oid,utf8string)。最好的方法是使用ASN.1 editor解析现有证书,以检查扩展的ASN.1结构。
答案 1 :(得分:1)
Microsoft提供Guidelines for enabling smart card logon with third-party certification authorities。在该文档中,枚举了证书的特定格式要求:
CRL分发点(CDP)位置(CRL为 证书撤销清单)必须填写,在线和 可用。例如:
[1] CRL分发点
分发点
名称:
全名:网址=http://server1.name.com/CertEnroll/caname.crl
密钥用法 =数字签名
基本约束 [主题类型=最终实体,路径长度约束=无(可选)
增强型密钥用法 =
- 客户端验证(1.3.6.1.5.5.7.3.2)
(仅在使用证书时才需要客户端身份验证OID) 用于SSL身份验证。)- 智能卡登录(1.3.6.1.4.1.311.20.2.2)
主题备用名称 =其他名称:主要名称=(UPN)。例如:
UPN = user1@name.com
UPN OtherName OID是:" 1.3.6.1.4.1.311.20.2.3"
UPN OtherName值:必须是ASN1编码的UTF8字符串主题 =用户的专有名称。此字段是必填字段,但此字段的填充是可选的。
考虑到格式要求和证书生成代码(包括您基于pepo's answer添加的UPN),您需要修改要添加证书扩展的部分:
if (keyPurposeId != null)
{
certificateGenerator.AddExtension(
X509Extensions.ExtendedKeyUsage.Id,
false,
new ExtendedKeyUsage(keyPurposeId));
// new extension not present in your question's code
certificateGenerator.AddExtension(
"1.3.6.1.5.5.7.3.2",
false,
new ExtendedKeyUsage(KeyPurposeID.IdKPClientAuth));
}
执行此操作应创建一个可用作X509Certificate2
GetNameInfo(X509NameType.UpnName, false);
实例的证书。
答案 2 :(得分:1)
以下内容适用于我并生成了具有“使用者备用名称”属性的证书,该证书的格式与Microsoft证书服务在用于用户/客户端身份验证时生成的证书的格式相同。也就是说,在Windows中查看.cer文件的“使用者备用名称”属性时,您会看到:
Other Name: Principal Name=john.doe@contoso.com RFC822 Name=john.doe@contoso.com
certificateGenerator.AddExtension(X509Extensions.SubjectAlternativeName.Id, false, new DerSequence(
new GeneralName(GeneralName.OtherName,
new DerSequence(new DerObjectIdentifier("1.3.6.1.4.1.311.20.2.3"),
new DerTaggedObject(0, new DerUtf8String("john.doe@contoso.com")))),
new GeneralName(GeneralName.Rfc822Name, "john.doe@costoso.com")));
关键是将UPN字符串包装在DerTaggedObject
中。根据{{3}}的建议,我使用pepo查看现有证书。它围绕这些值有一堆特定于上下文的节点。因此,我对此进行了研究ASN.1 Editor,这使我进入了DerTaggedObject
。从那里开始,我刚开始尝试一些代码,直到我生成的证书的输出与Microsoft证书服务创建的证书的输出相匹配。
另请注意,主题备用名称的扩展名未标记为严重(AddExtension
方法的第二个参数为false
)。这再次与Microsoft证书服务生成的内容相匹配。