Во встроенном языке текущей версии, на момент публикации нет штатных средств для расчета Хеша по алгоритму SHA-512, кроме того, нет встроенных механизмов для реализации HMAC, в частности HMAC512, приведённый пример кода позволяет получать хеш сообщения с ключом и без ключа.
В примере приведены вспомогательные функции для обеспечения побитовых операций над 64-х разрядными числами. А также функция расчета хеша SHA512 в которую передаются двоичные данные, для которых нужно рассчитать хеш. Хеш возвращается в виде Hex строки.
Функция HMAC512 рассчитывает хеш с ключом, в эту функцию передаются два строковых параметра - ключ и данные. Возвращает hex строку с хешем.
Алгоритмы расчета SHA-512 и HMAC находятся в общедоступных источниках, например есть соответствующие статьи в википедии, но вариантов реализации этих алгоритмов средствами встроенного языка 1С:Предприятие 8, версии 8.3.11 в открытом доступе не обнаружено.
Вы можете просто вставить код примера в модуль и вызывать нужные функции.
В коде используются функции языка, появившиеся в релизе платформы 8.3.11, например ПобитовоеНе, однако эти функции позволяют работать только с 32-х разрядными целыми числами.
В приведённом примере реализованы функции для работы с 64-х разрядными числами.
Текст кода имплементации:
Функция ПовторитьСтроку(Строка, Количество)
Части = Новый Массив();
Для к = 1 По Количество Цикл
Части.Добавить(Строка);
КонецЦикла;
Возврат СтрСоединить(Части, "");
КонецФункции
Функция СклеитьДвоичныеДанные(ДвоичныеДанные1, ДвоичныеДанные2)
МассивДвоичныхДанных = Новый Массив;
МассивДвоичныхДанных.Добавить(ДвоичныеДанные1);
МассивДвоичныхДанных.Добавить(ДвоичныеДанные2);
Возврат СоединитьДвоичныеДанные(МассивДвоичныхДанных);
КонецФункции
// Функция получает массив буферов двоичных данных из HEX строки с разделителями запятыми
// пример: "428A2F98,71374491,B5C0FBCF"
//
Функция ПолучитьМассивБДДИзHexСтроки(Знач пСтрока)
лПоз = Найти(пСтрока,",");
лМассив = Новый Массив();
Пока лПоз>1 Цикл
лПодстрока = Сред(пСтрока,1,лПоз-1);
пСтрока = Сред(пСтрока,лПоз+1);
лПоз = Найти(пСтрока,",");
лМассив.Добавить(ПолучитьБуферДвоичныхДанныхИзHexСтроки(лПодстрока));
КонецЦикла;
лМассив.Добавить(ПолучитьБуферДвоичныхДанныхИзHexСтроки(пСтрока));
Возврат лМассив;
КонецФункции // ПолучитьМассивБДДИзHexСтроки(Знач пСтрока)
// функция осуществляет циклический сдвиг влево
// в данном случае не используется, но пусть будет
Функция ЦиклическийСдвигВлево64(пЧисло, пСмещение)
Возврат(ПобитовыйСдвигВправо64(пЧисло, 64-пСмещение) + ПобитовыйСдвигВлево64(пЧисло, пСмещение));
КонецФункции // ЦиклическийСдвигВлево64(пЧисло, пСмещение)
// функция осуществляет циклический сдвиг вправо
//
Функция ЦиклическийСдвигВправо64(пЧисло, пСмещение)
Возврат(ПобитовыйСдвигВправо64(пЧисло, пСмещение) + ПобитовыйСдвигВлево64(пЧисло,64-пСмещение));
КонецФункции // ЦиклическийСдвигВправо64(пЧисло, пСмещение)
Функция ПобитовыйСдвигВправо64(пЧисло, пСмещение)
лЧисло0 = Цел(пЧисло/4294967296);
лЧисло1 = пЧисло%4294967296;
Если пСмещение<32 Тогда
Возврат (ПобитовыйСдвигВправо(лЧисло0, пСмещение))*4294967296+
ПобитовыйСдвигВправо(лЧисло1, пСмещение)+ПобитовыйСдвигВлево(лЧисло0, 32-пСмещение);
ИНаче
Возврат ПобитовыйСдвигВправо(лЧисло0, пСмещение - 32);
КонецЕсли;
КонецФункции // ПобитовыйСдвигВправо64(пЧисло, пСмещение)
Функция ПобитовыйСдвигВлево64(пЧисло, пСмещение)
лЧисло0 = Цел(пЧисло/4294967296);
лЧисло1 = пЧисло%4294967296;
Если пСмещение<32 Тогда
Возврат (ПобитовыйСдвигВлево(лЧисло0, пСмещение)+ПобитовыйСдвигВправо(лЧисло1, 32-пСмещение))*4294967296+
ПобитовыйСдвигВлево(лЧисло1, пСмещение);
ИНаче
Возврат ПобитовыйСдвигВлево(лЧисло1, пСмещение - 32)*4294967296;
КонецЕсли;
КонецФункции // ПобитовыйСдвигВлево64(пЧисло, пСмещение)
Функция ПобитовоеИсключительноеИли64(пЧисло1, пЧисло2)
лЧисло10 = Цел(пЧисло1/4294967296);
лЧисло11 = пЧисло1%4294967296;
лЧисло20 = Цел(пЧисло2/4294967296);
лЧисло21 = пЧисло2%4294967296;
Возврат (ПобитовоеИсключительноеИли(лЧисло10,лЧисло20)*4294967296+ПобитовоеИсключительноеИли(лЧисло11,лЧисло21));
КонецФункции // ПобитовоеИсключительноеИли64(пЧисло1, пЧисло2)
Функция ПобитовоеИ64(пЧисло1, пЧисло2)
лЧисло10 = Цел(пЧисло1/4294967296);
лЧисло11 = пЧисло1%4294967296;
лЧисло20 = Цел(пЧисло2/4294967296);
лЧисло21 = пЧисло2%4294967296;
Возврат (ПобитовоеИ(лЧисло10,лЧисло20)*4294967296+ПобитовоеИ(лЧисло11,лЧисло21));
КонецФункции // ПобитовоеИ64(пЧисло1, пЧисло2)
Функция ПобитовоеНе64(пЧисло1)
лЧисло10 = Цел(пЧисло1/4294967296);
лЧисло11 = пЧисло1%4294967296;
Возврат (ПобитовоеНе(лЧисло10)*4294967296+ПобитовоеНе(лЧисло11));
КонецФункции // ПобитовоеНе64(пЧисло1)
Функция ПривестиК64Битам(пЧисло)
Возврат пЧисло%18446744073709551616;
КонецФункции // ПривестиК64Битам(пЧисло)
// Функция расчета Хеша по алгоритму SHA-512
// Входной параметр - двоичные данные
// Возвращаяет HEX строку с хешем
//
Функция SHA512(пДвоичныеДанные) Экспорт
//Пояснения:
// Все переменные беззнаковые, имеют размер 64 бита и при вычислениях суммируются по модулю 2^64
// message — исходное двоичное сообщение
// m — преобразованное сообщение
// Инициализация переменных
// (первые 64 бита дробных частей квадратных корней первых восьми простых чисел [от 2 до 19]):
h0 = ПолучитьБуферДвоичныхДанныхИзHexСтроки("6a09e667f3bcc908");
h1 = ПолучитьБуферДвоичныхДанныхИзHexСтроки("bb67ae8584caa73b");
h2 = ПолучитьБуферДвоичныхДанныхИзHexСтроки("3c6ef372fe94f82b");
h3 = ПолучитьБуферДвоичныхДанныхИзHexСтроки("a54ff53a5f1d36f1");
h4 = ПолучитьБуферДвоичныхДанныхИзHexСтроки("510e527fade682d1");
h5 = ПолучитьБуферДвоичныхДанныхИзHexСтроки("9b05688c2b3e6c1f");
h6 = ПолучитьБуферДвоичныхДанныхИзHexСтроки("1f83d9abfb41bd6b");
h7 = ПолучитьБуферДвоичныхДанныхИзHexСтроки("5be0cd19137e2179");
//Таблица констант
//(первые 64 бита дробных частей кубических корней первых 80 простых чисел [от 2 до 409]):
лСтрока = "
|428a2f98d728ae22,7137449123ef65cd,b5c0fbcfec4d3b2f,e9b5dba58189dbbc,
|3956c25bf348b538,59f111f1b605d019,923f82a4af194f9b,ab1c5ed5da6d8118,
|d807aa98a3030242,12835b0145706fbe,243185be4ee4b28c,550c7dc3d5ffb4e2,
|72be5d74f27b896f,80deb1fe3b1696b1,9bdc06a725c71235,c19bf174cf692694,
|e49b69c19ef14ad2,efbe4786384f25e3,0fc19dc68b8cd5b5,240ca1cc77ac9c65,
|2de92c6f592b0275,4a7484aa6ea6e483,5cb0a9dcbd41fbd4,76f988da831153b5,
|983e5152ee66dfab,a831c66d2db43210,b00327c898fb213f,bf597fc7beef0ee4,
|c6e00bf33da88fc2,d5a79147930aa725,06ca6351e003826f,142929670a0e6e70,
|27b70a8546d22ffc,2e1b21385c26c926,4d2c6dfc5ac42aed,53380d139d95b3df,
|650a73548baf63de,766a0abb3c77b2a8,81c2c92e47edaee6,92722c851482353b,
|a2bfe8a14cf10364,a81a664bbc423001,c24b8b70d0f89791,c76c51a30654be30,
|d192e819d6ef5218,d69906245565a910,f40e35855771202a,106aa07032bbd1b8,
|19a4c116b8d2d0c8,1e376c085141ab53,2748774cdf8eeb99,34b0bcb5e19b48a8,
|391c0cb3c5c95a63,4ed8aa4ae3418acb,5b9cca4f7763e373,682e6ff3d6b2b8a3,
|748f82ee5defb2fc,78a5636f43172f60,84c87814a1f0ab72,8cc702081a6439ec,
|90befffa23631e28,a4506cebde82bde9,bef9a3f7b2c67915,c67178f2e372532b,
|ca273eceea26619c,d186b8c721c0c207,eada7dd6cde0eb1e,f57d4f7fee6ed178,
|06f067aa72176fba,0a637dc5a2c898a6,113f9804bef90dae,1b710b35131c471b,
|28db77f523047d84,32caab7b40c72493,3c9ebe0a15c9bebc,431d67c49c100d4c,
|4cc5d4becb3e42b6,597f299cfc657e2a,5fcb6fab3ad6faec,6c44198c4a475817";
Конст = ПолучитьМассивБДДИзHexСтроки(лСтрока);
//Предварительная обработка:
//m := m ǁ [k нулевых бит], где k — наименьшее неотрицательное число, такое что
// (L + 1 + K) mod 1024 = 960, где L — число бит в сообщении (сравнима по модулю 1024 c 960)
//m := m ǁ Длина(message) — длина исходного сообщения в битах в виде 64-битного числа
// с порядком байтов от старшего к младшему
// Исправлено 2022-04-09
//msg = ПолучитьБуферДвоичныхДанныхИзДвоичныхДанных(пДвоичныеДанные);
//L = msg.Размер*8; // получаем размер в битах
//K1 = 1024 - ((L + 64 + 1) % 1024); // сколько бит нужно добавить
//K = K1 - 7; // Потому что засчитали один бит, а добавлять будем целый байт с лидирующим битом
//КолвоHEX = K/8; // считаем количество добавляемых байтов
// /Исправлено 2022-04-09
msg = ПолучитьБуферДвоичныхДанныхИзДвоичныхДанных(пДвоичныеДанные);
L = msg.Размер*8; // получаем размер в битах
лРазмер = msg.Размер%128;
K1 = 112 - лРазмер;
Если лРазмер > 111 Тогда
K1 = K1 + 128;
КонецЕсли;
КолвоHEX = K1 + 7; // Потому что засчитали один бит, а добавлять будем целый байт с лидирующим битом
лБуферДвДанных = Новый БуферДвоичныхДанных(КолвоHEX,ПорядокБайтов.BigEndian);
лМассивБухДвДанных = Новый Массив();
лМассивБухДвДанных.Добавить(msg);
лМассивБухДвДанных.Добавить(ПолучитьБуферДвоичныхДанныхИзHexСтроки("80"));
лМассивБухДвДанных.Добавить(лБуферДвДанных);
лБуферДвДанных2 = Новый БуферДвоичныхДанных(8,ПорядокБайтов.BigEndian);
лБуферДвДанных2.ЗаписатьЦелое64(0,L,ПорядокБайтов.BigEndian);
лМассивБухДвДанных.Добавить(лБуферДвДанных2);
msg = СоединитьБуферыДвоичныхДанных(лМассивБухДвДанных,,ПорядокБайтов.BigEndian);
//разбить сообщение на куски по 512 бит
лМассив = РазделитьДвоичныеДанные(ПолучитьДвоичныеДанныеИзБуфераДвоичныхДанных(msg),128);
h_0 = h0.ПрочитатьЦелое64(0,ПорядокБайтов.BigEndian);
h_1 = h1.ПрочитатьЦелое64(0,ПорядокБайтов.BigEndian);
h_2 = h2.ПрочитатьЦелое64(0,ПорядокБайтов.BigEndian);
h_3 = h3.ПрочитатьЦелое64(0,ПорядокБайтов.BigEndian);
h_4 = h4.ПрочитатьЦелое64(0,ПорядокБайтов.BigEndian);
h_5 = h5.ПрочитатьЦелое64(0,ПорядокБайтов.BigEndian);
h_6 = h6.ПрочитатьЦелое64(0,ПорядокБайтов.BigEndian);
h_7 = h7.ПрочитатьЦелое64(0,ПорядокБайтов.BigEndian);
//Далее сообщение обрабатывается последовательными порциями по 1024 бит:
Для Каждого лКусок1 Из лМассив Цикл
// разбить кусок на 16 слов длиной 64 бита (с порядком байтов от старшего к младшему внутри слова): w[0..15]
лМассив2 = РазделитьДвоичныеДанные(лКусок1,8);
Для i=16 По 79 Цикл
лЦел15 = ПолучитьБуферДвоичныхДанныхИзДвоичныхДанных(лМассив2[i-15]).ПрочитатьЦелое64(0,ПорядокБайтов.BigEndian);
лЦел2 = ПолучитьБуферДвоичныхДанныхИзДвоичныхДанных(лМассив2[i-2]).ПрочитатьЦелое64(0,ПорядокБайтов.BigEndian);
// Сгенерировать дополнительные 48 слов:
// для i от 16 до 63
// s0 := (w[i-15] rotr 7) xor (w[i-15] rotr 18) xor (w[i-15] shr 3)
// s1 := (w[i-2] rotr 17) xor (w[i-2] rotr 19) xor (w[i-2] shr 10)
// w[i] := w[i-16] + s0 + w[i-7] + s1
s0 = ПобитовоеИсключительноеИли64(ПобитовоеИсключительноеИли64(ЦиклическийСдвигВправо64(лЦел15,1),ЦиклическийСдвигВправо64(лЦел15,8)),ПобитовыйСдвигВправо64(лЦел15,7));
s1 = ПобитовоеИсключительноеИли64(ПобитовоеИсключительноеИли64(ЦиклическийСдвигВправо64(лЦел2,19),ЦиклическийСдвигВправо64(лЦел2,61)),ПобитовыйСдвигВправо64(лЦел2,6));
лБуфер64 = Новый БуферДвоичныхДанных(8,ПорядокБайтов.BigEndian);
лЦел64 = ПолучитьБуферДвоичныхДанныхИзДвоичныхДанных(лМассив2[i-16]).ПрочитатьЦелое64(0,ПорядокБайтов.BigEndian) + s0 + ПолучитьБуферДвоичныхДанныхИзДвоичныхДанных(лМассив2[i-7]).ПрочитатьЦелое64(0,ПорядокБайтов.BigEndian) + s1;
лЦел64 = ПривестиК64Битам(лЦел64);
лБуфер64.ЗаписатьЦелое64(0,лЦел64,ПорядокБайтов.BigEndian);
лМассив2.Добавить(ПолучитьДвоичныеДанныеИзБуфераДвоичныхДанных(лБуфер64));
КонецЦикла;
// Инициализация вспомогательных переменных:
a = h_0;
b = h_1;
c = h_2;
d = h_3;
e = h_4;
f = h_5;
g = h_6;
h = h_7;
// Основной цикл:
Для i = 0 По 79 Цикл
S0 = ПривестиК64Битам(ПобитовоеИсключительноеИли64(ПобитовоеИсключительноеИли64(ЦиклическийСдвигВправо64(a, 28), ЦиклическийСдвигВправо64(a, 34)), ЦиклическийСдвигВправо64(a, 39)));
Ma = ПривестиК64Битам(ПобитовоеИсключительноеИли64(ПобитовоеИсключительноеИли64(ПобитовоеИ64(a, b), ПобитовоеИ64(a, c)), ПобитовоеИ64(b, c)));
t2 = ПривестиК64Битам(S0 + Ma);
S1 = ПривестиК64Битам(ПобитовоеИсключительноеИли64(ПобитовоеИсключительноеИли64(ЦиклическийСдвигВправо64(e, 14), ЦиклическийСдвигВправо64(e, 18)), ЦиклическийСдвигВправо64(e, 41)));
Ch = ПривестиК64Битам(ПобитовоеИсключительноеИли64(ПобитовоеИ64(e, f), ПобитовоеИ64(ПобитовоеНе64(e), g)));
t1 = ПривестиК64Битам(h + S1 + Ch + Конст[i].ПрочитатьЦелое64(0,ПорядокБайтов.BigEndian) + ПолучитьБуферДвоичныхДанныхИзДвоичныхДанных(лМассив2[i]).ПрочитатьЦелое64(0,ПорядокБайтов.BigEndian));
h = g;
g = f;
f = e;
e = ПривестиК64Битам(d + t1);
d = c;
c = b;
b = a;
a = ПривестиК64Битам(t1 + t2);
КонецЦикла;
// Добавить полученные значения к ранее вычисленному результату:
h_0 = ПривестиК64Битам(h_0 + a);
h_1 = ПривестиК64Битам(h_1 + b);
h_2 = ПривестиК64Битам(h_2 + c);
h_3 = ПривестиК64Битам(h_3 + d);
h_4 = ПривестиК64Битам(h_4 + e);
h_5 = ПривестиК64Битам(h_5 + f);
h_6 = ПривестиК64Битам(h_6 + g);
h_7 = ПривестиК64Битам(h_7 + h);
КонецЦикла;
h0.ЗаписатьЦелое64(0,h_0,ПорядокБайтов.BigEndian);
h1.ЗаписатьЦелое64(0,h_1,ПорядокБайтов.BigEndian);
h2.ЗаписатьЦелое64(0,h_2,ПорядокБайтов.BigEndian);
h3.ЗаписатьЦелое64(0,h_3,ПорядокБайтов.BigEndian);
h4.ЗаписатьЦелое64(0,h_4,ПорядокБайтов.BigEndian);
h5.ЗаписатьЦелое64(0,h_5,ПорядокБайтов.BigEndian);
h6.ЗаписатьЦелое64(0,h_6,ПорядокБайтов.BigEndian);
h7.ЗаписатьЦелое64(0,h_7,ПорядокБайтов.BigEndian);
//Получить итоговое значение хеша:
//digest = hash = h0 ǁ h1 ǁ h2 ǁ h3 ǁ h4 ǁ h5 ǁ h6 ǁ h7
лМассив3 = Новый Массив();
лМассив3.Добавить(h0);
лМассив3.Добавить(h1);
лМассив3.Добавить(h2);
лМассив3.Добавить(h3);
лМассив3.Добавить(h4);
лМассив3.Добавить(h5);
лМассив3.Добавить(h6);
лМассив3.Добавить(h7);
Буфер3 = СоединитьБуферыДвоичныхДанных(лМассив3);
digest = Нрег(ПолучитьHexСтрокуИзБуфераДвоичныхДанных(Буфер3));
Возврат digest;
КонецФункции // SHA512(пДвоичныеДанные)
// Функция расчета Хеша по алгоритму SHA-512 с ключем
// Входные параметры:
// пКлюч - строка с ключем, неограниченная, приводится к длине блока - 128
// пДанные - строка с данными, неограниченная
// Возвращаяет Hex строку с хешем
//
Функция HMAC512(пКлюч, пДанные) Экспорт
ДанныеДв = ПолучитьДвоичныеДанныеИзСтроки(пДанные);
КлючДв = ПолучитьДвоичныеДанныеИзСтроки(пКлюч);
РазмерБлока = 128; // Размер блока для HMAC512 - 128
Если КлючДв.Размер() > РазмерБлока Тогда
КлючHex = SHA512(КлючДв);
Иначе
КлючHex = ПолучитьHexСтрокуИзДвоичныхДанных(КлючДв);
Если КлючДв.Размер() < РазмерБлока Тогда
КлючHex = Лев(КлючHex + ПовторитьСтроку("00", РазмерБлока), РазмерБлока);
КонецЕсли;
КонецЕсли;
КлючБуфер = ПолучитьБуферДвоичныхДанныхИзHexСтроки(КлючHex);
opad = ПолучитьБуферДвоичныхДанныхИзHexСтроки(ПовторитьСтроку("5c", РазмерБлока));
ipad = ПолучитьБуферДвоичныхДанныхИзHexСтроки(ПовторитьСтроку("36", РазмерБлока));
ipad.ЗаписатьПобитовоеИсключительноеИли(0, КлючБуфер);
ikeypad = ПолучитьДвоичныеДанныеИзБуфераДвоичныхДанных(ipad);
opad.ЗаписатьПобитовоеИсключительноеИли(0, КлючБуфер);
okeypad = ПолучитьДвоичныеДанныеИзБуфераДвоичныхДанных(opad);
Возврат SHA512(СклеитьДвоичныеДанные(okeypad, ПолучитьДвоичныеДанныеИзHexСтроки(SHA512(СклеитьДвоичныеДанные(ikeypad, ДанныеДв)))));
КонецФункции // HMAC512(пКлюч, пДанные)
В прилагаемой обработке реализованы механизмы расчёта SHA-224, SHA-256, SHA-384, SHA-512, SHA-512/224, SHA-512/256, а также HMAC на базе этих Hash функций.