计算公钥密码 (.Net)

Jon*_*Jon 4 c# pki x509certificate http-headers

我想知道如何计算 X509 证书的公钥密码?

例如。我通过网络请求获得了证书

        var cert = (httpRequest as HttpWebRequest).ServicePoint.Certificate;
        X509Certificate2 cert2 = new X509Certificate2(cert);
Run Code Online (Sandbox Code Playgroud)

我不确定在此之后该怎么做,因为我需要主题公钥信息(对其进行哈希处理),但我在 X509Certificate2 类中找不到它,也无法确定如何构造它。我可以通过 GetKeyInfo() 方法获得指数和模数,这似乎是 SPKI 的核心。

我相信有一种简单的方法可以做到这一点,但任何帮助都会很棒!

谢谢

Ian*_*oyd 7

精简版

辅助方法:

String publicKeyPinningHash = certificate.GetPublicKeyPinningHash();

String s = String.Format("Public-Key-Pins: pin-sha256=\"{0}\"; 
                             max-age=31536000", publicKeyHash);
Run Code Online (Sandbox Code Playgroud)

Facebook.com 的内容是:

公钥引脚:pin-sha256="hUIG87ch71EZQYhZBEkq2VKBLjhussUw7nR8wyuY7rY="; 最大年龄=31536000

功能性 .NET 小提琴

https://dotnetfiddle.net/F9t6IQ

背景

正如您所发现的,.NET Framework 无法操作X509Certificate

X.509 证书使用 ASN.1 的 DER 风格进行编码。.NET Framework 无法操作AsnEcodedData

这意味着我们需要滚动自己的代码来计算 X.509 证书的 PublicKeyPinning 哈希:

static String GetPublicKeyPinningHash(X509Certificate2 x509Cert)
{
        //Public Domain: No attribution required
        //Get the SubjectPublicKeyInfo member of the certificate
        Byte[] subjectPublicKeyInfo = GetSubjectPublicKeyInfoRaw(x509Cert);

        //Take the SHA2-256 hash of the DER ASN.1 encoded value
        Byte[] digest;
        using (var sha2 = new SHA256Managed())
        {
            digest = sha2.ComputeHash(subjectPublicKeyInfo);
        }

        //Convert hash to base64
        String hash = Convert.ToBase64String(digest);

        return hash;
}
Run Code Online (Sandbox Code Playgroud)

此代码建立在 .NET 也不提供的较低功能上,以获取原始SubjectPublickeyInfo字节:

    static Byte[] GetSubjectPublicKeyInfoRaw(X509Certificate2 x509Cert)
    {
        //Public Domain: No attribution required
        Byte[] rawCert = x509Cert.GetRawCertData();

        /*
         Certificate is, by definition:

            Certificate  ::=  SEQUENCE  {
                tbsCertificate       TBSCertificate,
                signatureAlgorithm   AlgorithmIdentifier,
                signatureValue       BIT STRING  
            }

           TBSCertificate  ::=  SEQUENCE  {
                version         [0]  EXPLICIT Version DEFAULT v1,
                serialNumber         CertificateSerialNumber,
                signature            AlgorithmIdentifier,
                issuer               Name,
                validity             Validity,
                subject              Name,
                subjectPublicKeyInfo SubjectPublicKeyInfo,
                issuerUniqueID  [1]  IMPLICIT UniqueIdentifier OPTIONAL, -- If present, version MUST be v2 or v3
                subjectUniqueID [2]  IMPLICIT UniqueIdentifier OPTIONAL, -- If present, version MUST be v2 or v3
                extensions      [3]  EXPLICIT Extensions       OPTIONAL  -- If present, version MUST be v3
            }

        So we walk to ASN.1 DER tree in order to drill down to the SubjectPublicKeyInfo item
        */
        Byte[] list = AsnNext(ref rawCert, true); //unwrap certificate sequence
        Byte[] tbsCertificate = AsnNext(ref list, false); //get next item; which is tbsCertificate
        list = AsnNext(ref tbsCertificate, true); //unwap tbsCertificate sequence

        Byte[] version = AsnNext(ref list, false); //tbsCertificate.Version
        Byte[] serialNumber = AsnNext(ref list, false); //tbsCertificate.SerialNumber
        Byte[] signature = AsnNext(ref list, false); //tbsCertificate.Signature
        Byte[] issuer = AsnNext(ref list, false); //tbsCertificate.Issuer
        Byte[] validity = AsnNext(ref list, false); //tbsCertificate.Validity
        Byte[] subject = AsnNext(ref list, false); //tbsCertificate.Subject        
        Byte[] subjectPublicKeyInfo = AsnNext(ref list, false); //tbsCertificate.SubjectPublicKeyInfo        

        return subjectPublicKeyInfo;
    }
Run Code Online (Sandbox Code Playgroud)

它建立在较低级别的函数上,用于解析使用 ASN.1 的DER风格编码的 ASN.1 编码数据:

    static Byte[] AsnNext(ref Byte[] buffer, Boolean unwrap)
    {
        //Public Domain: No attribution required
        Byte[] result;

        if (buffer.Length < 2)
        {
            result = buffer;
            buffer = new Byte[0];
            return result;
        }

        int index = 0;
        Byte entityType = buffer[index];
        index += 1;

        int length = buffer[index];
        index += 1;

        int lengthBytes = 1;
        if (length >= 0x80)
        {
            lengthBytes = length & 0x0F; //low nibble is number of length bytes to follow
            length = 0;

            for (int i = 0; i < lengthBytes; i++)
            {
                length = (length << 8) + (int)buffer[2 + i];
                index += 1;
            }
            lengthBytes++;
        }

        int copyStart;
        int copyLength;
        if (unwrap)
        {
            copyStart = 1 + lengthBytes;
            copyLength = length;
        }
        else
        {
            copyStart = 0;
            copyLength = 1 + lengthBytes + length;
        }
        result = new Byte[copyLength];
        Array.Copy(buffer, copyStart, result, 0, copyLength);

        Byte[] remaining = new Byte[buffer.Length - (copyStart+copyLength)];
        if (remaining.Length > 0)
            Array.Copy(buffer, copyStart + copyLength, remaining, 0, remaining.Length);
        buffer = remaining;

        return result;
    }
Run Code Online (Sandbox Code Playgroud)