简体   繁体   English

从证书导入公共RSA密钥

[英]Import Public RSA Key From Certificate

Our customer has their public RSA key stored in a certificate. 我们的客户将其公共RSA密钥存储在证书中。

We need this key hardcoded in our WinRT app, so we can encrypt client-side. 我们需要在WinRT应用程序中对此密钥进行硬编码,以便我们可以对客户端进行加密。 However, we're having issues importing the key into an instance of the CryptographicKey class. 但是,将密钥导入CryptographicKey类的实例时遇到了问题。

We're using the ImportPublicKey on the RSAProvider: 我们在RSAProvider上使用ImportPublicKey:

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

We've tried loading several things into the publicKeyBuffer: The certificate, the public key exported from the certificate in several formats. 我们尝试将一些内容加载到publicKeyBuffer中:证书,即从证书导出的公钥,格式多种。

How do we load their public key? 我们如何加载他们的公钥?

I found this article in the MSDN Forum very helpful. 我在MSDN论坛中发现这篇文章非常有帮助。 Carlos Lopez postet some code to get the Public Key out of a Base64 encoded Certificate. 卡洛斯·洛佩兹(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 http://social.msdn.microsoft.com/Forums/zh-CN/17e1467a-2de7-47d2-8d8c-130518eaac68/how-to-use-a-x509-certificate-not-a-pfx-to-verify-一个签名

Here the code: 这里的代码:

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

Two things: 两件事情:

  1. The argument to ImportPublicKey key is an IBuffer. ImportPublicKey键的参数是IBuffer。 The easiest way to get this is using the ToBuffer extension method for a byte[]. 最简单的方法是将ToBuffer扩展方法用于byte []。
  2. Use the override of ImportPublicKey that takes both an IBuffer and a CryptographicPublicKeyBlobType , specifically CryptographicPublicKeyBlobType.X509SubjectPublicKeyInfo. 使用的覆盖ImportPublicKey一个同时的IBuffer和CryptographicPublicKeyBlobType ,特别CryptographicPublicKeyBlobType.X509SubjectPublicKeyInfo。 Pass in the subject public key info field from the certificate. 从证书中传递主题公用密钥信息字段。

For those banging his head as how you can use a public key stored in a certificate in a WinRT app, let me ease your pain: You can't, at least not directly. 对于那些如何使用WinRT应用程序中存储在证书中的公共密钥的人,让我减轻痛苦:您不能,至少不能直接这样做。

The AsymmetricKeyAlgorithmProvider.ImportPublicKey function takes an IBuffer and a CryptographicPublicKeyBlobType, the keyBlob (IBuffer) parameter it's the public key of the certificate, not the full certificate, only its public key. AsymmetricKeyAlgorithmProvider.ImportPublicKey函数采用IBuffer和CryptographicPublicKeyBlobType,keyBlob(IBuffer)参数是证书的公钥,而不是完整证书,仅是其公钥。

But you can't get the public key of the certificate with out parsing it first, here is where the problem lies, there is no way to parse the certificate on WinRT, given that the most used class for this task, X509Certificate, is not available, nor is its namespace, and the facilities for certificates are only to be used on web services connections. 但是您不能不先解析就获得证书的公钥,这是问题所在,没有办法在WinRT上解析证书,因为该任务最常用的类X509Certificate不是可用,其名称空间也不可用,并且证书的功能仅在Web服务连接上使用。

The only way to workaround this will be by implementing a certificate parser, or porting such functionality from an open source project, like Bouncy Castle . 解决此问题的唯一方法是实现证书解析器,或从诸如Bouncy Castle的开源项目中移植此类功能。 So, if you know one, please leave it in the comments. 因此,如果您知道一个,请在评论中保留它。

By the way, to export the public key from the certificate (in plain .NET) in a format that can be used in a WinRT app, use this: 顺便说一句,要以可在WinRT应用程序中使用的格式从证书(以纯.NET格式)导出公钥,请使用以下命令:

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

Then in the WinRT app use this: 然后在WinRT应用程序中使用以下命令:

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

Note that i encoded the public key in base 64 first, but you may use raw binary data instead (the CryptographicBuffer class has more methods for this purpose). 请注意,我首先在base 64中对公钥进行了编码,但是您可以改用原始二进制数据(CryptographicBuffer类为此目的提供了更多方法)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM