Использование средств шифрования .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, что считается на сегодняшний день вполне надежным |
Процедура Тест_Объект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 = Неопределено;
КонецПроцедуры
Функции поддержки преобразования параметров
Одна из основных сложностей состоит в преобразовании строк и бинарных данных в массивы COMSafaArray с типом элементов "VT_UI1". Возможная реализация приведена ниже:
Функция 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;
КонецФункции
Пример использования симметричного шифрования AES
Процедура Тест_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;
КонецФункции
Пример использования цифровой подписи RSA
Процедура Тест_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 = Неопределено;
Возврат результат;
КонецФункции
Совместное использование асимметричного и симметричного шифрования
При использовании алгоритмов асимметричного шифрования, таких как RSA, есть определенные ограничения:
- работает медленно, примерно в 100-1000 раз медленнее чем симметричное шифрование
- при реальных размерах ключа (1024 бита и более) приходится ограничивать размер сообщения, иначе алгоритмы требуют слишком много ресурсов (памяти и процессора)
- практически в реализации .Nеt при размере ключа 1024 бит максимальный размер сообщения RSA ограничен 117 байтами
- "многократное" применение RSA (то есть нарезка исходного сообщения по 100 байт и шифрование их по отдельности) не рекомендуется, так как это резко снижает потенциальную стойкость алгоритма
С учетом этих ограничений все реальные схемы использования асимметричных ключей работают примерно так:
- генерируется сеансовый ключ для какого-либо симметричного алгоритма
- сеансовый ключ шифруется схемой RSA
- сообщение шифруется сеансовым ключом
Пример совместного использования асимметричного и симметричного шифрования с подписью и верификацией данных:
Процедура Тест_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Результат;
КонецФункции
Пример упакованного пакета данных
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
<RSAKeyValue>
<Modulus>u7ptm/rGsEhZ3JhKApnDPUVj6bPnSYmVzGkMrAsElT2CIRD6zWbA0+GcoQ5qCz/ZTeCeDBgCIhDkACdy9Q+kjl1HGlwWS8OGCaOLt
/14zXuYoCeSZMP3sJMI7BuzRCvu5NBduxfZYC75UnKzO29WjXIxF5h32U8nArv6WivhPRs=</Modulus>
<Exponent>AQAB</Exponent>
</RSAKeyValue>
Пример xml-сериализации закрытого ключа RSA:
<RSAKeyValue>
<Modulus>u7ptm/rGsEhZ3JhKApnDPUVj6bPnSYmVzGkMrAsElT2CIRD6zWbA0+GcoQ5qCz/ZTeCeDBgCIhDkACdy9Q+kjl1HGlwWS8OGCaOLt
/14zXuYoCeSZMP3sJMI7BuzRCvu5NBduxfZYC75UnKzO29WjXIxF5h32U8nArv6WivhPRs=</Modulus>
<Exponent>AQAB</Exponent>
<P>/HvZG4UCGgsngAPG5jie9K4uctxIuuuTft1yVy9rz3zrO58AyxgqEKan2ezikpjzhKeUF6Y+6SBf/DJIs9W9fQ==</P>
<Q>vle25eGrqNY0y1QvQgQRBnFUDkxnvmH7zIiMJhL/fHzhLBQOIRDsgqIgEvGMGfkjMxPyOSi1cE4eDe1rMBdIdw==</Q>
<DP>vpLQpff4RSYkM3kmWUFlobQTdTkWYJhN5VVK58nwa1WTzJXQqHtdzOGuEky+G/782CURH+So2ZhJOvfNbognlQ==</DP>
<DQ>Mt0rseiJP7fmKcOYUVLW3drg9GU0f+qdJ/4BPZdsEG8qmOXPFMT5/rqmSYmkv7gU0OxumRmoypcFbwFX2GUQZw==</DQ>
<InverseQ>wbxr31t5DAUanNTJojPnQS5ex0RXDVAiGcyMCpktOQNTvvN64Hh6975JYg1wEtFuu8doLh6tgfexvpOtVhJq2A==</InverseQ>
<D>M6bn4bLuWDKQBxIfyvdjsMgW5YaKbMFeZg/BXNTrPeTemRqC52EOTT4WSnoc01uN
/s9+rPUuIRFkqmYwSx0yuT85ayfoiiDDLq7GDt8M21EuAep3yxOAHAgJ3Ddr9UApv72MuDKNHrOnr3WDR5OPWtuS3ZZJ8jVpv5jwADBsEYE=</D>
</RSAKeyValue>
Определение доступности .Net
Процедура Тест_ВерсияDotNet() Экспорт
objHtml = Новый COMОбъект("htmlfile");
агент = objHtml.parentWindow.navigator.userAgent;
Сообщить(агент);
КонецПроцедуры