Использование открытого ключа RSA для расшифровки строки, зашифрованной с использованием закрытого ключа RSA - PullRequest
10 голосов
/ 21 октября 2011

Я знаю, что основной ответ, который я, вероятно, получу, это то, почему, черт возьми, вы хотите это сделать ?!

К сожалению, несмотря на мои протесты, я должен это сделать, хотя я знаю, что это мало что значит.

У меня есть функции, написанные на .Net для дешифрования с использованием закрытого ключа, шифрования с использованием открытого ключа. Я также подписываю и проверяю RSA и хорошо понимаю, как все это работает, я думаю.

Теперь мне отправляется значение, которое зашифровано RSA с использованием личного ключа, и я должен получить полезное значение путем расшифровки с использованием открытого ключа.

Я не могу понять, как это сделать. Я идиот? Это нормальное занятие?

Человек, приславший мне значение, говорит мне, что это не проблема в PHP. Я не знаю и еще не использовал PHP. Я не могу найти библиотеку, чтобы сделать это на любом из основных языков, которые я знаю, то есть C ++, Java, C #. Сервер, на котором я работаю, использует .Net.

Я надеюсь, что кто-то сможет мне помочь.

Было бы замечательно, если бы было какое-то разумное решение, кроме того, чтобы просить их изменить то, что они делают.

Это мой метод (обновленный по сравнению с предыдущим, как указано Iridium), но когда я пытаюсь расшифровать значение, я получаю исключение

«Ошибка при декодировании заполнения OAEP.»

Если я использую rsa.Decrypt (байты, ложь), я получаю исключение неверного ключа.

