Использование средств шифрования .Net из 1С

Материал из КинтВики
Перейти к: навигация, поиск


Шифрование в .Net

Среда .Net предоставляет достаточно полный и удобный набор средств симметричного (DES,AES,RC2..) и асимметричного (RSA) шифрования.

Эти средства собраны в пространстве имен System.Security.Cryptography уже начиная с релиза 1.1 .Net. На современных компьютерах практически гарантированно стоит та или иная версия .Net.

Благодаря тому, что значительная часть управляемого кода .Net прекрасно работает через шлюзы COM-interop, средства шифрования .Net можно использовать из скриптовых языков, поддерживающих COM-automation. Это можно делать из vbscript, jscript и из 1С.


Пример использования RSA-шифрования

По умолчанию используются 1024-битные ключи RSA, что считается на сегодняшний день вполне надежным



<source lang=1c> Процедура Тест_ОбъектRSAиз1С_2() Экспорт strSrcTextValue = "abc тест";

objCrypt = Новый COMОбъект("System.Security.Cryptography.RSACryptoServiceProvider"); strXMLPrivateAndPublicKey = objCrypt.ToXmlString(True); strXMLPublicKey = objCrypt.ToXmlString(False);

objCrypt.FromXmlString(strXMLPublicKey);

binSrcTextValue = UtfToSafeArray(strSrcTextValue); binEncryptValue = objCrypt.Encrypt(binSrcTextValue, False);

strEncryptValue = SafeArrayToBase64(binEncryptValue);

Тестирование.ПроверитьНеРавенство(strSrcTextValue, strEncryptValue);

objCrypt.FromXmlString(strXMLPrivateAndPublicKey);

binEncryptValue2 = Base64ToSafeArray(strEncryptValue); binDecryptValue = objCrypt.Decrypt(binEncryptValue2, False);


strDecryptValue = SafeArrayToUtf(binDecryptValue);

Сообщить("Decrypt: " + strDecryptValue);

Тестирование.ПроверитьРавенство(strSrcTextValue, strDecryptValue); objCrypt = Неопределено; КонецПроцедуры </source>

Функции поддержки преобразования параметров

Одна из основных сложностей состоит в преобразовании строк и бинарных данных в массивы COMSafaArray с типом элементов "VT_UI1". Возможная реализация приведена ниже:

<source lang=1c> Функция UtfToSafeArray(строка) Экспорт adTypeBinary = 1; adTypeText = 2; adReadAll = -1; csUTF8 = "utf-8";

objStream = Новый COMОбъект("ADODB.Stream");

objStream.Open(); objStream.Type = adTypeText; objStream.Charset = csUTF8; objStream.WriteText(строка);

objStream.Position = 0; objStream.Type = adTypeBinary;

bin = objStream.Read(adReadAll);

objStream.Close(); objStream = Неопределено;

Return bin; КонецФункции

Функция SafeArrayToUtf(bin) Экспорт adTypeBinary = 1; adTypeText = 2; adReadAll = -1; csUTF8 = "utf-8";

objStream = Новый COMОбъект("ADODB.Stream"); objStream.Open(); objStream.Type = adTypeBinary; objStream.Write(bin);

objStream.Position = 0; objStream.Type = adTypeText; objStream.Charset = csUTF8;

str = objStream.ReadText(adReadAll);

objStream.Close(); objStream = Неопределено;

Return str; КонецФункции

Функция SafeArrayToBase64(bin) Экспорт ИмяВременногоФайла = ПолучитьИмяВременногоФайла();

adTypeBinary = 1; adSaveCreateNotExist = 1; objStream = Новый COMОбъект("ADODB.Stream"); objStream.Open(); objStream.Type = adTypeBinary; objStream.Write(bin); objStream.SaveToFile(ИмяВременногоФайла, adSaveCreateNotExist); objStream.Close(); objStream = Неопределено;

