AlexMAS / GostCryptography

.NET driver for ViPNet CSP and CryptoPro CSP
MIT License
129 stars 41 forks source link

Дешифрация на стороне сервиса #3

Closed DPonomarchuk closed 7 years ago

DPonomarchuk commented 7 years ago

Здравствуйте, Александр. Пробую использовать вашу библиотеку в сервисе на IIS. При дешифрации использовал пример из https://github.com/AlexMAS/GostCryptography/tree/master/Source/GostCryptography.Tests

var encryptedXml= new GostEncryptedXml(doc); encryptedXml.AddKeyNameMapping("KeyName1", privateKey); / Добавляю ссылку на приватный ассиметричный ключ с установленным privateKey.SetContainerPassword(secureString); / encryptedXml.DecryptDocument(); / Но на этапе дешифровки криптопровайдер всё равно запрашивает выводит окно для указания пароля для сертификата /

Возможно ли установить пароль для контейнера сертификата программно без вывода на экран окна криптопровайдера при дешифрации (сертификаты находятся в локальном хранилище компьютера)?

AlexMAS commented 7 years ago

Здравствуйте.

Если я правильно помню, то сделать это можно примерно так:

// Получение параметров криптопровайдера по сертификату
var cspParameters = X509CertificateHelper.GetPrivateKeyInfo(Certificate);

// Запрет вывода окна ввода пароля, при неправильном пароле получить ошибку
cspParameters.Flags = CspProviderFlags.NoPrompt;

// Создание строки с паролем (пример: Qwerty123)
var password = new SecureString();
password.AppendChar('Q');
password.AppendChar('w');
password.AppendChar('e');
password.AppendChar('r');
password.AppendChar('t');
password.AppendChar('y');
password.AppendChar('1');
password.AppendChar('2');
password.AppendChar('3');

var alg = new Gost3410AsymmetricAlgorithm(cspParameters);

alg.SetContainerPassword(password);

Далее, например, в случае с подписью XML:

xml.SigningKey = alg;

xml.ComputeSignature();
DPonomarchuk commented 7 years ago

Да. При подписи xml все прекрасно работает и окно ввода пароля не появляется. А вот при дешифрации каждый раз запрашивается пароль. Ума не приложу, как его задать программно.

AlexMAS commented 7 years ago

А можете кусочек кода с примером, который воспроизводит ситуацию? ;)

AlexMAS commented 7 years ago

Забял сказать, что у класса CspParameters также есть свойство для установки пароля. Можно попробовать использовать его.

cspParameters.KeyPassword = password;

Это нужно сделать соответственно до создания алгоритма.

DPonomarchuk commented 7 years ago

Пример кода (вчера не получилось ответить - немного приболел...). Указание пароля в cspParamets также не помогло.

        XmlDocument doc = new XmlDocument();
        var fileName = Path.Combine(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location), 
        @"XmlFile1.xml");
        doc.Load(fileName);
        var exml = new GostEncryptedXml(doc);

        X509Certificate2 Certificate = GetCertByNum("‎01 d1 fe a4 65 b3 f8 40 00 00 00 0d 00 05 00 01");
        var cspParam = X509CertificateHelper.GetPrivateKeyInfo(Certificate);

        System.Security.SecureString ss = new System.Security.SecureString();
        for (int i = 0; i < "1234567890".Length; i++)
        {
            ss.AppendChar("1234567890".ToCharArray(i, 1)[0]);
        }
        cspParam.KeyPassword = ss;
        Gost3410AsymmetricAlgorithm alg = new Gost3410AsymmetricAlgorithm(cspParam);
        alg.SetContainerPassword(ss); //При вычислении ЭЦП установка пароля прекрасно работает

        exml.AddKeyNameMapping("KeyName1", alg); // Возможно тут я делаю, что-то неправильно
        exml.DecryptDocument(); // появляется окно ввода пароля. 

Зашифрованная xml и сертификат (если нужно) [https://1drv.ms/u/s!AopaN--FViu5a-002UWBgxSkVkI]

AlexMAS commented 7 years ago

Я создал self-signed сертификат (реального у меня, к сожалению, нет). Установил его во вновь созданный контейнер VipNet. В настройках контейнера указал, что пароль доступа нужно сохранить на компьютере и не запрашивать при доступе к контейнеру (см. ниже). После этого выполнил тест https://github.com/AlexMAS/GostCryptography/blob/master/Source/GostCryptography.Tests/Xml/Encrypt/EncryptedXmlCertificateTest.cs#L22 Все прошло успешно, без запроса пароля.

image

Я не знаю, безопасно ли хранить пароль к контейнеру таким образом. Даже если не хранить, его придется как-то получать в коде вашей программы, например, из конфигурационного файла приложения.

Сейчас попробую удалить пароль и воспроизвести вашу ситуацию.

AlexMAS commented 7 years ago

Да, после того, как я указал, что пароль доступа не нужно хранить, я смог воспроизвести вашу ситуацию. Я попробовал разные способы, но у меня так и не получилось заставить GostEncryptedXml работать без окна ввода пароля (в отличие от ЭЦП). Возможно, есть способ сделать это через CryptoAPI и указать пароль программно, как в случае с методом SetContainerPassword() класса Gost3410AsymmetricAlgorithm, но я не знаю, как, слишком мало информации по этому поводу.

По всей видимости, единственный возможный способ отключить диалог - это сохранить пароль доступа, как указанно в предыдущем ответе. Подойдет ли такой вариант решения вопроса? Также буду благодарен, если вы скажите, как в итоге решили проблему. :)

