Import Public RSA Key From Certificate

后端 未结 3 1956
情歌与酒
情歌与酒 2021-01-28 13:08

Our customer has their public RSA key stored in a certificate.

We need this key hardcoded in our WinRT app, so we can encrypt client-side. However, we\'re having issues

相关标签:
3条回答
  • 2021-01-28 13:09

    I found this article in the MSDN Forum very helpful. Carlos Lopez postet some code to get the Public Key out of a Base64 encoded Certificate.

    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

    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;
        }
    
    0 讨论(0)
  • 2021-01-28 13:14

    Two things:

    1. The argument to ImportPublicKey key is an IBuffer. The easiest way to get this is using the ToBuffer extension method for a byte[].
    2. Use the override of ImportPublicKey that takes both an IBuffer and a CryptographicPublicKeyBlobType, specifically CryptographicPublicKeyBlobType.X509SubjectPublicKeyInfo. Pass in the subject public key info field from the certificate.
    0 讨论(0)
  • 2021-01-28 13:25

    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.

    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.

    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.

    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. 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:

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

    Then in the WinRT app use this:

    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).

    0 讨论(0)
提交回复
热议问题