public static string DecryptUsingPublic(string dataEncrypted, string publicKey)
    {
        if (dataEncrypted == null) throw new ArgumentNullException("dataEncrypted");
        if (publicKey == null) throw new ArgumentNullException("publicKey");
        try
        {
            RSAParameters _publicKey = LoadRsaPublicKey(publicKey, false);
            RSACryptoServiceProvider rsa = InitRSAProvider(_publicKey);

            byte[] bytes = Convert.FromBase64String(dataEncrypted);
            byte[] decryptedBytes = rsa.Decrypt(bytes, true);

            ArrayList arrayList = new ArrayList();
            arrayList.AddRange(decryptedBytes);

           return Encoding.UTF8.GetString(decryptedBytes);
        }
        catch
        {
            return null;
        }
    }

    private static RSAParameters LoadRsaPublicKey(String publicKeyFilePath, Boolean isFile)
    {
        RSAParameters RSAKeyInfo = new RSAParameters();
        byte[] pubkey = ReadFileKey(publicKeyFilePath, "PUBLIC KEY", isFile);
        byte[] SeqOID = { 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, 0x05, 0x00 };
        byte[] seq = new byte[15];
        // ---------  Set up stream to read the asn.1 encoded SubjectPublicKeyInfo blob  ------
        MemoryStream mem = new MemoryStream(pubkey);
        BinaryReader binr = new BinaryReader(mem);    //wrap Memory Stream with BinaryReader for easy reading
        byte bt = 0;
        ushort twobytes = 0;

        try
        {

            twobytes = binr.ReadUInt16();
            if (twobytes == 0x8130) //data read as little endian order (actual data order for Sequence is 30 81)
                binr.ReadByte();    //advance 1 byte
            else if (twobytes == 0x8230)
                binr.ReadInt16();   //advance 2 bytes
            else
                return RSAKeyInfo;

            seq = binr.ReadBytes(15);       //read the Sequence OID
            if (!CompareBytearrays(seq, SeqOID))    //make sure Sequence for OID is correct
                return RSAKeyInfo;

            twobytes = binr.ReadUInt16();
            if (twobytes == 0x8103) //data read as little endian order (actual data order for Bit String is 03 81)
                binr.ReadByte();    //advance 1 byte
            else if (twobytes == 0x8203)
                binr.ReadInt16();   //advance 2 bytes
            else
                return RSAKeyInfo;

            bt = binr.ReadByte();
            if (bt != 0x00)     //expect null byte next
                return RSAKeyInfo;

            twobytes = binr.ReadUInt16();
            if (twobytes == 0x8130) //data read as little endian order (actual data order for Sequence is 30 81)
                binr.ReadByte();    //advance 1 byte
            else if (twobytes == 0x8230)
                binr.ReadInt16();   //advance 2 bytes
            else
                return RSAKeyInfo;

            twobytes = binr.ReadUInt16();
            byte lowbyte = 0x00;
            byte highbyte = 0x00;

            if (twobytes == 0x8102) //data read as little endian order (actual data order for Integer is 02 81)
                lowbyte = binr.ReadByte();  // read next bytes which is bytes in modulus
            else if (twobytes == 0x8202)
            {
                highbyte = binr.ReadByte(); //advance 2 bytes
                lowbyte = binr.ReadByte();
            }
            else
                return RSAKeyInfo;
            byte[] modint = { lowbyte, highbyte, 0x00, 0x00 };   //reverse byte order since asn.1 key uses big endian order
            int modsize = BitConverter.ToInt32(modint, 0);

            byte firstbyte = binr.ReadByte();
            binr.BaseStream.Seek(-1, SeekOrigin.Current);

            if (firstbyte == 0x00)
            {   //if first byte (highest order) of modulus is zero, don't include it
                binr.ReadByte();    //skip this null byte
                modsize -= 1;   //reduce modulus buffer size by 1
            }

            byte[] modulus = binr.ReadBytes(modsize);   //read the modulus bytes

            if (binr.ReadByte() != 0x02)            //expect an Integer for the exponent data
                return RSAKeyInfo;
            int expbytes = (int)binr.ReadByte();        // should only need one byte for actual exponent data (for all useful values)
            byte[] exponent = binr.ReadBytes(expbytes);


            RSAKeyInfo.Modulus = modulus;
            RSAKeyInfo.Exponent = exponent;

            return RSAKeyInfo;
        }
        catch (Exception)
        {
            return RSAKeyInfo;
        }

        finally { binr.Close(); }
        //return RSAparams;

    }

 private static RSACryptoServiceProvider InitRSAProvider(RSAParameters rsaParam)
    {
        //
        // Initailize the CSP
        //   Supresses creation of a new key
        //
        CspParameters csp = new CspParameters();
        //csp.KeyContainerName = "RSA Test (OK to Delete)";

        const int PROV_RSA_FULL = 1;
        csp.ProviderType = PROV_RSA_FULL;

        const int AT_KEYEXCHANGE = 1;
        // const int AT_SIGNATURE = 2;
        csp.KeyNumber = AT_KEYEXCHANGE;
        //
        // Initialize the Provider
        //
        RSACryptoServiceProvider rsa =
          new RSACryptoServiceProvider(csp);
        rsa.PersistKeyInCsp = false;

        //
        // The moment of truth...
        //
        rsa.ImportParameters(rsaParam);
        return rsa;
    }

    private static int GetIntegerSize(BinaryReader binr)
    {
        byte bt = 0;
        byte lowbyte = 0x00;
        byte highbyte = 0x00;
        int count = 0;
        bt = binr.ReadByte();
        if (bt != 0x02)     //expect integer
            return 0;
        bt = binr.ReadByte();

        if (bt == 0x81)
            count = binr.ReadByte();    // data size in next byte
        else
            if (bt == 0x82)
            {
                highbyte = binr.ReadByte(); // data size in next 2 bytes
                lowbyte = binr.ReadByte();
                byte[] modint = { lowbyte, highbyte, 0x00, 0x00 };
                count = BitConverter.ToInt32(modint, 0);
            }
            else
            {
                count = bt;     // we already have the data size
            }

        while (binr.ReadByte() == 0x00)
        {   //remove high order zeros in data
            count -= 1;
        }
        binr.BaseStream.Seek(-1, SeekOrigin.Current);       //last ReadByte wasn't a removed zero, so back up a byte
        return count;
    }

    private static bool CompareBytearrays(byte[] a, byte[] b)
    {
        if (a.Length != b.Length)
            return false;
        int i = 0;
        foreach (byte c in a)
        {
            if (c != b[i])
                return false;
            i++;
        }
        return true;
    }