ДвоичныеДанные = Новый ДвоичныеДанные(ИмяВременногоФайла); УдалитьФайлы(ИмяВременногоФайла);

Возврат Base64Строка(ДвоичныеДанные); КонецФункции

Функция Base64ToSafeArray(str) Экспорт ИмяВременногоФайла = ПолучитьИмяВременногоФайла(); ДвоичныеДанные = Base64Значение(str); ДвоичныеДанные.Записать(ИмяВременногоФайла);

adTypeBinary = 1; adReadAll = -1; objStream = Новый COMОбъект("ADODB.Stream"); objStream.Open(); objStream.Type = adTypeBinary; objStream.LoadFromFile(ИмяВременногоФайла); bin = objStream.Read(adReadAll); objStream.Close(); objStream = Неопределено;

УдалитьФайлы(ИмяВременногоФайла); Возврат bin; КонецФункции

</source>

Пример использования симметричного шифрования AES

<source lang=1c>

Процедура Тест_AESРасшифровать() Экспорт saKey = Неопределено; saIV = Неопределено; AESСоздатьКлюч(saKey, saIV);

strSrc = "тест test"; binSrc = UtfToSafeArray(strSrc);

binEnc = AESЗашифровать(saKey, saIV, binSrc); binDec = AESРасшифровать(saKey, saIV, binEnc);

strDec = SafeArrayToUtf(binDec);

Тестирование.ПроверитьРавенство(strSrc, strDec); КонецПроцедуры


Процедура AESСоздатьКлюч(saKey, saIV, KeySize = 128) Экспорт aes = Новый COMОбъект("System.Security.Cryptography.RijndaelManaged"); aes.KeySize = KeySize; aes.GenerateKey(); aes.GenerateIV(); saKey = aes.Key; saIV = aes.IV; aes = Неопределено; КонецПроцедуры

Функция AESЗашифровать(saKey, saIV, binSrc) Экспорт aes = Новый COMОбъект("System.Security.Cryptography.RijndaelManaged"); aes.Key = saKey; aes.IV = saIV; encryptor = aes.CreateEncryptor(); binEnc = encryptor.TransformFinalBlock(binSrc, 0, binSrc.GetLength()); aes = Неопределено; Возврат binEnc; КонецФункции


Функция AESРасшифровать(saKey, saIV, binEnc) Экспорт aes = Новый COMОбъект("System.Security.Cryptography.RijndaelManaged"); aes.Key = saKey; aes.IV = saIV; Decryptor = aes.CreateDecryptor(); binDec = decryptor.TransformFinalBlock(binEnc, 0, binEnc.GetLength()); aes = Неопределено; Возврат binDec; КонецФункции

</source>

Пример использования цифровой подписи RSA

<source lang=1c>

Процедура Тест_RSAПроверитьПодпись() Экспорт strSrc = "тест test"; binSrc = UtfToSafeArray(strSrc);

ОткрытыйКлючОтправителя = ""; ЗакрытыйКлючОтправителя = ""; RSAСоздатьКлючи(ОткрытыйКлючОтправителя, ЗакрытыйКлючОтправителя);

binSign = RSAПодписать(ЗакрытыйКлючОтправителя, binSrc); Тестирование.ПроверитьРавенство(128, binSign.GetLength());

результат = RSAПроверитьПодпись(ОткрытыйКлючОтправителя, binSrc, binSign); Тестирование.ПроверитьИстину(результат); КонецПроцедуры


Процедура RSAСоздатьКлючи(ОткрытыйКлюч, ЗакрытыйКлюч) Экспорт objCrypt = Новый COMОбъект("System.Security.Cryptography.RSACryptoServiceProvider"); ЗакрытыйКлюч = objCrypt.ToXmlString(True); ОткрытыйКлюч = objCrypt.ToXmlString(False); objCrypt = Неопределено; КонецПроцедуры

