首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >证书扩展值包含2个额外字节(\u0004\u0002)八位字节编码

证书扩展值包含2个额外字节(\u0004\u0002)八位字节编码
EN

Stack Overflow用户
提问于 2020-07-17 11:03:13
回答 1查看 239关注 0票数 1

为了测试目的,我的单元测试使用BouncyCastle作为.NET核心生成一个带有自定义扩展的测试证书。

生成函数

代码语言:javascript
复制
static internal class CertificateGenerator
{
    public static X509Certificate2 GenerateCertificate(string region)
    {
        var randomGenerator = new CryptoApiRandomGenerator();
        var random = new SecureRandom(randomGenerator);
        var certificateGenerator = new X509V3CertificateGenerator();
        var serialNumber =
            BigIntegers.CreateRandomInRange(
                BigInteger.One, BigInteger.ValueOf(Int64.MaxValue), random);
        certificateGenerator.SetSerialNumber(serialNumber);
        const string signatureAlgorithm = "SHA1WithRSA";
        certificateGenerator.SetSignatureAlgorithm(signatureAlgorithm);
        var subjectDN = new X509Name("CN=FOOBAR");
        var issuerDN = subjectDN;
        certificateGenerator.SetIssuerDN(issuerDN);
        certificateGenerator.SetSubjectDN(subjectDN);
        var notBefore = DateTime.UtcNow.Date.AddHours(-24);
        var notAfter = notBefore.AddYears(1000);
        certificateGenerator.SetNotBefore(notBefore);
        certificateGenerator.SetNotAfter(notAfter);
      
        var fakeOid = "1.3.6.1.1.5.6.100.345434.345";
        if (region != null)
        {
            certificateGenerator.AddExtension(new DerObjectIdentifier(fakeOid), false, Encoding.ASCII.GetBytes(region));
        }

        const int strength = 4096;
        var keyGenerationParameters = new KeyGenerationParameters(random, strength);
        var keyPairGenerator = new RsaKeyPairGenerator();
        keyPairGenerator.Init(keyGenerationParameters);
        var subjectKeyPair = keyPairGenerator.GenerateKeyPair();

        certificateGenerator.SetPublicKey(subjectKeyPair.Public);

        var issuerKeyPair = subjectKeyPair;
        var certificate = certificateGenerator.Generate(issuerKeyPair.Private, random);

        var store = new Pkcs12Store();
        string friendlyName = certificate.SubjectDN.ToString();
        var certificateEntry = new X509CertificateEntry(certificate);
        store.SetCertificateEntry(friendlyName, certificateEntry);

        store.SetKeyEntry(friendlyName, new AsymmetricKeyEntry(subjectKeyPair.Private), new[] { certificateEntry });

        string password = "password";
        var stream = new MemoryStream();
        store.Save(stream, password.ToCharArray(), random);


        byte[] pfx = Pkcs12Utilities.ConvertToDefiniteLength(stream.ToArray(), password.ToCharArray());

        var convertedCertificate =
            new X509Certificate2(
                pfx, password,
                X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.Exportable);

        return convertedCertificate;
    }
}

阅读器

代码语言:javascript
复制
public class CertificateExtensionReader
{
    private readonly ILogger logger;

    public CertificateExtensionReader(ILogger logger)
    {
        this.logger = logger;
    }

    public CertificateExtensionValues ReadExtensionValues(byte[] certificate)
    {
        var x509Certificate2 = new X509Certificate2(certificate);
        var region = GetCustomExtensionValue(x509Certificate2.Extensions, new Oid("1.3.6.1.1.5.6.100.345434.345"));

        return new CertificateExtensionValues { Region = region };
    }

    private string GetCustomExtensionValue(X509ExtensionCollection x509Extensions, Oid oId)
    {
        var extension = x509Extensions[oId.Value];

        if(extension == null)
            throw new CertificateExtensionValidationException($"The client certificate does not contain the expected extensions '{oId.FriendlyName}' with OID {oId.Value}.");

        if (extension.RawData == null)
            throw new CertificateExtensionValidationException($"Device client certificate does not a value for the '{oId.FriendlyName}' extension with OID {oId.Value}");

        var customExtensionValue = Encoding.UTF8.GetString(extension.RawData).Trim();
        logger.LogInformation($"Custom Extension value for the '{oId.FriendlyName}' extension with OID {oId.Value}: '{customExtensionValue}'");
        return customExtensionValue;
    }
}

public class CertificateExtensionValues
{
    public string Region { get; set; }
}

测试

代码语言:javascript
复制
[TestFixture]
public class CertificateExtensionReaderFixture
{
    private ILogger logger = new NullLogger<CertificateExtensionReaderFixture>();
    private CertificateExtensionReader reader;

    [SetUp]
    public void Setup()
    {
        reader = new CertificateExtensionReader(logger);
    }

    [Test]
    public void ShouldReadExtensionValues()
    {
        var certificate = CertificateGenerator.GenerateCertificate("r1").Export(X509ContentType.Pfx);

        var values = reader.ReadExtensionValues(certificate);

        values.Region.Should().Be("r1");
    }
}

期望values.Region为长度为2的"r1“,但”u0004\u0002r1“的长度为4,与"\u0004\u0002r”(索引0)不同。

因此,BouncyCastle为扩展值添加了两个额外的字节\u0004\u0002 (传输结束,开始文本)。

我将证书保存到一个文件中,并通过certutil -dump -v test.pfx将其转储。

我做错了什么?这是证书的产生吗?还是我是如何解读这些价值观的?所有扩展值都是这样编码的吗?我只期望字符串字节。我在说明书里找不到什么东西。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2020-07-17 12:07:11

我做错什么了?

你的扩展是错误的。

代码语言:javascript
复制
certificateGenerator.AddExtension(new DerObjectIdentifier(fakeOid), false, Encoding.ASCII.GetBytes(region));

最后一个参数不正确。它必须包含任何有效的ASN.1类型。由于参数值是无效的ASN.1类型,BC假定它只是一个随机/任意的八进制字符串,并隐式地将原始值编码为ASN.1 OCTET_STRING类型。如果它应该是文本字符串,那么使用任何适合您的要求和char集的适用的ASN.1字符串类型。

您必须更新您的阅读器,以期望您选择的ASN.1字符串类型编码,然后从ASN.1字符串类型解码字符串值。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/62952657

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档