SubjectAlternativeName 中的非 ASCII 字符
Non-ASCII characters in SubjectAlternativeName
我正在尝试使用 BouncyCastle.NET
创建 self-signed X509 certificates
。它似乎在一般情况下工作,但我对包含非 ASCII 字符的国际化域名失败了。
下面是我正在做的一个简单的例子。 “myDevice.abc.example.com
”的单元测试用例成功,但对于包含 non-ASCII
个字符(如“myDevice.äöü.example.com
”)的其他用例则失败。
[TestCase("myDevice.abc.example.com")]
[TestCase("myDevice.äöü.example.com")] // western european
[TestCase("myDevice.ařa.example.com")] // eastern european
[TestCase("mydevice.aデa.example.com")] // katakana
[Test]
public void IdnTest(string fqdn)
{
#region Preparation
ECKeyPairGenerator kpgen = new ECKeyPairGenerator();
kpgen.Init(new KeyGenerationParameters(new SecureRandom(new CryptoApiRandomGenerator()), Constants.SelectedRootKeySize));
var caKeyPair = kpgen.GenerateKeyPair();
var certKeyPair = kpgen.GenerateKeyPair();
X509V3CertificateGenerator certGenerator = new X509V3CertificateGenerator();
certGenerator.SetSerialNumber(Org.BouncyCastle.Math.BigInteger.ProbablePrime(120, new Random()));
IList oids = new ArrayList() { X509Name.OU };
IList values = new ArrayList() { "Test" };
certGenerator.SetIssuerDN(new X509Name(oids, values));
certGenerator.SetNotBefore(DateTime.Now.Date);
certGenerator.SetNotAfter(DateTime.Now + TimeSpan.FromDays(365));
certGenerator.SetPublicKey(certKeyPair.Public);
//var dnsString = new Org.BouncyCastle.Asn1.DerIA5String(fqdn, true); //explicit validation would fail here
//var dnsName = new GeneralName(dnsString, GeneralName.DnsName);
var dnsName = new GeneralName(GeneralName.DnsName, fqdn); //here I can create an GeneralName without validation failure
GeneralNames subjectAltName = new GeneralNames(dnsName);
certGenerator.AddExtension(X509Extensions.SubjectAlternativeName, true, subjectAltName);
ISignatureFactory signatureFactory = new Asn1SignatureFactory("sha256WithECDSA", caKeyPair.Private, new SecureRandom(new CryptoApiRandomGenerator()));
Org.BouncyCastle.X509.X509Certificate x509Certificate = certGenerator.Generate(signatureFactory);
#endregion
#region Verification
var san = x509Certificate.GetSubjectAlternativeNames() as ArrayList;
Assert.AreEqual(1, san.Count);
var generalName = san[0] as System.Collections.ArrayList;
Assert.AreEqual(GeneralName.DnsName, generalName[0]);
object actual = generalName[1];
Assert.AreEqual(fqdn, actual);
#endregion
}
最终断言失败,从证书中检索到的字符串中的特殊字符被替换为'?'。
这应该行得通吗,我是不是做错了什么?
感谢 DJDaveMark and James K Polk I found the answer: The domain name must be encoded according to rfc 5280 在创建 GeneralName 之前的评论:
var idn = new System.Globalization.IdnMapping();
idn.UseStd3AsciiRules = true;
idn.AllowUnassigned = false;
string encodedFqdn = idn.GetAscii(fqdn);
var dnsName = new GeneralName(GeneralName.DnsName, encodedFqdn);
并且显然必须在读取证书后对其进行解码:
var actualEncodedFqdn = generalName[1];
var actual = idn.GetUnicode(actualEncodedFqdn.ToString());
StringAssert.AreEqualIgnoringCase(fqdn, actual);
我正在尝试使用 BouncyCastle.NET
创建 self-signed X509 certificates
。它似乎在一般情况下工作,但我对包含非 ASCII 字符的国际化域名失败了。
下面是我正在做的一个简单的例子。 “myDevice.abc.example.com
”的单元测试用例成功,但对于包含 non-ASCII
个字符(如“myDevice.äöü.example.com
”)的其他用例则失败。
[TestCase("myDevice.abc.example.com")]
[TestCase("myDevice.äöü.example.com")] // western european
[TestCase("myDevice.ařa.example.com")] // eastern european
[TestCase("mydevice.aデa.example.com")] // katakana
[Test]
public void IdnTest(string fqdn)
{
#region Preparation
ECKeyPairGenerator kpgen = new ECKeyPairGenerator();
kpgen.Init(new KeyGenerationParameters(new SecureRandom(new CryptoApiRandomGenerator()), Constants.SelectedRootKeySize));
var caKeyPair = kpgen.GenerateKeyPair();
var certKeyPair = kpgen.GenerateKeyPair();
X509V3CertificateGenerator certGenerator = new X509V3CertificateGenerator();
certGenerator.SetSerialNumber(Org.BouncyCastle.Math.BigInteger.ProbablePrime(120, new Random()));
IList oids = new ArrayList() { X509Name.OU };
IList values = new ArrayList() { "Test" };
certGenerator.SetIssuerDN(new X509Name(oids, values));
certGenerator.SetNotBefore(DateTime.Now.Date);
certGenerator.SetNotAfter(DateTime.Now + TimeSpan.FromDays(365));
certGenerator.SetPublicKey(certKeyPair.Public);
//var dnsString = new Org.BouncyCastle.Asn1.DerIA5String(fqdn, true); //explicit validation would fail here
//var dnsName = new GeneralName(dnsString, GeneralName.DnsName);
var dnsName = new GeneralName(GeneralName.DnsName, fqdn); //here I can create an GeneralName without validation failure
GeneralNames subjectAltName = new GeneralNames(dnsName);
certGenerator.AddExtension(X509Extensions.SubjectAlternativeName, true, subjectAltName);
ISignatureFactory signatureFactory = new Asn1SignatureFactory("sha256WithECDSA", caKeyPair.Private, new SecureRandom(new CryptoApiRandomGenerator()));
Org.BouncyCastle.X509.X509Certificate x509Certificate = certGenerator.Generate(signatureFactory);
#endregion
#region Verification
var san = x509Certificate.GetSubjectAlternativeNames() as ArrayList;
Assert.AreEqual(1, san.Count);
var generalName = san[0] as System.Collections.ArrayList;
Assert.AreEqual(GeneralName.DnsName, generalName[0]);
object actual = generalName[1];
Assert.AreEqual(fqdn, actual);
#endregion
}
最终断言失败,从证书中检索到的字符串中的特殊字符被替换为'?'。
这应该行得通吗,我是不是做错了什么?
感谢 DJDaveMark and James K Polk I found the answer: The domain name must be encoded according to rfc 5280 在创建 GeneralName 之前的评论:
var idn = new System.Globalization.IdnMapping();
idn.UseStd3AsciiRules = true;
idn.AllowUnassigned = false;
string encodedFqdn = idn.GetAscii(fqdn);
var dnsName = new GeneralName(GeneralName.DnsName, encodedFqdn);
并且显然必须在读取证书后对其进行解码:
var actualEncodedFqdn = generalName[1];
var actual = idn.GetUnicode(actualEncodedFqdn.ToString());
StringAssert.AreEqualIgnoringCase(fqdn, actual);