Функция RSAПодписать(ЗакрытыйКлючОтправителя, binSrc) Экспорт objCrypt = Новый COMОбъект("System.Security.Cryptography.RSACryptoServiceProvider"); objCrypt.FromXmlString(ЗакрытыйКлючОтправителя);

objHash = Новый COMОбъект("System.Security.Cryptography.SHA1CryptoServiceProvider"); hash = objHash.ComputeHash_2(binSrc);

binSign = objCrypt.SignHash(hash, "SHA1");

objCrypt = Неопределено; objHash = Неопределено;

Возврат binSign; КонецФункции


Функция RSAПроверитьПодпись(ОткрытыйКлючОтправителя, binSrc, binSign) Экспорт objCrypt = Новый COMОбъект("System.Security.Cryptography.RSACryptoServiceProvider"); objCrypt.FromXmlString(ОткрытыйКлючОтправителя);

objHash = Новый COMОбъект("System.Security.Cryptography.SHA1CryptoServiceProvider"); hash = objHash.ComputeHash_2(binSrc);

результат = objCrypt.VerifyHash(hash, "SHA1", binSign);

objCrypt = Неопределено; objHash = Неопределено;

Возврат результат; КонецФункции

</source>

Совместное использование асимметричного и симметричного шифрования

При использовании алгоритмов асимметричного шифрования, таких как RSA, есть определенные ограничения:

  • работает медленно, примерно в 100-1000 раз медленнее чем симметричное шифрование
  • при реальных размерах ключа (1024 бита и более) приходится ограничивать размер сообщения, иначе алгоритмы требуют слишком много ресурсов (памяти и процессора)
  • практически в реализации .Nеt при размере ключа 1024 бит максимальный размер сообщения RSA ограничен 117 байтами
  • "многократное" применение RSA (то есть нарезка исходного сообщения по 100 байт и шифрование их по отдельности) не рекомендуется, так как это резко снижает потенциальную стойкость алгоритма

С учетом этих ограничений все реальные схемы использования асимметричных ключей работают примерно так:

  • генерируется сеансовый ключ для какого-либо симметричного алгоритма
  • сеансовый ключ шифруется схемой RSA
  • сообщение шифруется сеансовым ключом

Пример совместного использования асимметричного и симметричного шифрования с подписью и верификацией данных:

<source lang=1c> Процедура Тест_RSAРаспаковать() Экспорт ОткрытыйКлючПолучателя = ""; ЗакрытыйКлючПолучателя = ""; RSAСоздатьКлючи(ОткрытыйКлючПолучателя, ЗакрытыйКлючПолучателя);

ОткрытыйКлючОтправителя = ""; ЗакрытыйКлючОтправителя = ""; RSAСоздатьКлючи(ОткрытыйКлючОтправителя, ЗакрытыйКлючОтправителя);

НаДату = ТекущаяДата(); стДанные = Новый Структура("Наименование,Дата", "Тестовая организация", НаДату);

стрПакет = RSAУпаковать(ОткрытыйКлючПолучателя, ЗакрытыйКлючОтправителя, стДанные);

стДанные1 = RSAРаспаковать(ЗакрытыйКлючПолучателя, ОткрытыйКлючОтправителя, стрПакет);

Тестирование.ПроверитьРавенство(Тип("Структура"), ТипЗнч(стДанные1)); Тестирование.ПроверитьРавенство("Тестовая организация", стДанные1.Наименование); Тестирование.ПроверитьРавенство(НаДату, стДанные1.Дата);

КонецПроцедуры

Процедура RSAСоздатьКлючи(ОткрытыйКлюч, ЗакрытыйКлюч) Экспорт objCrypt = Новый COMОбъект("System.Security.Cryptography.RSACryptoServiceProvider"); ЗакрытыйКлюч = objCrypt.ToXmlString(True); ОткрытыйКлюч = objCrypt.ToXmlString(False); objCrypt = Неопределено; КонецПроцедуры


