在C#中生成OpenSSL subject_name哈希

3

我们正在尝试在C#中使主题名称哈希算法运行。 您可以使用以下代码从openssl命令行运行它。

openssl x509 -inform PEM -hash -noout -in google.pem

这将返回一个8个字符的代码,它是证书主题的截断哈希。但是我们的代码不会返回相同的代码。

public static String GenerateX509SubjectNameHash(X509Certificate xCert)
{
    byte[] bytes = Encoding.ASCII.GetBytes(xCert.SubjectName.Name);
    HashAlgorithm hash = new SHA1Managed();
    var hashBytes = hash.ComputeHash(bytes);
    ulong hashVal = (((ulong)hashBytes[0]) | ((ulong)hashBytes[1] << 8) | ((ulong)hashBytes[2] << 16) | ((ulong)hashBytes[3] << 24) & 0xffffffff);

    return hashVal.ToString("X");
}

以下是相关的it技术内容的翻译:

在openssl中执行此操作的原始代码可以在此处查阅:https://github.com/openssl/openssl/blob/master/crypto/x509/x509_cmp.c#L226

我们做错了什么? --谢谢。


1
还可以参见在Java中生成X509Certificate的主题哈希。虽然它是关于Java的,但详细介绍了OpenSSL处理主题哈希的方法。 - jww
2个回答

0
正如Benny所提到的,这个线程已经相当老了,但我也曾处于同样的情况,即找不到一个真正令人满意的解决方案,首先是将.cer文件转换为.pem文件,其次是提取(在我的情况下)旧的主题哈希,这对于Android是必要的。最终我能找到的最好的解决方案是使用BouncyCastle nuget包。因此,以下代码基于该库。
using Org.BouncyCastle.X509;
using System;
using System.Collections.Generic;
using System.IO;
using System.Security.Cryptography;

namespace CertificateTest
{
    public class CertificateService
    {
        private readonly X509Certificate certificate;

        public string AsPem
        {
            get
            {
                var base64Pem = Convert.ToBase64String(certificate.GetEncoded());
                var wrappedPem = string.Join("\n", SplitByLength(base64Pem, 64));
                return $"-----BEGIN CERTIFICATE-----\n{wrappedPem}\n-----END CERTIFICATE-----";
            }
        }

        public string SubjectHash => CreateHash(certificate.SubjectDN.GetDerEncoded(), SHA1.Create());

        public string SubjectHashOld => CreateHash(certificate.SubjectDN.GetDerEncoded(), MD5.Create());

        public CertificateService(string certificateFilePath)
        {
            certificate = new X509CertificateParser().ReadCertificate(File.ReadAllBytes(certificateFilePath));
        }

        // hashing based on https://github.com/openssl/openssl/blob/master/crypto/x509/x509_cmp.c
        private string CreateHash(byte[] subjectName, HashAlgorithm hashAlgorithm)
        {
            var hashBytes = hashAlgorithm.ComputeHash(subjectName);
            ulong ret = (
                    ((ulong)hashBytes[0]) |
                    ((ulong)hashBytes[1] << 8) |
                    ((ulong)hashBytes[2] << 16) |
                    ((ulong)hashBytes[3] << 24)
                ) & 0xffffffff;
            return ret.ToString("X2").PadLeft(8, '0').ToLower();
        }

        // Taken from: https://dev59.com/UHA75IYBdhLWcg3w9-Ox#3008775
        private IEnumerable<string> SplitByLength(string str, int maxLength)
        {
            for (var index = 0; index < str.Length; index += maxLength)
            {
                yield return str.Substring(index, Math.Min(maxLength, str.Length - index));
            }
        }
    }
}

使用非常简单:

private static void Main(string[] args)
{
    var service = new CertificateService("my-certificate.pem"); // new CertificateService("my-certificate.cer");
    Debug.WriteLine(service.AsPem);
    Debug.WriteLine(service.SubjectHash);
    Debug.WriteLine(service.SubjectHashOld);
}

正如您所看到的,一些代码(例如哈希)我已经在像这样的线程中找到了。对我来说,真正的突破是使用BouncyCastle库而不是内置类,并完全加载证书并从中提取所有信息。手动解析和编码SubjectDN没有任何作用。

由于Convert.ToBase64String无法自行在64个字符后拆分行,因此在此手动使用来自堆栈溢出的引用代码进行操作。


0

我知道这是一个旧的帖子,但它可能会对其他人有所帮助。虽然不容易找到,但我认为我找到了一种方法,目前对我测试的证书有效。您需要使用bouncycastle的nuget包,然后使用以下代码。

 public async Task ExecuteAsync()
    {
        var issuer = GetIssuer(await GetServerCertificateAsync());
        System.Text.Encoding.RegisterProvider(System.Text.CodePagesEncodingProvider.Instance);


        var certString = Convert.ToBase64String(issuer.RawData);

        //Alles canonical maken (lowercase certificaat opvragen via bouncyCastle)
        //subject name omzetten naar Lowercase zoals OpenSSL doet.
        //een ASN1Object van maken alle bytes samen nemen en hash berekenen. WHOOPIEeee!!
        Org.BouncyCastle.X509.X509CertificateParser certParser = new Org.BouncyCastle.X509.X509CertificateParser();
        var cert = certParser.ReadCertificate(issuer.RawData);
        var subjectDNCanonical = new Org.BouncyCastle.Asn1.X509.X509Name(cert.SubjectDN.ToString().ToLower());
        var asn1Object = (Asn1Sequence)subjectDNCanonical.ToAsn1Object();
        List<byte[]> terms = new List<byte[]>();
        int finalLen = 0;
        foreach (var asn1Obj in asn1Object.ToArray())
        {
            byte[] term = asn1Obj.GetEncoded();
            term[9] = 0x0c; // change tag from 0x13 (printable string) to 0x0c
            terms.Add(term);
            finalLen += term.Length;
        }
        byte[] finalEncForw = new byte[finalLen];
        int j = 0;
        foreach (byte[] term in terms)
        {
            foreach (byte b in term)
            {
                finalEncForw[j++] = b;
            }
        }
        HashAlgorithm hash;
        hash = System.Security.Cryptography.SHA1.Create();
        var hashBytes = hash.ComputeHash(finalEncForw);
        ulong ret = (((ulong)hashBytes[0]) | ((ulong)hashBytes[1] << 8) | ((ulong)hashBytes[2] << 16) | ((ulong)hashBytes[3] << 24)) & 0xffffffff;
        Result = ret.ToString("X2").PadLeft(8, '0').ToLower();
    }

主要是.ToLower方法引起的问题。你不能使用.NET默认的X509Certificate2进行操作,否则只会显示主题的一部分,而不是完整的X509Name。所以你需要将它转换成BouncyCastle证书。


你的回答可以通过提供更多支持信息来改进。请编辑以添加进一步的细节,例如引用或文档,以便他人可以确认你的答案是正确的。您可以在帮助中心找到有关如何编写良好答案的更多信息。 - Community

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接