Каталог решений - SHA512 и HMAC512 на 1С 8 без использования внешних компонент

SHA512 и HMAC512 на 1С 8 без использования внешних компонент

SHA512 и HMAC512 на 1С 8 без использования внешних компонент

В наличии

Имплементация алгоритма расчета Hash 512 с ключом и без. С ключом по алгоритму HMAC. Используются механизмы платформы начиная с версии 8.3.11.

Категория:

Описание

Во встроенном языке текущей версии, на момент публикации нет штатных средств для расчета Хеша по алгоритму 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-битного числа
	//            с порядком байтов от старшего к младшему
	
	msg = ПолучитьБуферДвоичныхДанныхИзДвоичныхДанных(пДвоичныеДанные);
	L = msg.Размер*8; // получаем размер в битах
	K1 = 1024 - ((L + 64 + 1) % 1024); // сколько бит нужно добавить
	K = K1 - 7; // Потому что засчитали один бит, а добавлять будем целый байт с лидирующим битом
	КолвоHEX = K/8; // считаем количество добавляемых байтов
	лБуферДвДанных = Новый БуферДвоичныхДанных(Колво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 функций.

has been added to your cart:
Оформление заказа