Функция RSAУпаковать(ОткрытыйКлючПолучателя, ЗакрытыйКлючОтправителя, Данные1С) Экспорт Если ЗначениеЗаполнено(ЗакрытыйКлючОтправителя) Тогда rsaОтправителя = Новый COMОбъект("System.Security.Cryptography.RSACryptoServiceProvider"); rsaОтправителя.FromXmlString(ЗакрытыйКлючОтправителя); Иначе rsaОтправителя = Неопределено; КонецЕсли;


rsaПолучателя = Новый COMОбъект("System.Security.Cryptography.RSACryptoServiceProvider"); rsaПолучателя.FromXmlString(ОткрытыйКлючПолучателя);

sha1 = Новый COMОбъект("System.Security.Cryptography.SHA1CryptoServiceProvider");

стрДанные = ЗначениеВСтрокуВнутр(Данные1С); saДанные = UtfToSafeArray(стрДанные);

aes = Новый COMОбъект("System.Security.Cryptography.RijndaelManaged"); aes.KeySize = 128; aes.GenerateKey(); aes.GenerateIV(); encryptor = aes.CreateEncryptor();

saКлючСеанса = saСоединить(aes.IV, aes.Key); saEncКлючСеанса = rsaПолучателя.Encrypt(saКлючСеанса, False);

saHash = sha1.ComputeHash_2(saДанные);

Если rsaОтправителя = Неопределено Тогда saПодпись = Новый COMSafeArray("VT_UI1", 0); Иначе saПодпись = rsaОтправителя.SignHash(saHash, "SHA1"); КонецЕсли;

saEncПодпись = encryptor.TransformFinalBlock(saПодпись, 0, saПодпись.GetLength());

saEncДанные = encryptor.TransformFinalBlock(saДанные, 0, saДанные.GetLength());

saПакет = saСобратьПакет(saEncКлючСеанса, saEncПодпись, saEncДанные);

стрПакет = SafeArrayToBase64(saПакет);

rsaОтправителя = Неопределено; rsaПолучателя = Неопределено; sha1 = Неопределено; aes = Неопределено;

Возврат стрПакет; КонецФункции

Функция RSAРаспаковать(ЗакрытыйКлючПолучателя, ОткрытыйКлючОтправителя, стрПакет) Экспорт Если ЗначениеЗаполнено(ОткрытыйКлючОтправителя) Тогда rsaОтправителя = Новый COMОбъект("System.Security.Cryptography.RSACryptoServiceProvider"); rsaОтправителя.FromXmlString(ОткрытыйКлючОтправителя); Иначе rsaОтправителя = Неопределено; КонецЕсли;

rsaПолучателя = Новый COMОбъект("System.Security.Cryptography.RSACryptoServiceProvider"); rsaПолучателя.FromXmlString(ЗакрытыйКлючПолучателя);

sha1 = Новый COMОбъект("System.Security.Cryptography.SHA1CryptoServiceProvider");

aes = Новый COMОбъект("System.Security.Cryptography.RijndaelManaged");

saПакет = Base64ToSafeArray(стрПакет);

смещение = 0; saEncКлючСеанса = saПолучитьБлок(saПакет, смещение); saEncПодпись = saПолучитьБлок(saПакет, смещение); saEncДанные = saПолучитьБлок(saПакет, смещение);

saКлючСеанса = rsaПолучателя.Decrypt(saEncКлючСеанса, False); saIV = saВыделитьДиапазон(saКлючСеанса, 0, 16); saKey = saВыделитьДиапазон(saКлючСеанса, 16, saКлючСеанса.GetLength() - 16); aes.Key = saKey; aes.IV = saIV; decryptor = aes.CreateDecryptor();

saПодпись = decryptor.TransformFinalBlock(saEncПодпись, 0, saEncПодпись.GetLength()); saДанные = decryptor.TransformFinalBlock(saEncДанные, 0, saEncДанные.GetLength());

