簡體   English   中英

在Crypto ++中獲取Unicode字符串的SHA1

[英]Get SHA1 of Unicode string in Crypto++

我獨立學習C ++,我遇到了一個問題,我不能解決這個問題。 我希望你能幫助我。

我需要獲取Unicode字符串的SHA1摘要(如Привет ),但我不知道如何做到這一點。

我嘗試這樣做,但它返回錯誤的摘要!

對於wstring('Ы')它返回 - A469A61DF29A7568A6CC63318EA8741FA1CF2A7
我需要 - 8dbe718ab1e0c4d75f7ab50fc9a53ec4f0528373

關心並抱歉我的英語:)。

CryptoPP 5.6.2 MVC ++ 2013

#include <iostream>
#include "cryptopp562\cryptlib.h"
#include "cryptopp562\sha.h"
#include "cryptopp562\hex.h"

int main() {

    std::wstring string(L"Ы");
    int bs_size = (int)string.length() * sizeof(wchar_t);

    byte* bytes_string = new byte[bs_size];

    int n = 0; //real bytes count
    for (int i = 0; i < string.length(); i++) {
        wchar_t wcharacter = string[i];

        int high_byte = wcharacter & 0xFF00;

        high_byte = high_byte >> 8;

        int low_byte = wcharacter & 0xFF;

        if (high_byte != 0) {
            bytes_string[n++] = (byte)high_byte;
        }

        bytes_string[n++] = (byte)low_byte;
    }

    CryptoPP::SHA1 sha1;
    std::string hash;

    CryptoPP::StringSource ss(bytes_string, n, true,
        new CryptoPP::HashFilter(sha1,
            new CryptoPP::HexEncoder(
                new CryptoPP::StringSink(hash)
            ) 
        ) 
    );

    std::cout << hash << std::endl;

    return 0;
}

你說'但它會返回錯誤的摘要' - 你在比較它是什么?

關鍵點:諸如SHA-1之類的摘要不適用於字符序列,而是使用字節序列。

你在這段代碼中所做的就是在字符串"Ы"生成unicode字符的ad-hoc 編碼 如果字符串中的字符全部在BMP中(“基本多語言平面”,在這種情況下為真), 並且最終在wcharacter中的數字是,那么這種編碼將(結果證明)匹配UTF-16編碼。表示unicode代碼點的整數(這可能是正確的,但我認為不保證)。

如果您正在比較的摘要將輸入字符串轉換為使用UTF-8編碼的字節序列(這很可能),那么這將產生與您的字節序列不同的字節序列,以便SHA-1摘要該序列將與您在此處計算的摘要不同。

所以:

  • 檢查測試字符串使用的編碼。

  • 您最好使用一些庫函數來專門生成要處理的字符串的UTF-16或UTF-8(視情況而定)編碼,以確保您正在使用的字節序列是您認為的是。

在適當命名的文檔中有一個關於unicode和編碼的出色介紹絕對最少,每個軟件開發人員絕對必須知道Unicode和字符集(沒有借口!)

我需要獲取Unicode字符串的SHA1摘要(如Привет),但我不知道如何做到這一點。

這里的技巧是你需要知道如何編碼Unicode字符串。 在Windows上, wchar_t是2個八位字節; 而在Linux上, wchar_t是4 otects。 字符集注意事項上有一個Crypto ++維基頁面,但它不是那么好。

為了最有效地進行互操作,請始終使用UTF-8。 這意味着您將UTF-16或UTF-32轉換為UTF-8。 因為您在Windows上,所以您需要調用WideCharToMultiByte函數以使用CP_UTF8進行轉換。 如果您使用的是Linux,那么您將使用libiconv

Crypto ++有一個名為StringNarrow的內置函數,它使用C ++。 它在文件misc.h 務必在使用之前調用setlocale

Stack Overflow有一些關於使用Windows功能的問題。 例如,請參閱如何正確使用WideCharToMultiByte


我需要 - 8dbe718ab1e0c4d75f7ab50fc9a53ec4f0528373

什么是哈希值(SHA-1,SHA-256,...)? 它是HMAC(鍵控哈希)嗎? 信息是否被腌制(如存儲中的密碼)? 它是如何編碼的? 我不得不問,因為我無法重現你想要的結果:

