【问题标题】:Calculate Public Key Pin (.Net)计算公钥密码 (.Net)
【发布时间】:2017-01-19 08:47:55
【问题描述】:

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

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

        var cert = (httpRequest as HttpWebRequest).ServicePoint.Certificate;
        X509Certificate2 cert2 = new X509Certificate2(cert);

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

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

谢谢

【问题讨论】:

  • 所以基本上你是在尝试从cert 对象构造一个新证书?
  • 嗯,不是真的 - 我正在使用从 Web 请求中检索到的那个,只是将它包装在 X509Certificate2 中,因为它上面有更多信息。
  • 您在寻找什么样的 Pin 图?

标签: c# http-headers x509certificate pki


【解决方案1】:

公钥固定意味着验证证书的公钥是否正确。公钥不是您必须计算的东西,它包含在证书中的明文中。我认为您的意思是您要计算公钥的哈希值(固定并不意味着哈希,它意味着与已知值进行比较)。如果您知道后端的 URL 或 IP,则可以轻松提取公钥。

此页面的 chrome 示例(您需要 OpenSSL 工具):

  1. 点击网址左侧的绿色锁
  2. 点击详情
  3. 点击安全中的“查看证书”
  4. 转到“详细信息”选项卡和“复制到文件”并保存在某处
  5. 使用命令

    openssl x509 -inform der -in yourcertificate.cer -pubkey -noout | openssl rsa -pubin -outform der | openssl dgst -sha256 -binary

它将返回公钥的 sha256 哈希

【讨论】:

    【解决方案2】:

    短版

    辅助方法:

    String publicKeyPinningHash = certificate.GetPublicKeyPinningHash();
    
    String s = String.Format("Public-Key-Pins: pin-sha256=\"{0}\"; 
                                 max-age=31536000", publicKeyHash);
    

    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;
    }
    

    此代码建立在 .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;
        }
    

    它基于较低级别的函数来解析使用 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;
        }
    

    【讨论】:

    • 终于找到解决办法了。一直在寻找那个 27 小时。谢谢。非常感谢。
    猜你喜欢
    • 2016-03-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-06-23
    相关资源
    最近更新 更多