MS CryptoAPI дает неправильные результаты RC4?

Я работаю над продуктом, который должен иметь возможность использовать файлы, созданные с помощью более старого продукта. Некоторые из этих файлов содержат контент, зашифрованный с помощью шифрования RC4 с использованием MS CryptoAPI. До сих пор я не смог успешно расшифровать контент, используя другие криптографические библиотеки. После некоторых экспериментов выясняется, что вывод CryptoAPI RC4 является "правильным" (то есть согласуется с другими библиотеками), когда ключ является ASCII, но "неправильным" (отличается от других библиотек, которые все согласны), когда ключ не является ASCII (например, хеш пароля).

Поскольку весь интересующий меня контент был зашифрован ключом, который был получен из пароля посредством хеширования, я немного застрял на данный момент. Я написал небольшой тест, чтобы показать проблему, которая включает в себя 3 тестовых примера, как вы можете видеть ниже в коде. Botan (C++) и CryptoJS (JS) всегда соглашаются на вывод. MS CryptoAPI, однако, согласен только для ключей ASCII.

Прежде чем перейти к MS с этой проблемой, кто-нибудь знает о том, что я неправильно понимаю или делаю неправильно, что может вызвать эту проблему?

Кроме того, я прошу прощения за мой зверский JavaScript.

#pragma pack (push, 1)
struct PlainTextKeyBlob
{
    BLOBHEADER _hdr;
    DWORD      _cbKey;
    BYTE       _key[1];
};
#pragma pack (pop)

void TestBotanAndMSCryptoRC4()
{
    struct TestItem
    {
        std::string key;
        std::string plainText;
    };

    TestItem TestItems[] = {
    { "Secret",               "Attack at dawn" },  // Example taken from Wikipedia RC4 page to verify output.
    { "!\\\"#$%&'()*+",       "Encrypt me" } ,     // Key with various ASCII symbols.
    { "\xF4\xE7\xA8\x74\x0D", "Encrypt me" }       // Key is first 5 bytes of SHA1 hash of "Secret".
    };
    DWORD NumTestItems = _countof(TestItems);

    for( DWORD i = 0; i < NumTestItems; i++ )
    {
        // Botan Encryption
        Botan::SymmetricKey symmKey((BYTE*)TestItems[i].key.c_str(), TestItems[i].key.size());
        Botan::Pipe pipe(Botan::get_cipher("ARC4", symmKey, Botan::ENCRYPTION));
        pipe.process_msg(TestItems[i].plainText);
        SecureByteVector& encryptedBuff = pipe.read_all();

        // MS Crypto API Encryption
        AutoCryptProv CryptProv;
        if( !CryptAcquireContext( CryptProv, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT))
        {
            ASSERT(false);
            return;
        }

        DWORD blobKeySize = TestItems[i].key.size();
        DWORD blobSize    = sizeof(PlainTextKeyBlob) + blobKeySize - 1;
        CByteArray keyBlob;
        keyBlob.SetSize(blobSize);

        PlainTextKeyBlob *pKeyBlob = reinterpret_cast<PlainTextKeyBlob*>(keyBlob.GetData());
        pKeyBlob->_hdr.bType       = PLAINTEXTKEYBLOB;
        pKeyBlob->_hdr.bVersion    = CUR_BLOB_VERSION;
        pKeyBlob->_hdr.reserved    = 0;
        pKeyBlob->_hdr.aiKeyAlg    = CALG_RC4;
        pKeyBlob->_cbKey           = TestItems[i].key.size();
        memcpy_s(pKeyBlob->_key, blobKeySize, TestItems[i].key.c_str(), TestItems[i].key.size());

        AutoCryptKey CryptKey;
        if( !CryptImportKey(CryptProv, reinterpret_cast<BYTE*>(pKeyBlob), blobSize, NULL, 0, CryptKey) )
        {
            ASSERT(false);
            return;
        }

        CByteArray dataBytes;
        dataBytes.SetSize(TestItems[i].plainText.size());
        memcpy_s(dataBytes.GetData(), dataBytes.GetSize(), TestItems[i].plainText.c_str(), TestItems[i].plainText.size());

        DWORD buffSize = dataBytes.GetSize();
        if( !CryptEncrypt(CryptKey, 0, TRUE, 0, dataBytes.GetData(), &buffSize, dataBytes.GetSize()) )
        {
            ASSERT(false);
            return;
        }

        ASSERT(encryptedBuff.size() == dataBytes.GetSize());
        ASSERT( 0 == memcmp(encryptedBuff.begin(), dataBytes.GetData(), dataBytes.GetSize()) );
    }
}

<html>
<body>
<script src="http://crypto-js.googlecode.com/svn/tags/3.1.2/build/rollups/rc4.js"></script>
<div id="output" />
<script>
    var key1 = CryptoJS.enc.Hex.parse('536563726574'); // Secret
    var message1 = "Attack at dawn";
    var encrypted1 = CryptoJS.RC4.encrypt(message1, key1);

    var key2 = CryptoJS.enc.Hex.parse('215c22232425262728292a2b'); // !\"#$%&'()*+
    var message2 = "Encrypt me";
    var encrypted2 = CryptoJS.RC4.encrypt(message2, key2);

    var key3 = CryptoJS.enc.Hex.parse('f4e7a8740d'); // First 5 bytes of hash of "Secret"
    var message3 = "Encrypt me";
    var encrypted3 = CryptoJS.RC4.encrypt(message3, key3);

    var elem = document.getElementById("output");
    elem.innerHTML = "Key1: " + encrypted1.key + "<br> ciphertext1: " + encrypted1.ciphertext + "<br><br>" +
                     "Key2: " + encrypted2.key + "<br> ciphertext2: " + encrypted2.ciphertext + "<br><br>" +
                     "Key3: " + encrypted3.key + "<br> ciphertext3: " + encrypted3.ciphertext;
</script>
</body>
</html>

1 ответ

Решение

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

В первую очередь ответ был на RTFM! Как оказалось, по умолчанию CryptDeriveKey добавляет "соль" к 40-битным симметричным ключам, состоящим из всех 0. Когда я посмотрел на наш старый код CryptoAPI и увидел, что мы не передавали ему какие-либо флаги, я предположил, что это означает, что ничего особенного не происходит, и я не читал подробности всех возможных флагов в MSDN. Кроме того, поскольку MS считает эту часть ключа солью, она не включается при экспорте ключа, так что это тоже тупик.

В конце концов я нашел свой путь к http://msdn.microsoft.com/en-us/library/windows/desktop/aa387695(v=vs.85).aspx который объясняет механизм соли для 40-битных ключей и строку, которая я обратил внимание, что для совместимости вы должны создавать ключи с флагом CRYPT_NO_SALT. В нашем случае у нас уже есть зашифрованный контент с такими ключами, поэтому мы просто изменили наш код Botan/CryptoJS, добавив 11 байтов 0 к концу базового 40-битного ключа.

Подробную информацию о различных флагах CryptDeriveKey можно найти здесь: http://msdn.microsoft.com/en-us/library/windows/desktop/aa379916(v=vs.85).aspx

Другие вопросы по тегам