HMAC на встроенном языке 1С

Программирование - Защита и шифрование

Внешняя обработка хеширования данных HMAC (SHA-1, MD5, SHA-256, CRC-32) без использования внешних компонент.

Для получения HMAC в функцию передается первым параметром — ключ K. Второй параметр — сообщение text, которое будет передаваться отправителем и подлинность которого будет проверяться получателем. Третим параметром тип хеш-функции Hash (CRC32, MD5, SHA1, SHA256) . После чего на выходе мы получаем код аутентификации.

// Функция - HMAC
//
// Параметры:                            1
//  K     - ключ    в шестнадцатеричном виде - Строка
//  text - текстовое сообщение - Строка
//  Hash - Hash function (CRC32, MD5, SHA1, SHA256) - Строка
// Возвращаемое значение:
//     строка HMAC - Строка
Функция HMAC(Знач K, Знач text, Знач Hash)
	
	Перем kResult;
	Перем К0;
	
	//Если длина ключа K больше размера блока, то к ключу K применяем хэш-функцию
	Если СтрДлина(K)>128 Тогда 
		K = SHA1(K,Hash);
	КонецЕсли;	
	
	//1 Дополняем ключ K нулевыми байтами до размера блока. Размер блока хэш-функции SHA-1 равен 64 байтам.
	StringSHA1 = Лев(K,128);
	Для к = СтрДлина(K) По 128 Цикл
		StringSHA1 = StringSHA1 + "0";
	КонецЦикла;
	К0 = StringSHA1;
	
	//2 Выполняем операцию «побитовое исключающее ИЛИ» c константой 0x36.
	b = ПреобразоватьЧислоВДвоичнуюСИ(ПреобразоватьHexВДесятичнуюСИ("36"));
	
	к = 1;
	Пока к < 128 Цикл
		a             = ПреобразоватьЧислоВДвоичнуюСИ(ПреобразоватьHexВДесятичнуюСИ(Сред(StringSHA1,к,2)));
		с             = XOR(a,b);        
		StringSHA1     = Лев(StringSHA1,к-1)+с+Прав(StringSHA1, 128-к);
		к             = к + 2;
	КонецЦикла;
	
	StringSHA1 = Лев(StringSHA1,128);
	
	//3 Выполняем склейку исходного сообщения со строкой, полученной на шаге 2.
	Для к = 1 По СтрДлина(text) Цикл
		
		StringSHA1 = StringSHA1 + ПреобразоватьДесятичнуюСИВHex(КодСимвола(Сред(text,к,1)));
		
	КонецЦикла;
	
	//4 Применим хэш-функцию SHA-1 к строке, полученной на прошлом шаге.
	StringSHA1     = SHA1(StringSHA1,Hash);
	kResult     = StringSHA1;
	
	//5 Выполним операцию «побитовое исключающее ИЛИ» c константой 0x5c.
	StringSHA1 = К0;
	
	b = ПреобразоватьЧислоВДвоичнуюСИ(ПреобразоватьHexВДесятичнуюСИ("5c"));
	
	к = 1;
	Пока к < 128 Цикл
		a             = ПреобразоватьЧислоВДвоичнуюСИ(ПреобразоватьHexВДесятичнуюСИ(Сред(StringSHA1,к,2)));
		с             = XOR(a,b);        
		StringSHA1     = Лев(StringSHA1,к-1)+с+Прав(StringSHA1, 128-к);
		к             = к + 2;
	КонецЦикла;
	
	StringSHA1 = Лев(StringSHA1,128);
	
	//6 Склейка строки, полученной на шаге 4, со строкой, полученной на шаге 5.
	StringSHA1 = StringSHA1 + kResult;
	
	//7 Применим хэш-функцию SHA-1 к строке, полученной на прошлом шаге.
	StringSHA1 = SHA1(StringSHA1,Hash);    
	
	Возврат StringSHA1;
	
КонецФункции

Функция SHA1(Знач nString, Hash)    
	Хеширование        = Новый ХешированиеДанных(ХешФункция[Hash]);
	ТипhexBinary    = ФабрикаXDTO.Тип("http://www.w3.org/2001/XMLSchema", "hexBinary");
	ДвоичныеДанные    = ФабрикаXDTO.Создать(ТипhexBinary,nString);
	Хеширование.Добавить(ДвоичныеДанные.Значение);    
	sign             = Хеширование.ХешСумма;
	sign 			 = СтрЗаменить(НРЕГ(sign), " ", "");
	Возврат СтрЗаменить(НРЕГ(sign), " ", "");