DPonomarchuk commented 7 years ago

Меня бы устроил такой вариант хранения пароля, но проблема в том, что доступ к контейнеру будет осуществляться через сервис, развернутый в IIS. А при обращении сервиса к контейнеру пароль, заданный для пользователя не подходит, и запрашивается заново + также окно для ввода пароля не появляется на экране, а выводится где-то там, внутри IIS-ки. Если, хотя бы окно появлялось, можно было бы поставить галку "Сохранить пароль" и при следующих операциях дешифровка проходила бы без проблем...

AlexMAS commented 7 years ago

Поправьте меня, если я не прав. У вас есть некоторое Web-приложение, которое размещается на IIS. Для каждого пользователя этого приложения есть сертификат, который хранится в своем контейнере, со своим паролем доступа. При дешифрации пользователь (видимо, через Web-интерфейс) вводит пароль, который должен использоваться на стороне Web-приложения. То есть получается, что пароль в общем случае знает только конечный пользователь вашего Web-приложения. Все верно?

P.s. Я постараюсь в свободное время попробовать найти решение проблемы, просто хочется представлять понять суть проблемы.

DPonomarchuk commented 7 years ago

Всё практически так, как вы себе и представляете. Имеется веб-приложение развернутое на сервере IIS. Пользователь отправляет ему xml, которая подписывается ЭЦП и оборачивается в сообщение, которое в зашифрованном виде передаётся на сторонний сервис, далее веб приложение дешифрует ответ от стороннего сервиса и возвращает ответ пользователю. (пароль от сертификата хранится в файле конфигурации веб-приложения) Подпись и дешифрация проходят на одном сертификате. Буду крайне благодарен, если поможете разобраться )

AlexMAS commented 7 years ago

Может я что-то не до конца понимаю, но смотрите. Web-приложение, работающее на базе IIS, и CSP со всеми сертификатами физически находятся на одной машине (виртуальной или нет, не имеет значения). Хранить пароль контейнера в конфигурационном файле Web-приложения или на диске, используя средства CSP, в общем случае одно и тоже (суть одна - пароль будет сохранен на этой же машине, на постоянном носителе). Только в первом случае мы имеем проблему с диалоговым окном, а во втором - нет. Я полагаю, что проблема в том процесс вашего Web-приложения, будучи запущенным под управлением IIS, запускается от имени какой-то конкретной учетной записи. И, возможно, для нее пароль не сохранен (в понимании CSP). Попробуйте поэкспериментировать с "Параметрами доступа" (см. настройку на картинке выше ), например, добавив туда учетную запись, от имени которой работает процесс IIS. В общем, постарайтесь как-то через настройки CSP сохранить пароль для пользователя, от имени которого запускается ваше Web-приложение. Должно получиться.

P.s. Теорию можно проверить следующим образом. На своей машине установите CSP, сертификат и сохраните пароль. Запустите приложение из под IDE (или как-то из под своей учетной записи) и проверьте, что все работает нормально. После этого создайте новую учетную запись, зайдите под ней, запустите приложение из под IDE. (или как-то из под своей новой учетной записи). Должен появиться диалог. После этого сохраните пароль для новой учетной записи и повторите действия. Диалог должен пропасть.

AlexMAS commented 7 years ago

@DPonomarchuk, у вас получилось добиться успеха?

DPonomarchuk commented 7 years ago

Добрый день, да, похоже такой вариант жизнеспособен. Но пришлось покопаться в IIS. Проблема состояла в том, что учетные записи IIS AppPool \ - это учетные записи «Идентификатор пула приложений», время жизни учетной записи ApplicationPoolIdentity полностью управляется IIS и ОС, и запустить под именем этой учетной записи VipNet CSP либо CryptoPro нельзя. Пришлось создать нового пользователя и добавить в группу IIS_IUSRS, далее в IIS в дополнительных параметрах пула приложений, на котором было развернуто моё веб приложение (в моём случае DefaultAPPPool), изменил параметр "Удостоверение пула приложений" с встроенной учётной записи на "Особая учетная запись" с указанием созданного ранее пользователя. Соответственно после этих манипуляций я смог запустить VipNetCSP через runas и сохранить пароль для сертификата этого пользователя. Благодарю, жаль, что так и не удалось разобраться, как это провернуть программно.)

AlexMAS commented 7 years ago

Спасибо за информацию. Хорошо, что нашелся рабочий способ решить проблему, пускай даже таким образом. К сожалению, механизмы работы с криптографией в .NET Framework не назовешь удобными, особенно, когда речь заходит о криптографии по ГОСТ. Надеюсь в .NET Standard 2+ появятся более гибкие механизмы расширения. С программной реализацией указания пароля постараюсь разобраться в ближайшее время, правда, не уверен, что смогу это сделать, судя по тому, что мне удалось вспомнить.