【问题标题】:Generate OpenSSL subject_name hash in C#在 C# 中生成 OpenSSL subject_name 哈希
【发布时间】:2016-08-13 20:40:54
【问题描述】:

我们正在尝试让主题名称哈希算法在 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");
}

在 openssl 中做这件事的原始代码可以参考这里:https://github.com/openssl/openssl/blob/master/crypto/x509/x509_cmp.c#L226

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

【问题讨论】:

标签: c# hash openssl


【解决方案1】:

我知道这是一个旧线程。但它可以帮助其他人。找出来并不容易。但我想我找到了一种迄今为止对我测试的证书有效的方法。你需要 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 证书的原因。

【讨论】:

  • 您的答案可以通过额外的支持信息得到改进。请edit 添加更多详细信息,例如引用或文档,以便其他人可以确认您的答案是正确的。你可以找到更多关于如何写好答案的信息in the help center
【解决方案2】:

正如 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://stackoverflow.com/a/3008775/773339
        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 个字符后分割行,因此这里通过使用堆栈溢出中的引用代码手动完成。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-12-21
    • 1970-01-01
    相关资源
    最近更新 更多