Если rsaОтправителя <> Неопределено Тогда saHash = sha1.ComputeHash_2(saДанные); ПодписьКорректна = rsaОтправителя.VerifyHash(saHash, "SHA1", saПодпись); Если НЕ ПодписьКорректна Тогда ВызватьИсключение "Обнаружено разрушение данных: некорректная цифровая подпись"; КонецЕсли; КонецЕсли;

стрДанные = SafeArrayToUtf(saДанные); Данные1С = ЗначениеИзСтрокиВнутр(стрДанные);

rsaОтправителя = Неопределено; rsaПолучателя = Неопределено; sha1 = Неопределено; aes = Неопределено;

Возврат Данные1С; КонецФункции


Функция saПолучитьБлок(saПакет, смещение) старшийБайт = saПакет.GetValue(смещение + 0); младшийБайт = saПакет.GetValue(смещение + 1); длина = старшийБайт * 256 + младшийБайт;

saБлок = Новый COMSafeArray("VT_UI1", длина);

смещение = смещение + 2; Для индекс = 0 по длина - 1 Цикл saБлок.SetValue(индекс, saПакет.GetValue(индекс + смещение)); КонецЦикла; смещение = смещение + длина;

Возврат saБлок; КонецФункции

Функция saВыделитьДиапазон(saПакет, смещение, длина) saБлок = Новый COMSafeArray("VT_UI1", длина);

Для индекс = 0 по длина - 1 Цикл saБлок.SetValue(индекс, saПакет.GetValue(индекс + смещение)); КонецЦикла;

Возврат saБлок; КонецФункции

Процедура saДобавитьДанные(saПакет, saДанные, смещение) длина = saДанные.GetLength(); saПакет.SetValue(смещение + 0, Цел(длина / 256)); saПакет.SetValue(смещение + 1, длина % 256); смещение = смещение + 2; Для индекс = 0 по длина - 1 Цикл saПакет.SetValue(смещение + индекс, saДанные.GetValue(индекс)); КонецЦикла; смещение = смещение + длина; КонецПроцедуры

Функция saСобратьПакет(saКлюч, saПодпись, saДанные) Экспорт saПакет = Новый COMSafeArray("VT_UI1", saКлюч.GetLength() + saПодпись.GetLength() + saДанные.GetLength() + 6);

смещение = 0;

saДобавитьДанные(saПакет, saКлюч, смещение); saДобавитьДанные(saПакет, saПодпись, смещение); saДобавитьДанные(saПакет, saДанные, смещение);

Возврат saПакет; КонецФункции

Функция saСоединить(sa1, sa2) длина1 = sa1.GetLength(); длина2 = sa2.GetLength(); saРезультат = Новый COMSafeArray("VT_UI1", длина1 + длина2);

Для индекс = 0 по длина1 - 1 Цикл saРезультат.SetValue(индекс, sa1.GetValue(индекс)); КонецЦикла;

Для индекс = 0 по длина2 - 1 Цикл saРезультат.SetValue(индекс + длина1, sa2.GetValue(индекс)); КонецЦикла;

Возврат saРезультат; КонецФункции


</source>


Пример упакованного пакета данных