КонецФункции

Функция ПреобразоватьДесятичнуюСИВHex(Знач int)	
	Если int < 256 Тогда 
		Возврат Прав("00" + ПреобразоватьДесятичнуюСИВОднобайтовыйHex(int),2);
	Иначе
		Возврат Прав("0000" + ПреобразоватьДесятичнуюСИВДвухбайтовыйHex(int),4);
	КонецЕсли;                                 	
КонецФункции

Функция ПреобразоватьHexВДесятичнуюСИ(Знач hex)
	simbol     = СтрДлина(hex) - 1;
	dec     = 0;
	i         = 1;
	Пока simbol >= 0 Цикл
		simbolHex     = Сред(hex, i, 1);
		Res         = Найти("0123456789abcdef", simbolHex) - 1;
		dec         = dec + Res * Pow(16, simbol);
		simbol         = simbol - 1;
		i             = i + 1;
	КонецЦикла;   
	Возврат dec;
КонецФункции

Функция ПреобразоватьЧислоВДвоичнуюСИ(Знач int, rBit = 8)
	b = "";
	Для k = 1 По rBit Цикл
		m     = pow(2, rBit - k);
		bit = Цел(int / m);
		int = int - m * bit;
		b     = b + bit;
	КонецЦикла;                                
	Возврат b;                                     
КонецФункции

Функция XOR(a, b)    
	res = 0;
	s     = 1;
	к     = Мин(СтрДлина(a), СтрДлина(b));    
	Пока к > 0 Цикл        
		a1     = Сред(a,к,1);
		b1     = Сред(b,к,1);                     
		res = res + s * ?(a1=b1,0,Макс(a1,b1));
		s     = s*2;        
		к     = к-1;        
	КонецЦикла;     
	Возврат ПреобразоватьДесятичнуюСИВHex(res);
КонецФункции

Функция ПреобразоватьДесятичнуюСИВДвухбайтовыйHex(Знач int)	
	BinaryData = ПреобразоватьЧислоВДвоичнуюСИ(int, 11);	
	BinaryData = "110" + Лев(BinaryData,5) + "10" + Прав(BinaryData, 6);	
	DecimalData = ПолучитьДесятичноеЧислоИзДвоичного(BinaryData);	
	HexData = ПреобразоватьДесятичнуюСИВОднобайтовыйHex(DecimalData);	
	Возврат HexData;                                        	
КонецФункции

Функция ПолучитьДесятичноеЧислоИзДвоичного(b)	
	res 	= 0;
	s     	= 1;
	к     	= СтрДлина(b);
	Пока к > 0 Цикл        
		bit   = Сред(b,к,1);
		res = res + s * bit;
		s   = s*2;        
		к   = к-1;        
	КонецЦикла;                              	
	Возврат res;                             	
КонецФункции

Функция ПреобразоватьДесятичнуюСИВОднобайтовыйHex(Знач int)	
	hex = "";
	Пока int <> 0 Цикл
		p   = int % 16;
		hex = Сред("0123456789abcdef", p + 1, 1) + hex;
		int = Цел(int / 16);
	КонецЦикла;
	Возврат hex;                                           	
КонецФункции

