【问题标题】:Import Public RSA Key From Certificate从证书导入公共 RSA 密钥
【发布时间】:2012-09-17 12:41:11
【问题描述】:

我们的客户将其公共 RSA 密钥存储在证书中。

我们需要在我们的 WinRT 应用程序中硬编码此密钥,以便我们可以加密客户端。但是,我们在将密钥导入 CryptographicKey 类的实例时遇到了问题。

我们在 RSAProvider 上使用 ImportPublicKey:

rsaProvider = AsymmetricKeyAlgorithmProvider.OpenAlgorithm(AsymmetricAlgorithmNames.RsaPkcs1);
key = rsaProvider.ImportPublicKey(publicKeyBuffer);

我们尝试将几个东西加载到 publicKeyBuffer 中:证书,从证书导出的几种格式的公钥。

我们如何加载他们的公钥?

【问题讨论】:

    标签: c# windows-8 windows-runtime


    【解决方案1】:

    我发现 MSDN 论坛中的这篇文章很有帮助。 Carlos Lopez 发布了一些代码以从 Base64 编码的证书中获取公钥。

    http://social.msdn.microsoft.com/Forums/en-US/17e1467a-2de7-47d2-8d8c-130518eaac68/how-to-use-a-x509-certificate-not-a-pfx-to-verify-a-signature

    代码如下:

    public static CryptographicKey GetCryptographicPublicKeyFromCert(string strCert)
        {
            int length;
            CryptographicKey CryptKey = null;
    
            byte[] bCert = Convert.FromBase64String(strCert);
    
            // Assume Cert contains RSA public key 
            // Find matching OID in the certificate and return public key
            byte[] rsaOID = EncodeOID("1.2.840.113549.1.1.1");
            int index = FindX509PubKeyIndex(bCert, rsaOID, out length);
    
            // Found X509PublicKey in certificate so copy it.
            if (index > -1)
            {
                byte[] X509PublicKey = new byte[length];
                Array.Copy(bCert, index, X509PublicKey, 0, length);
    
                AsymmetricKeyAlgorithmProvider AlgProvider = AsymmetricKeyAlgorithmProvider.OpenAlgorithm(AsymmetricAlgorithmNames.RsaPkcs1);
                CryptKey = AlgProvider.ImportPublicKey(CryptographicBuffer.CreateFromByteArray(X509PublicKey));
            }
    
            return CryptKey;
        }
    
        static int FindX509PubKeyIndex(byte[] Reference, byte[] value, out int length)
        {
            int index = -1;
            bool found;
            length = 0;
    
            for (int n = 0; n < Reference.Length; n++)
            {
                if ((Reference[n] == value[0]) && (n + value.Length < Reference.Length))
                {
                    index = n;
                    found = true;
    
                    for (int m = 1; m < value.Length; m++)
                    {
                        if (Reference[n + m] != value[m])
                        {
                            found = false;
                            break;
                        }
                    }
    
                    if (found) break;
                    else index = -1;
                }
            }
    
            if (index > -1)
            {
                // Find outer Sequence
                while (index > 0 && Reference[index] != 0x30) index--;
                index--;
                while (index > 0 && Reference[index] != 0x30) index--;
            }
    
            if (index > -1)
            {
                // Find the length of encoded Public Key
                if ((Reference[index + 1] & 0x80) == 0x80)
                {
                    int numBytes = Reference[index + 1] & 0x7F;
                    for (int m = 0; m < numBytes; m++)
                    {
                        length += (Reference[index + 2 + m] << ((numBytes - 1 - m) * 8));
                    }
    
                    length += 4;
                }
                else
                {
                    length = Reference[index + 1] + 2;
                }
            }
    
            return index;
        }
    
        static public byte[] EncodeOID(string szOID)
        {
            int[] OIDNums;
            byte[] pbEncodedTemp = new byte[64];
            byte[] pbEncoded = null;
            int n, index, num, count;
    
            OIDNums = ParseOID(szOID);
    
            pbEncodedTemp[0] = 6;
    
            pbEncodedTemp[2] = Convert.ToByte(OIDNums[0] * 40 + OIDNums[1]);
            count = 1;
    
            for (n = 2, index = 3; n < OIDNums.Length; n++)
            {
                num = OIDNums[n];
    
                if (num >= 16384)
                {
                    pbEncodedTemp[index++] = Convert.ToByte(num / 16384 | 0x80);
                    num = num % 16384;
    
                    count++;
                }
    
                if (num >= 128)
                {
                    pbEncodedTemp[index++] = Convert.ToByte(num / 128 | 0x80);
                    num = num % 128;
    
                    count++;
                }
    
    
                pbEncodedTemp[index++] = Convert.ToByte(num);
                count++;
            }
    
            pbEncodedTemp[1] = Convert.ToByte(count);
    
            pbEncoded = new byte[count + 2];
            Array.Copy(pbEncodedTemp, 0, pbEncoded, 0, count + 2);
    
            return pbEncoded;
        }
    
        static public int[] ParseOID(string szOID)
        {
            int nlast, n = 0;
            bool fFinished = false;
            int count = 0;
            int[] dwNums = null;
    
            do
            {
                nlast = n;
                n = szOID.IndexOf(".", nlast);
                if (n == -1) fFinished = true;
                count++;
                n++;
            } while (fFinished == false);
    
            dwNums = new int[count];
    
            count = 0;
            fFinished = false;
    
            do
            {
                nlast = n;
                n = szOID.IndexOf(".", nlast);
                if (n != -1)
                {
                    dwNums[count] = Convert.ToInt32(szOID.Substring(nlast, n - nlast), 10);
                }
                else
                {
                    fFinished = true;
                    dwNums[count] = Convert.ToInt32(szOID.Substring(nlast, szOID.Length - nlast), 10);
                }
    
                n++;
                count++;
            } while (fFinished == false);
    
            return dwNums;
        }
    

    【讨论】:

      【解决方案2】:

      两件事:

      1. ImportPublicKey 键的参数是一个 IBuffer。最简单的方法是使用 byte[] 的 ToBuffer 扩展方法。
      2. 使用同时采用 IBuffer 和 CryptographicPublicKeyBlobTypeImportPublicKey 覆盖,特别是 CryptographicPublicKeyBlobType.X509SubjectPublicKeyInfo。传入证书中的主题公钥信息字段。

      【讨论】:

      【解决方案3】:

      对于那些想知道如何在 WinRT 应用程序中使用存储在证书中的公钥的人来说,让我减轻你的痛苦:你不能,至少不能直接。

      AsymmetricKeyAlgorithmProvider.ImportPublicKey 函数接受一个 IBuffer 和一个 CryptographicPublicKeyBlobType,keyBlob (IBuffer) 参数是证书的公钥,不是完整的证书,只是它的公钥。

      但是如果不先解析就无法获取证书的公钥,这就是问题所在,鉴于此任务最常用的类 X509Certificate,无法在 WinRT 上解析证书, 不可用,它的命名空间也不可用,并且证书工具只能用于 Web 服务连接。

      解决此问题的唯一方法是实现证书解析器,或从开源项目(如Bouncy Castle)移植此类功能。所以,如果你知道,请把它留在 cmets 中。

      顺便说一句,要以可在 WinRT 应用程序中使用的格式从证书(纯 .NET)中导出公钥,请使用以下命令:

      X509Certificate2 Certificate;
      ....
      byte[] CertificatePublicKey = Certificate.PublicKey.EncodedKeyValue.RawData;
      

      然后在 WinRT 应用中使用这个:

      AsymmetricKeyAlgorithmProvider algorithm = AsymmetricKeyAlgorithmProvider.OpenAlgorithm(AsymmetricAlgorithmNames.RsaSignPkcs1Sha1);
      IBuffer KeyBuffer = CryptographicBuffer.DecodeFromBase64String(CertificatePublicKeyContent);
      CryptographicKey key = algorithm.ImportPublicKey(KeyBuffer, CryptographicPublicKeyBlobType.Pkcs1RsaPublicKey);
      

      请注意,我首先以 base 64 对公钥进行了编码,但您可以使用原始二进制数据代替(CryptographicBuffer 类为此提供了更多方法)。

      【讨论】:

        猜你喜欢
        • 2023-01-17
        • 2013-06-09
        • 2018-08-03
        • 1970-01-01
        • 2012-12-11
        • 2021-03-03
        • 1970-01-01
        • 2012-07-17
        相关资源
        最近更新 更多