Получить SHA1 из строки Unicode в Crypto ++

Я изучаю C ++ самостоятельно, и у меня есть одна проблема, которую я не могу решить больше недели. Я надеюсь, что вы можете мне помочь.

Мне нужно получить SHA1-дайджест строки Unicode (например, Привет), но я не знаю, как это сделать.

Я пытался сделать это так, но он возвращает неправильный дайджест!

За 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;
}

5

Решение

Мне нужно получить SHA1-дайджест строки Unicode (например, Привет), но я не знаю, как это сделать.

Хитрость здесь в том, что вам нужно знать, как кодировать строку Unicode. В Windows wchar_t 2 октета; в то время как в Linux wchar_t это 4 октета. На ней есть вики-страница Crypto ++ по адресу Особенности набора символов, но это не так хорошо.

Для наиболее эффективного взаимодействия всегда используйте UTF-8. Это означает, что вы конвертируете UTF-16 или UTF-32 в UTF-8. Поскольку вы находитесь на Windows, вы хотите позвонить Функция WideCharToMultiByte конвертировать его с помощью CP_UTF8, Если бы вы были на Linux, то вы бы использовали libiconv.

Crypto ++ имеет встроенную функцию под названием StringNarrow который использует C ++. Это в файле misc.h. Обязательно позвони setlocale перед его использованием.

У переполнения стека есть несколько вопросов по использованию функции 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;
}
3

Другие решения

Вы говорите «но он возвращает неправильный дайджест» — с чем вы его сравниваете?

Ключевой момент: дайджесты, такие как SHA-1, работают не с последовательностями символов, а с последовательностями байтов.

То, что вы делаете в этом фрагменте кода, генерирует ad-hoc кодирование символов Юникода в строке "Ы", Эта кодировка будет (как оказывается) соответствовать кодировке UTF-16 если все символы в строке находятся в BMP («базовая многоязычная плоскость», что в данном случае верно) и если числа, которые заканчиваются в wcharacter являются целыми числами, представляющими кодовые точки Юникода (что-то вроде правильно, но, я думаю, не гарантировано).

Если дайджест, с которым вы сравниваете его, превращает входную строку в последовательность байтов, используя кодировку UTF-8 (что вполне вероятно), то это приведет к тому, что последовательность байтов будет отличаться от вашей, так что дайджест SHA-1 эта последовательность будет отличаться от дайджеста, который вы рассчитываете здесь.

Так:

  • Проверьте, какую кодировку использует ваша тестовая строка.

  • Лучше всего использовать некоторые библиотечные функции для конкретной генерации кодировки UTF-16 или UTF-8 (в зависимости от случая) строки, которую вы хотите обработать, чтобы убедиться, что последовательность байтов, с которой вы работаете, соответствует вашим ожиданиям. является.

Там есть отличное введение в Unicode и кодировки в метко названном документе Абсолютный минимум Каждый разработчик программного обеспечения должен абсолютно точно знать о юникоде и наборах символов (никаких оправданий!)

3

Это, кажется, работает хорошо для меня.

Вместо того, чтобы возиться с попытками извлечь фрагменты, я просто приводил буфер широких символов к 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
2
По вопросам рекламы ammmcru@yandex.ru
Adblock
detector