Различные результаты шифрования между платформами, используя OpenSSL

Я работаю над частью кросс-платформенного (Windows и Mac OS X) кода на C, который должен шифровать/дешифровать капли с использованием AES-256 с CBC и блочным размером 128 бит. Среди различных библиотек и API я выбрал OpenSSL.

Этот кусок кода затем загрузит blob с помощью многостраничного PUT на сервер, который затем расшифровывает его, используя те же настройки в .NET crypto framework (Aes, CryptoStream и т.д.).

Проблема, с которой я сталкиваюсь, заключается в том, что дешифрование сервера отлично работает, когда локальное шифрование выполняется в Windows, но оно не выполняется, когда шифрование выполняется в Mac OS X - сервер выдает "Недопустимое заполнение и исключение исключений".

Я рассматривал это со многих точек зрения:

  • Я подтвердил правильность транспортировки - массив байтов, полученный по методу дешифрования сервера, точно такой же, как и от Mac OS X и Windows
  • Фактическое содержимое зашифрованного blob для одного и того же ключа отличается от Windows и Mac OS X. Я протестировал это с помощью жесткого ключа и запустил этот патч в Windows и Mac OS X для того же blob
  • Я уверен, что исправление правильное, так как оно позаботится OpenSSL и поскольку тот же код работает для Windows. Тем не менее, я попробовал реализовать схему дополнений , как это в исходном источнике Microsoft для .NET, но все же, не хочешь
  • Я подтвердил, что IV одинаково для Windows и Mac OS X (я подумал, может быть, возникла проблема с некоторыми из специальных символов, таких как ETB, которые появляются в IV, но не было)
  • Я пробовал LibreSSL и mbedtls без положительных результатов. В mbedtls мне также пришлось реализовать дополнение, потому что, насколько мне известно, отладчик несет ответственность за пользователя API.
  • Я занимаюсь этой проблемой почти две недели, и я начинаю вытаскивать свои (когда-нибудь скудные) волосы.

В качестве справочной системы я отправлю код клиента C для шифрования и код сервера С# для дешифрования. Некоторые мелкие детали на стороне сервера будут опущены (они не мешают криптовому коду).

Клиент:

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
void
__setup_aes(EVP_CIPHER_CTX *ctx, const char *key, qvr_bool encrypt)
{
    static const char *iv = ""; /* for security reasons, the actual IV is omitted... */

    if (encrypt)
        EVP_EncryptInit(ctx, EVP_aes_256_cbc(), key, iv);
    else
        EVP_DecryptInit(ctx, EVP_aes_256_cbc(), key, iv);
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
void
__encrypt(void *buf,
    size_t buflen,
    const char *key,
    unsigned char **outbuf,
    size_t *outlen)
{
    EVP_CIPHER_CTX ctx;
    int blocklen = 0;
    int finallen = 0;
    int remainder = 0;

    __setup_aes(&ctx, key, QVR_TRUE);

    EVP_CIPHER *c = ctx.cipher;
    blocklen = EVP_CIPHER_CTX_block_size(&ctx);

    //*outbuf = (unsigned char *) malloc((buflen + blocklen - 1) / blocklen * blocklen);
    remainder = buflen % blocklen;
    *outlen = remainder == 0 ? buflen : buflen + blocklen - remainder;
    *outbuf = (unsigned char *) calloc(*outlen, sizeof(unsigned char));

    EVP_EncryptUpdate(&ctx, *outbuf, outlen, buf, buflen);
    EVP_EncryptFinal_ex(&ctx, *outbuf + *outlen, &finallen);

    EVP_CIPHER_CTX_cleanup(&ctx);
    //*outlen += finallen;
}

Сервер:

static Byte[] Decrypt(byte[] input, byte[] key, byte[] iv)
    {
        try
        {
            // Check arguments.
            if (input == null || input.Length <= 0)
                throw new ArgumentNullException("input");
            if (key == null || key.Length <= 0)
                throw new ArgumentNullException("key");
            if (iv == null || iv.Length <= 0)
                throw new ArgumentNullException("iv");

            byte[] unprotected;


            using (var encryptor = Aes.Create())
            {
                encryptor.Key = key;
                encryptor.IV = iv;
                using (var msInput = new MemoryStream(input))
                {
                    msInput.Position = 0;
                    using (
                        var cs = new CryptoStream(msInput, encryptor.CreateDecryptor(),
                            CryptoStreamMode.Read))
                    using (var data = new BinaryReader(cs))
                    using (var outStream = new MemoryStream())
                    {
                        byte[] buf = new byte[2048];
                        int bytes = 0;
                        while ((bytes = data.Read(buf, 0, buf.Length)) != 0)
                            outStream.Write(buf, 0, bytes);

                        return outStream.ToArray();
                    }
                }
            }
        }
        catch (Exception ex)
        {
            throw ex;
        }

    }

Кто-нибудь знает, почему это может произойти? Для справки, это метод .NET из исходного источника Microsoft.sln, который (я думаю) делает дешифрование: https://gist.github.com/Metaluim/fcf9a4f1012fdeb2a44f#file-rijndaelmanagedtransform-cs

Ответ 1

Я думаю, что схема дополнения AES была изменена между версиями OpenSSL 0.9.8 * и 1.0.1 * (по крайней мере между 0.9.8r и 1.0.1j). Если два из ваших модулей используют эти разные версии OpenSSL, это может быть причиной вашей проблемы. Чтобы проверить эту первую версию OpenSSL. Если вы нажмете на описанный случай, вы можете подумать о том, чтобы выравнивание схемы заполнения было одинаковым.