2

我正在尝试将我的程序从 Windows 移植到 Linux。windows 程序使用 Window CryptoAPI 而 linux 使用 libmcrypt。

这是 Windows 代码:

#include <windows.h>
#include <iostream>
#include <iomanip>
#include <vector>
#include <string>
#include <exception>

using namespace std;

class CryptError:public exception{
    public:
        CryptError(){}
};


#define CHECK_RET(x) if(x == FALSE) {retval = GetLastError(); throw CryptError();};

LONG Decrypt(const string &key, std::vector<BYTE> &data){
    LONG retval = 0;
    try{
        HCRYPTPROV hCrypt;
        HCRYPTHASH hHash;
        HCRYPTKEY hKey;
        CHECK_RET(CryptAcquireContext(&hCrypt, NULL, NULL, PROV_RSA_FULL, 0));
        CHECK_RET(CryptCreateHash(hCrypt, CALG_MD5, 0, 0, &hHash));
        CHECK_RET(CryptHashData(hHash, reinterpret_cast<const BYTE *>(key.c_str()), key.size(), 0));

        CHECK_RET(CryptDeriveKey(hCrypt, CALG_RC2, hHash, MAKELPARAM(CRYPT_EXPORTABLE, 80), &hKey));

        BYTE tempVal[200];
        DWORD len = 200;
        CryptGetKeyParam(hKey, KP_EFFECTIVE_KEYLEN, tempVal, &len, 0);

        len = 200;
        CryptGetKeyParam(hKey, KP_MODE, tempVal, &len, 0);

        len = 200;
        CryptExportKey(hKey, NULL, PLAINTEXTKEYBLOB, 0, tempVal, &len);

        len = 200;
        CryptGetKeyParam(hKey, KP_IV, tempVal, &len, 0);

        DWORD count = data.size();
        CHECK_RET(CryptDecrypt(hKey, 0, TRUE, 0, &(data[0]), &count));
        data.resize(count);
    }catch(CryptError &e){
    }

    return retval;
}

int main(void){
    BYTE data[9] = {0xdc,0x3d,0x96,0x23,0x29,0xdd,0x1b,0x2f, 0};
    vector<BYTE> vData(data, data + 8);

    Decrypt("PNEMAIL", vData);

    cerr << "vData: ";
    int len = vData.size();
    for(int i = 0; i < len; i++){
        if(i > 0)
            cerr << ',';
        cerr << hex << setw(2) << setfill('0') << (int)(vData[i]);
    }
    cerr << endl;

    return 0;
} 

当程序运行时,它返回:

vData: 42,46,30,41,43,34,31

Q&D linux 版本长这样:

#include <mcrypt.h>
#include <iostream>
#include <iomanip>
#include <string>
#include <openssl/md5.h>
#include <stdint.h>
#include <stdexcept>
#include <vector>
#include <valarray>
#include <memory.h>

using namespace std;

class MCrypt{
    private:
        MCRYPT mcrypt;
    public:
        MCrypt(char *algorithm, char* algorithm_directory, char *mode, char* mode_directory){
            mcrypt = mcrypt_module_open(algorithm, algorithm_directory, mode, mode_directory);
            if(mcrypt == MCRYPT_FAILED)
                throw runtime_error("MCrypt init failed");
        }

        int init(void *key, int lenofkey, void *IV){
            return mcrypt_generic_init(mcrypt, key, lenofkey, IV);
        }

        int enc_get_iv_size(){
            return mcrypt_enc_get_iv_size(mcrypt);
        }

        int deinit(){
            return mcrypt_generic_deinit(mcrypt);
        }

        int decrypt(void *data, int len){
            mdecrypt_generic(mcrypt, data, len);
        }

        ~MCrypt(){
            deinit();
            mcrypt_module_close(mcrypt);
        }
};

#ifdef DEBUG
void inline printArrayFunc(const char *start, const uint8_t *data, int len){
    // DEBUG: print value of $key1
    cerr << start;
    for(int i = 0; i < len; i++){
        if(i > 0)
            cerr << ',';
        cerr << hex << setw(2) << setfill('0') << (int)(data[i]);
    }
    cerr << endl;
}
#define printArray(start, data, len) printArrayFunc(start, data, len)
#else
#define printArray(start, data, len)
#endif

int main(void){
    uint8_t data[8] = {0xdc,0x3d,0x96,0x23,0x29,0xdd,0x1b,0x2f};
    const char *sKey1 = "PNEMAIL";
    const int key1Len = 7;

    uint8_t *dataPtr = &(data[0]);

    uint8_t key1[17];
    key1[16] = 0;

    // Hash sKey1
    MD5(reinterpret_cast<const unsigned char *>(sKey1), key1Len, key1);

    MCrypt mcrypt(MCRYPT_RC2, NULL, MCRYPT_CBC, NULL);

    vector<uint8_t> iv(mcrypt.enc_get_iv_size(), 0);

    // Use the first 80-bits of key1
    mcrypt.init(key1, 10, &(iv[0]));

    mcrypt.decrypt(dataPtr, 8);

    printArray("vData: ", dataPtr, 8);

    return 0;
}

当程序运行时,它返回:

vData: 4d,3d,82,71,88,d2,d5,4b

我检查了两个程序是否使用相同的数据。

  • CryptDeriveKey 创建一个密钥07,f1,e2,ea,d4,c8,79,74,03,a6(根据 CryptExportKey),与 Linux 中生成的 md5 的前 10 个字节相同(我将其缩短以匹配请求的 80 位密钥)。
  • 两者都没有在算法上使用盐(或者至少没有这样报告)
  • 他们都使用 {0,0,0,0,0,0,0,0} 的 8 字节 IV
  • 他们都使用 RC2 算法
  • 他们都使用CBC模式

我无法弄清楚他们为什么返回不同的数据。任何帮助将不胜感激。

4

0 回答 0