Два метода, описанные выше: InitRSAProvider и LoadRsaPublicKey, были получены из учебных пособий, позволяющих использовать ключи PEM в качестве строк для .Net.

Ответы [ 3 ]

14 голосов
/ 21 октября 2011

RSA встроен в .NET: System.Security.Cryptography.RSA.

Шифрование с использованием открытого ключа и дешифрование с помощью закрытого ключа - это одна из самых распространенных вещей, которые люди делают с асимметричными алгоритмами, она позволяет любому отправлять вам что-то безопасным образом.

Если вы делаете это другим способом: зашифровываете с помощью закрытого ключа и расшифровываете с помощью открытого ключа, то это подтверждает, что сообщение было отправлено владельцем закрытого ключа. Но поскольку предположительно любой может получить доступ к открытому ключу, люди не стремятся зашифровать все сообщение, вместо этого они просто подписывают хеш данных с помощью закрытого ключа. Следовательно, RSACryptoServiceProvider имеет Sign__ и Verify__ методы для этого.

Тем не менее, существуют Encrypt/Decrypt методы, если ваш партнер настаивает.

Сказав это, я обнаружил, что крипто-классы Microsoft немного сложны в работе и отсутствуют в некоторых областях, и я предпочитаю библиотеки Bouncy Castle .

12 голосов
/ 21 октября 2011

Изучив некоторую информацию о режимах шифрования RSA, вы увидите, что PKCS # 1 v1.5 (который вы используете, потому что вы звоните Decrypt(..., false))

«... может работать с сообщениями длиной до k - 11 октетов (k - длина октета модуля RSA)"

(RFC 3447, выделение мое).

На основании сообщения об ошибке, которое указывает, что ваш ключ имеет длину 128 байт, это означает, что вы не можете выполнить шифрование RSA (en | de) с использованием PKCS # 1 v1.5для сообщения длиной более 128 - 11 = 117 байт.

Вместо того чтобы шифровать ваше сообщение напрямую с помощью RSA, вы должны использовать симметричный алгоритм для шифрования тела сообщения и шифровать только симметричный ключ шифрования.используя RSA.Только если ваше сообщение достаточно короткое (т. Е. Размер ключа ниже 117 байт), вам следует подумать о шифровании сообщения напрямую с помощью RSA.

Я добавил следующее, предполагая, что ваш ввод закодирован Base64, как указано вВаш комментарий ниже:

public string DecryptUsingPublic(string dataEncryptedBase64, string publicKey)
    {
        if (dataEncryptedBase64 == null) throw new ArgumentNullException("dataEncryptedBase64");
        if (publicKey == null) throw new ArgumentNullException("publicKey");
        try
        {
            RSAParameters _publicKey = LoadRsaPublicKey(publicKey, false);
            RSACryptoServiceProvider rsa = InitRSAProvider(_publicKey);

            byte[] bytes = Convert.FromBase64String(dataEncryptedBase64);
            byte[] decryptedBytes = rsa.Decrypt(bytes, false);

            // I assume here that the decrypted data is intended to be a
            // human-readable string, and that it was UTF8 encoded.
            return Encoding.UTF8.GetString(decryptedBytes);
        }
        catch
        {
            return null;
        }
    }
6 голосов
/ 21 октября 2011

RSA - это , а не , предназначенное для шифрования произвольных данных, даже меньше произвольной длины данных (как вам уже сказал @Iridium).Предел зависит от используемого дополнения, и использование наполнения очень важно (достаточно, чтобы MS не позволяла вам напрямую вызывать EncryptValue и DecryptValue).

Правильный способ сделать это - зашифровать вашу строку с помощью симметричного шифра (например, AES), а затем зашифровать секретный ключ с помощью открытого ключа RSA.

Другая сторона сможет расшифровать секрет (AES).) ключ с использованием закрытого ключа RSA.Затем с помощью ключа расшифруйте вашу строку.

У меня есть старая (но все еще актуальная) запись в блоге на тему, которая включает исходный код (C #).

...