SHA-1:   2805AE8E7E12F182135F92FB90843BB1080D3BE8
SHA-224: 891CFB544EB6F3C212190705F7229D91DB6CECD4718EA65E0FA1B112
SHA-256: DD679C0B9FD408A04148AA7D30C9DF393F67B7227F65693FFFE0ED6D0F0ADE59
SHA-384: 0D83489095F455E4EF5186F2B071AB28E0D06132ABC9050B683DA28A463697AD
         1195FF77F050F20AFBD3D5101DF18C0D
SHA-512: 0F9F88EE4FA40D2135F98B839F601F227B4710F00C8BC48FDE78FF3333BD17E4
         1D80AF9FE6FD68515A5F5F91E83E87DE3C33F899661066B638DB505C9CC0153D

這是我用過的程序。 請務必指定寬字符串的長度 如果不這樣做(並且長度使用-1 ),則WideCharToMultiByte將在其計算中包含終止ASCII-Z。 由於我們使用的是std::string ,因此我們不需要該函數來包含ASCII-Z終止符。

int main(int argc, char* argv[])
{
    wstring m1 = L"Привет"; string m2;

    int req = WideCharToMultiByte(CP_UTF8, 0, m1.c_str(), (int)m1.length(), NULL, 0, NULL, NULL);
    if(req < 0 || req == 0)
        throw runtime_error("Failed to convert string");

    m2.resize((size_t)req);

    int cch = WideCharToMultiByte(CP_UTF8, 0, m1.c_str(), (int)m1.length(), &m2[0], (int)m2.length(), NULL, NULL);
    if(cch < 0 || cch == 0)
        throw runtime_error("Failed to convert string");

    // Should not be required
    m2.resize((size_t)cch);

    string s1, s2, s3, s4, s5;
    SHA1 sha1; SHA224 sha224; SHA256 sha256; SHA384 sha384; SHA512 sha512;

    HashFilter f1(sha1, new HexEncoder(new StringSink(s1)));
    HashFilter f2(sha224, new HexEncoder(new StringSink(s2)));
    HashFilter f3(sha256, new HexEncoder(new StringSink(s3)));
    HashFilter f4(sha384, new HexEncoder(new StringSink(s4)));
    HashFilter f5(sha512, new HexEncoder(new StringSink(s5)));

    ChannelSwitch cs;
    cs.AddDefaultRoute(f1);
    cs.AddDefaultRoute(f2);
    cs.AddDefaultRoute(f3);
    cs.AddDefaultRoute(f4);
    cs.AddDefaultRoute(f5);

    StringSource ss(m2, true /*pumpAll*/, new Redirector(cs));

    cout << "SHA-1:   " << s1 << endl;
    cout << "SHA-224: " << s2 << endl;
    cout << "SHA-256: " << s3 << endl;
    cout << "SHA-384: " << s4 << endl;
    cout << "SHA-512: " << s5 << endl;

    return 0;
}

這對我來說似乎很好。

我只是將寬字符緩沖區強制轉換為const byte* ,並將其(和調整后的大小)傳遞給散列函數,而不是擺弄試圖提取碎片。

int main() {

    std::wstring string(L"Привет");

    CryptoPP::SHA1 sha1;
    std::string hash;

    CryptoPP::StringSource ss(
        reinterpret_cast<const byte*>(string.c_str()), // cast to const byte*
        string.size() * sizeof(std::wstring::value_type), // adjust for size
        true,
        new CryptoPP::HashFilter(sha1,
            new CryptoPP::HexEncoder(
                new CryptoPP::StringSink(hash)
            )
        )
    );

    std::cout << hash << std::endl;

    return 0;
}

輸出:

C6F8291E68E478DD5BD1BC2EC2A7B7FC0CEE1420

編輯:添加。

結果將依賴於encoding 例如,我在Linux上運行它,其中wchar_t是4個字節。 Windows我相信wchar_t可能只有2個字節。

為了保持一致性,最好使用UTF8將文本存儲在普通的std::string 這也使得調用API更簡單:

int main() {

    std::string string("Привет"); // UTF-8 encoded

    CryptoPP::SHA1 sha1;
    std::string hash;

    CryptoPP::StringSource ss(
        string,
        true,
        new CryptoPP::HashFilter(sha1,
            new CryptoPP::HexEncoder(
                new CryptoPP::StringSink(hash)
            )
        )
    );

    std::cout << hash << std::endl;

    return 0;
}

輸出:

2805AE8E7E12F182135F92FB90843BB1080D3BE8

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM