Использование открытого ключа RSA для дешифрования строки, которая была зашифрована с помощью RSA Private Key

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

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

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

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

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

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

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

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

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

"Ошибка при декодировании OAEP-дополнения".

Если я использую rsa.Decrypt(bytes, false), я получаю исключение из "плохого ключа".

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.

Ответ 1

Посмотрев на некоторую информацию о режимах шифрования 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;
        }
    }

Ответ 2

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

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

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

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

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

Ответ 3

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

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

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

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