【问题标题】:Get SHA1 of Unicode string in Crypto++在 Crypto++ 中获取 Unicode 字符串的 SHA1
【发布时间】:2015-06-27 14:43:59
【问题描述】:

我独立学习 C++,但有一个问题,我无法解决超过一周的问题。我希望你能帮助我。

我需要获取 Unicode 字符串的 SHA1 摘要(如 Привет),但我不知道该怎么做。

我尝试这样做,但它返回错误的摘要!

对于wstring('Ы') 它返回 - A469A61DF29A7568A6CC63318EA8741FA1CF2A7
我需要-8dbe718ab1e0c4d75f7ab50fc9a53ec4f0528373

对于我的英语的问候和抱歉:)。

加密PP 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;
}

【问题讨论】:

  • “我试着这样做,但它返回错误的摘要!” - Crypto++ 代码看起来不错,所以问题可能出在其他地方。它产生了什么摘要,您期望什么摘要?我怀疑您需要转换为 UTF-8 的宽字符串的摘要。 UTF-8 是最具互操作性的。通过单击编辑将预期的和实际的摘要添加到您的问题中(不要将其作为评论发布)。

标签: c++ unicode sha1 crypto++ digest


【解决方案1】:

这对我来说似乎很好。

我没有尝试提取片段,而是简单地将宽字符缓冲区转换为 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

【讨论】:

  • 对我来说,它会生成摘要 AD5EF6AFD4BADE078F3E19FAE5E45A43635A18CB。我的 IDE 或项目设置可能有问题吗?请告诉我,你有什么 ide 和项目设置?
  • @DmitryAurokk 我在Linux 上使用eclipse.com IDE。我认为问题很可能是编码。在 Linux wchar_t 上是 4 个字节(UTF32 兼容)。我认为Windows wchar_t 是 2 个字节? (UTF16 ish)。我所做的是针对命令行程序sha1sum 测试程序输出,它给出了相同的结果。如果您希望跨平台获得一致的结果,那么最好使用UTF8 并忘记宽字符串?
  • @DmitryAurokk 我添加了一个使用UTF-8 编码的新示例(需要兼容 UTF-8 的文本编辑器)
  • @DmitryAurokk 这很奇怪。只是为了确保您可以将 UTF-8 字符代码显式分配给变量以排除文本文件编码吗?将创建字符串的行替换为:std::string string("\u041F\u0440\u0438\u0432\u0435\u0442"); Values take from this table here.
【解决方案2】:

您说“但它返回错误的摘要”——您将其与什么进行比较?

关键点:像 SHA-1 这样的摘要不适用于字符序列,而是字节序列。

您在这段代码的 sn-p 中所做的是生成字符串 "Ы" 中 unicode 字符的临时编码。这种编码将(事实证明)匹配 UTF-16 编码如果字符串中的字符都在 BMP 中('基本多语言平面',在这种情况下是真的)如果wcharacter 结尾的数字是代表Unicode 代码点的整数(这可能是正确的,但我认为不能保证)。

如果您要与之比较的摘要将输入字符串转换为使用 UTF-8 编码的字节序列(这很可能),那么这将产生与您的不同的字节序列,因此 SHA-该序列的 1 个摘要与您在此处计算的摘要不同。

所以:

  • 检查您的测试字符串使用什么编码。

  • 您最好使用一些库函数专门为要处理的字符串生成 UTF-16 或 UTF-8(视情况而定)编码,以确保您正在处理的字节序列with 就是你想的那样。

在恰当命名的文档The Absolute Minimum Every Software Developer Absolutely, Positively Must Know About Unicode and Character Sets (No Excuses!)中有对 unicode 和编码的精彩介绍

【讨论】:

    【解决方案3】:

    我需要获取 Unicode 字符串的 SHA1 摘要(如Привет),但我不知道该怎么做。

    这里的诀窍是您需要知道如何对 Unicode 字符串进行编码。在 Windows 上,wchar_t 是 2 个八位字节;而在 Linux 上,wchar_t 是 4 个字节。 Character Set Considerations 上有一个 Crypto++ wiki 页面,但它不是那么好。

    要最有效地进行互操作,请始终使用 UTF-8。这意味着您将 UTF-16 或 UTF-32 转换为 UTF-8。因为您使用的是 Windows,所以您需要调用 WideCharToMultiByte function 以使用 CP_UTF8 进行转换。如果你在 Linux 上,那么你会使用libiconv

    Crypto++ 有一个名为 StringNarrow 的内置函数,它使用 C++。它在文件misc.h 中。使用前请务必致电setlocale

    Stack Overflow 有几个关于使用 Windows 功能的问题。例如,请参阅How do you properly use 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;
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-10-22
      • 2021-07-31
      • 1970-01-01
      • 2014-10-21
      • 2011-05-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多