Описпние алгоритма можно посмотреть в Википедии (https://ru.wikipedia.org/wiki/HMAC)

Битовые операции (//1c.ppt.ru/public/99739/)

Преобразование dec to hex и hex to dec https://ru.wikipedia.org/wiki/Позиционная_система_счисления

Представление кириллицы в UTF-8 http://i.voenmeh.ru/kafi5/Kam.loc/inform/UTF-8.htm

 

Скачать файлы

Наименование Файл Версия Размер
HMAC
.epf 8,43Kb
08.12.17
31
.epf 1.0 8,43Kb 31 Скачать

См. также

Комментарии
1. Александр Лукин (i_lo) 182 02.03.17 03:09 Сейчас в теме
Скопировал код из статьи в обработку. Ввожу ключ - 123, текст - sss, функцию - sha-1 и получаю hmac - da2a7fa2ac3e18892b6d0d24221285fa1030b03d. Разве он не должен быть, как на скриншоте 83е...?
Ввожу ключ - 123456, тест - Вышел зайчик погулять, функцию - sha-1 и получаю:

{ВнешняяОбработка.HMAC.МодульОбъекта(74)}: Ошибка при вызове метода контекста (Создать)
ДвоичныеДанные = ФабрикаXDTO.Создать(ТипhexBinary,nString);
по причине:
Несоответствие типов XDTO
по причине:
Ошибка проверки данных XDTO:
Значение: '24026036363636363636363636363636363636363636363636363636363­636363636363636363636363636363636363636363636363636363636363­63636363641244b44843543b2043743043944743843a2043f43e43b44343­b44f44244c' не соответствует простому типу: {http://www.w3.org/2001/XMLSchema}hexBinary
2. Владимир Куприненко (Knup) 16 06.03.17 22:48 Сейчас в теме
(1)
Разве он не должен быть, как на скриншоте 83е...?


нет, потому что ключ нужно передавать в шестнадцатеричном виде.

(1)
Ввожу ключ - 123456, тест - Вышел зайчик погулять, функцию - sha-1 и получаю:


Добавил поддержку кирилицы
3. Владимир Куприненко (Knup) 16 06.03.17 22:48 Сейчас в теме
(1)
Разве он не должен быть, как на скриншоте 83е...?


в файле обработки есть пример
4. Алексей (Операция1Ы) 53 20.03.17 16:09 Сейчас в теме
на 8.2 работать не будет, там нет ХешированиеДанных.
5. Владимир Куприненко (Knup) 16 22.03.17 01:12 Сейчас в теме
(4) к сожелению, да. Если хеширование sha1, тогда посмотрите http://infostart.ru/public/99739/ готовое решение.
6. Александр Матвиенко (user839842) 07.10.17 13:58 Сейчас в теме
пробовал реализовать sha256 на приведенном примере. Но хеш получается другой. Нежели генерировать его в online конвертерах. В коде какая-то ошибка ?
7. Владимир Куприненко (Knup) 16 23.10.17 20:08 Сейчас в теме
(6) Какой текст и ключ? У меня все работает как часы
8. Victor (V_K) 03.11.17 13:21 Сейчас в теме
Функция HMAC(Знач K, Знач text, Знач Hash)

А если ключ "K" это строка,а не число. Как его зашифровать в этот параметр?
9. Владимир Куприненко (Knup) 16 07.11.17 09:53 Сейчас в теме
(8) Вводите строку, все должно работать (:
10. Пафнутий Чебышев (primat) 1254 06.12.17 15:17 Сейчас в теме
Владимир, подскажите, пожалуйста, если мне нужно хеширование SHA-384, то получится ли использовать вместо функции SHA1(Знач nString, Hash) из Вашего алгоритма функцию из этого примера? https://1c-programmer-blog.ru/programmirovanie/md5-v-1s.html
Например, ту, что использует библиотеку CAPICOM. Ведь хеширования SHA-384 у 1С нет в платформе, верно?
11. Владимир Куприненко (Knup) 16 06.12.17 17:52 Сейчас в теме
(10) Да, все будет работать.
И да, хеширования SHA-384 в 1С нет (:
12. Евгений Ф. (sudden) 17.12.17 09:46 Сейчас в теме
Помогите пожалуйста.

АПИСекрет = "cEMBFULinI6rzG3mYKL45czc81pjU7BXbwImC1CFNXk";
Сигнатура = "8692c7a03a684765ac5fe04f04dd72c8POSThttps://localhost/api/getopenorders11223311FxOYiYfpMxmANj4kGJzg=="­
хмак = HMAC(АПИСекрет, Сигнатура, "SHA256");

На выходе 224c54567d42c5c3537bc305329694347fba095592094d6e88a2f342e788­ec1e

PHP:
$hmacsignature = base64_encode( hash_hmac("sha256", $signature, base64_decode( $API_SECRET ), true ) );
На выходе: v+YF+qxhVqB95bjKZB7bN7ywpTgoPUleObTmXNoEE2E=

Расскажите что не так?
13. Владимир Куприненко (Knup) 16 19.12.17 11:01 Сейчас в теме
(12) Судя по всему в коде php у вас не просто получается hmac, а еще использует кодирование информации в 64-разрядный код. В публикации функция просто получает HMAC. Попробуйте получить по этим данным HMAC в онлайн генераторе и 1С. Результат будет одинаковый.
16. Евгений Ф. (sudden) 23.12.17 08:50 Сейчас в теме
(13) Все равно не то что-то. Секрет передать в каком виде надо?
АПИСекрет = "cEMBFULinI6rzG3mYKL45czc81pjU7BXbwImC1CFNXk";
Потому что даже убрав base64_encode из ПХП получая по
$hmacsignature = hash_hmac("sha256", $signature, base64_decode( $API_SECRET ), true ) ;
Ответы разные.
Остается только в обработке Первое: base64_decode( $API_SECRET ) и Второе: hash_hmac("sha256",$signature, Результат_Первого), В_Двоичных)
Но даже получив ответ от ПХП не двоичные данные, а в шестнадцатеричной кодировке = Разные.
18. Владимир Куприненко (Knup) 16 25.12.17 09:37 Сейчас в теме
(16) Сложно сказать основываясь на вашем коде, что именно происходит. Предлагаю вам воспользоватся онлайн генератором https://www.freeformatter.com/hmac-generator.html. Введите туда
АПИСекрет = "cEMBFULinI6rzG3mYKL45czc81pjU7BXbwImC1CFNXk";
Сигнатура = "8692c7a03a684765ac5fe04f04dd72c8POSThttps://localhost/api/getopenorders11223311FxOYiYfpMxmANj4kGJzg=="­­;
и результат у вас получится аналогичный с результатом в 1С,

Повторюсь ищите причину в php, судя по всему там не просто хеширование в hmac
14. Родион (lamdth) 11 20.12.17 11:30 Сейчас в теме
тоже интересно, можно ли как-то доработать обработку чтобы получить HMAC в Base 64
15. Владимир Куприненко (Knup) 16 20.12.17 17:17 Сейчас в теме
(14) Конечно можно. Посмотрите на сайте https://infostart.ru/public/all/?st=t&public-filter%5Bsearch%5D=base64, или поищите в интернете. Скорее всего Вашу проблему уже кто-то решал.
17. Евгений Ф. (sudden) 23.12.17 08:54 Сейчас в теме
hash_hmac(""sha256"", $signature, $API_SECRET, false )
Даже так разное
19. Владимир Шер (vl-sher1) 23.01.18 14:55 Сейчас в теме
Владимир, спасибо за процедуру. Она некорректно работала с кодировкой UTF8, я бы предложил сделать так (8.3):
Функция HMAC(Знач K, Знач text, Знач Hash, Кодировка = null ) Экспорт
	
	Перем kResult;
	Перем К0;
	
	Если Кодировка = null Тогда Кодировка = КодировкаТекста.UTF8; КонецЕсли;
// ...
//3 Выполняем склейку исходного сообщения со строкой, полученной на шаге 2.    БуферДвоичныхДанных
	ДвоичныйТекст = ПолучитьБуферДвоичныхДанныхИзСтроки(text, Кодировка, ложь);
	Для к = 0 По ДвоичныйТекст.Размер - 1 Цикл
		StringSHA1 = StringSHA1 + ПреобразоватьДесятичнуюСИВHex(ДвоичныйТекст[к]);
	КонецЦикла;
Показать


Для проверки удобно использовать https://www.freeformatter.com/hmac-generator.html
20. uno dos (unoDosTres) 05.02.18 10:12 Сейчас в теме
Доброго дня! Подскажите ,выложенный в публикации код такой же как и в обработке?
потому что воспользовавшись, кодом из публикации и попытавшись сравнить результат с онлайн генератором предложенным автором, ни разу не получил сходство ни по одному из алгоритмов шифрования. прикрепил файл
Прикрепленные файлы:
21. Владимир Куприненко (Knup) 16 05.02.18 15:33 Сейчас в теме
(20) День добрый! Код абсолютно идентичный. Проверил, у меня все работает на вашем примере. Посмотрите на описание передаваемых параметров и их формат, Вы точно правильно передаете их? Хочу обратить Ваше внимание на параметр K - ключ, он передается в шестнадцатеричном виде.
unoDosTres; +1 Ответить
22. uno dos (unoDosTres) 05.02.18 15:47 Сейчас в теме
(21),
Т.е. в моем примере на скрине у меня ключ имеет строковое значение "key" я его должен перевести в hex и только после этого запускать функцию hmac?
23. Владимир Куприненко (Knup) 16 05.02.18 16:09 Сейчас в теме
24. af af (afafaf) 26.03.18 16:07 Сейчас в теме
(23) Подскажите, пожалуйста, как именно это сделать (привести строку к hex виду)?
25. Владимир Куприненко (Knup) 16 27.03.18 09:23 Сейчас в теме
(24)
kKey = "";
	Для к = 1 ПО СтрДлина(Key) Цикл
		
		kKey = kKey + ПреобразоватьДесятичнуюСИВHex(КодСимвола(Сред(Key,к,1)));
		
	КонецЦикла; 
26. af af (afafaf) 03.04.18 13:05 Сейчас в теме
А вот мой вариант. Строго в соответствии с алгоритмом из Вики (единственное отличие - предварительно от ключа рассчитывается MD5, это требуется в конкретно моей реализации, можно убрать)
Функция Шмяк(СтрокаТекст,СтрокаКлюч) Экспорт
	
	СтрокаНули = "00000000000000000000000000000000000000000000000000000000000­000000000000000000000000000000000000000000000000000000000000­000000000";
	СтрокаОПАД = "5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5­c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5­c5c5c5c5c";
	СтрокаИПАД = "36363636363636363636363636363636363636363636363636363636363­636363636363636363636363636363636363636363636363636363636363­636363636";
	
	К = ПолучитьДвоичныеДанныеИзСтроки(СтрокаКлюч);
	ХД = Новый ХешированиеДанных(ХешФункция.MD5);
	ХД.Добавить(К);
	Шаг0 = ХД.ХешСумма;
	
	К = ПолучитьБуферДвоичныхДанныхИзДвоичныхДанных(Шаг0);
	Нули = ПолучитьБуферДвоичныхДанныхИзHexСтроки(СтрокаНули);  	
	Нули.ЗаписатьПобитовоеИли(0,К);         	
	Шаг1 = ПолучитьДвоичныеДанныеИзБуфераДвоичныхДанных(Нули);
	
	ИПАД = ПолучитьБуферДвоичныхДанныхИзHexСтроки(СтрокаИПАД);
	БуферШаг1 = ПолучитьБуферДвоичныхДанныхИзДвоичныхДанных(Шаг1);
	ИПАД.ЗаписатьПобитовоеИсключительноеИли(0,БуферШаг1);
	Шаг2 = ПолучитьДвоичныеДанныеИзБуфераДвоичныхДанных(ипад);
	
	БуферШаг2 = ПолучитьБуферДвоичныхДанныхИзДвоичныхДанных(Шаг2);
	БуферТекст = ПолучитьБуферДвоичныхДанныхИзСтроки(СтрокаТекст);
	БуферШаг3 = БуферШаг2.Соединить(БуферТекст);
	Шаг3 = ПолучитьДвоичныеДанныеИзБуфераДвоичныхДанных(БуферШаг3);
	
	ХД = Новый ХешированиеДанных(ХешФункция.SHA1);
	ХД.Добавить(Шаг3);
	Шаг4 = ХД.ХешСумма;
	
	ОПАД = ПолучитьБуферДвоичныхДанныхИзHexСтроки(СтрокаОПАД);
	БуферШаг1 = ПолучитьБуферДвоичныхДанныхИзДвоичныхДанных(Шаг1);
	ОПАД.ЗаписатьПобитовоеИсключительноеИли(0,БуферШаг1);
	Шаг5 = ПолучитьДвоичныеДанныеИзБуфераДвоичныхДанных(ОПАД);
	
	БуферШаг4 = ПолучитьБуферДвоичныхДанныхИзДвоичныхДанных(Шаг4);
	БуферШаг5 = ПолучитьБуферДвоичныхДанныхИзДвоичныхДанных(Шаг5);
	БуферШаг6 = БуферШаг5.Соединить(БуферШаг4);
	Шаг6 = ПолучитьДвоичныеДанныеИзБуфераДвоичныхДанных(БуферШаг6);
	
	ХД = Новый ХешированиеДанных(ХешФункция.SHA1);
	ХД.Добавить(Шаг6);
	Шаг7 = ХД.ХешСумма;
	
	Возврат ПолучитьHexСтрокуИзДвоичныхДанных(Шаг7);  	
	
КонецФункции

Показать
27. user user (inf012) 09.06.18 21:08 Сейчас в теме
Здравствуйте.
Я не понял, SHA-256 работает в 8.3 без использования внешних компонент?
Нужно в мобильное приложение вставить шифрование SHA-256?

Есть примеры кода?
28. Владимир Куприненко (Knup) 16 13.06.18 09:42 Сейчас в теме
(27)
Здравствуйте!
Есть, есть и даже поддерживает мобильное приложение

Хеширование        = Новый ХешированиеДанных(ХешФункция.SHA256);
Хеширование.Добавить("<Строка для хеш функции>");
SHA256             = Хеширование.ХешСумма;
Оставьте свое сообщение