AICMxryOHYJJuxYufTP4X643HVamNgUuQ4lcY/L1YXk6HkQ9LnL02h/ERIgKwDSX
pFJqTVisEF3Y+BORPJGyDlSeJ/Z/qwnX606AfTQxgEkB1qfW72a86DYyhG6kvjbY
Kc4e9QIW1Za6HJgkw1hjXr85meXIibOVm4BfNDZCvYmRswCQemALjNne1dacqmW4
RzcASMLxLZciFVXDBQB+LbuojpdY5Vn++hpzpWt1vh/Mj4bdeEHNGg3p229kbOBW
Iog5zVHpj+mKADK8IeLKZwYWghP19bAo4cMVJ+zzceMjzLCAWQ+c1GI6aq6FRcyJ
Dh07lzyvDfcw1iS0AbZg23Y5BRW9zCpg5xt4RX2vcAhFHO0iAMC+6/jZofsIY72g
YukEgKWm9TSDhiRMcozbrCjv4Gug+aPXUAK0gvrS42C/ITynTyrtTE1erHFWXmCt
VhAdpiE5sE6QFI8LYFB4pcPAqOooFEj/kBUQHEArJyyacWazs2/jTLCv1SmLrKjs
tTZhafQ2y9pEBGvfqsG4YhKSjA7SVJzgtfcgAi76bHiWyURGQqiqNym1WpXp9WO/
eckaQFAi4rqYpt0Qn93G3/iJ/Wq+hPMN/z9c6UqmQtbm7/Tp0f4=


Пример xml-сериализации открытого ключа RSA

<source lang=xml> <RSAKeyValue> <Modulus>u7ptm/rGsEhZ3JhKApnDPUVj6bPnSYmVzGkMrAsElT2CIRD6zWbA0+GcoQ5qCz/ZTeCeDBgCIhDkACdy9Q+kjl1HGlwWS8OGCaOLt /14zXuYoCeSZMP3sJMI7BuzRCvu5NBduxfZYC75UnKzO29WjXIxF5h32U8nArv6WivhPRs=</Modulus> <Exponent>AQAB</Exponent> </RSAKeyValue> </source>

Пример xml-сериализации закрытого ключа RSA:

<source lang=xml> <RSAKeyValue> <Modulus>u7ptm/rGsEhZ3JhKApnDPUVj6bPnSYmVzGkMrAsElT2CIRD6zWbA0+GcoQ5qCz/ZTeCeDBgCIhDkACdy9Q+kjl1HGlwWS8OGCaOLt /14zXuYoCeSZMP3sJMI7BuzRCvu5NBduxfZYC75UnKzO29WjXIxF5h32U8nArv6WivhPRs=</Modulus> <Exponent>AQAB</Exponent>

/HvZG4UCGgsngAPG5jie9K4uctxIuuuTft1yVy9rz3zrO58AyxgqEKan2ezikpjzhKeUF6Y+6SBf/DJIs9W9fQ==

vle25eGrqNY0y1QvQgQRBnFUDkxnvmH7zIiMJhL/fHzhLBQOIRDsgqIgEvGMGfkjMxPyOSi1cE4eDe1rMBdIdw== <DP>vpLQpff4RSYkM3kmWUFlobQTdTkWYJhN5VVK58nwa1WTzJXQqHtdzOGuEky+G/782CURH+So2ZhJOvfNbognlQ==</DP> <DQ>Mt0rseiJP7fmKcOYUVLW3drg9GU0f+qdJ/4BPZdsEG8qmOXPFMT5/rqmSYmkv7gU0OxumRmoypcFbwFX2GUQZw==</DQ> <InverseQ>wbxr31t5DAUanNTJojPnQS5ex0RXDVAiGcyMCpktOQNTvvN64Hh6975JYg1wEtFuu8doLh6tgfexvpOtVhJq2A==</InverseQ> <D>M6bn4bLuWDKQBxIfyvdjsMgW5YaKbMFeZg/BXNTrPeTemRqC52EOTT4WSnoc01uN /s9+rPUuIRFkqmYwSx0yuT85ayfoiiDDLq7GDt8M21EuAep3yxOAHAgJ3Ddr9UApv72MuDKNHrOnr3WDR5OPWtuS3ZZJ8jVpv5jwADBsEYE=</D> </RSAKeyValue> </source>

Определение доступности .Net

<source lang=1c> Процедура Тест_ВерсияDotNet() Экспорт objHtml = Новый COMОбъект("htmlfile"); агент = objHtml.parentWindow.navigator.userAgent;

Сообщить(агент); КонецПроцедуры

</source>