Ключевое слово в защите информации
КЛЮЧЕВОЕ СЛОВО
в защите информации
Получить ГОСТ TLS-сертификат для домена (SSL-сертификат)
Добро пожаловать, Гость! Чтобы использовать все возможности Вход или Регистрация.

Уведомление

Icon
Error

Опции
К последнему сообщению К первому непрочитанному
Offline benchstyle  
#1 Оставлено : 19 ноября 2020 г. 11:04:51(UTC)
benchstyle

Статус: Участник

Группы: Участники
Зарегистрирован: 26.04.2020(UTC)
Сообщений: 10
Откуда: Муром

Сказал(а) «Спасибо»: 1 раз
Здравствуйте, реализовано подписание пдф прикрепленной подписью с постановкой штампа. Если подписывать несколько документов, то для первого документа вводим в форме PIN, а для второго документа PIN не запрашивается и падает исключение System.Security.Cryptography.CryptographicException: "Нет доступа к карте. Введен неправильный PIN-код
Пользовался этим кодом из SDK для доступа к закрытому ключу:

Цитата:

var cspParameters = new CspParameters
{
//копируем параметры csp из исходного контекста сертификата
KeyContainerName = cert_key.CspKeyContainerInfo.KeyContainerName,
ProviderType = cert_key.CspKeyContainerInfo.ProviderType,
ProviderName = cert_key.CspKeyContainerInfo.ProviderName,
Flags = cert_key.CspKeyContainerInfo.MachineKeyStore
? (CspProviderFlags.UseExistingKey | CspProviderFlags.UseMachineKeyStore)
: (CspProviderFlags.UseExistingKey),
KeyPassword = new SecureString()
};

certificate = new X509Certificate2(certificate.RawData)
{
PrivateKey = new Gost3410_2012_256CryptoServiceProvider(cspParameters)
};
Offline Артём Макаров  
#2 Оставлено : 19 ноября 2020 г. 11:55:01(UTC)
Артём Макаров

Статус: Сотрудник

Группы: Участники
Зарегистрирован: 20.02.2017(UTC)
Сообщений: 205

Сказал(а) «Спасибо»: 4 раз
Поблагодарили: 54 раз в 53 постах
Добрый день. Попробуйте указывать пин напрямую в CspParameters, из тех же примеров

Код:
var cspParameters = new CspParameters();
//копируем параметры csp из исходного контекста сертификата
cspParameters.KeyContainerName = cert_key.CspKeyContainerInfo.KeyContainerName;
cspParameters.ProviderType = cert_key.CspKeyContainerInfo.ProviderType;
cspParameters.ProviderName = cert_key.CspKeyContainerInfo.ProviderName;
cspParameters.Flags = cert_key.CspKeyContainerInfo.MachineKeyStore
                                      ? (CspProviderFlags.UseExistingKey|CspProviderFlags.UseMachineKeyStore)
                                      : (CspProviderFlags.UseExistingKey);
cspParameters.KeyPassword = new SecureString();
// Задаём пин-код на контейнер. string pin - строка с пин кодом на контейнер
foreach (var c in pin)
{
    cspParameters.KeyPassword.AppendChar(c);
}
Техническую поддержку оказываем тут
Наша база знаний
Offline benchstyle  
#3 Оставлено : 19 ноября 2020 г. 12:00:22(UTC)
benchstyle

Статус: Участник

Группы: Участники
Зарегистрирован: 26.04.2020(UTC)
Сообщений: 10
Откуда: Муром

Сказал(а) «Спасибо»: 1 раз
Артем, пользователей довольно много, не говоря о том, что хранить реквизиты доступа в коде не лучшая идея, других вариантов нет?
На данный момент я не задаю cspParameters.KeyPassword = new SecureString(), что позволяет подписывать документы по несколько штук, но и пин-код также не запрашивается.
Это видится мне не лучшим решением с точки зрения ИБ.
Автор: Артём Макаров Перейти к цитате
Добрый день. Попробуйте указывать пин напрямую в CspParameters, из тех же примеров

Код:
var cspParameters = new CspParameters();
//копируем параметры csp из исходного контекста сертификата
cspParameters.KeyContainerName = cert_key.CspKeyContainerInfo.KeyContainerName;
cspParameters.ProviderType = cert_key.CspKeyContainerInfo.ProviderType;
cspParameters.ProviderName = cert_key.CspKeyContainerInfo.ProviderName;
cspParameters.Flags = cert_key.CspKeyContainerInfo.MachineKeyStore
                                      ? (CspProviderFlags.UseExistingKey|CspProviderFlags.UseMachineKeyStore)
                                      : (CspProviderFlags.UseExistingKey);
cspParameters.KeyPassword = new SecureString();
// Задаём пин-код на контейнер. string pin - строка с пин кодом на контейнер
foreach (var c in pin)
{
    cspParameters.KeyPassword.AppendChar(c);
}


Offline Артём Макаров  
#4 Оставлено : 19 ноября 2020 г. 12:47:36(UTC)
Артём Макаров

Статус: Сотрудник

Группы: Участники
Зарегистрирован: 20.02.2017(UTC)
Сообщений: 205

Сказал(а) «Спасибо»: 4 раз
Поблагодарили: 54 раз в 53 постах
Хранить пины в коде действительно плохая идея, её я вам и не предлагаю. Запрашивать пин код можно в вашем приложении у пользователя самостоятельно и передавать в коде в виде строки.

Однако вопрос в другом. Сделал небольшой код открытия сертификата с пином и использования для подписи.

Код:
 store.Open(OpenFlags.ReadOnly);
                var cert = store.Certificates.Find(X509FindType.FindBySubjectName, "testCertWithContainer", false)[0];
                var cert_key = cert.PrivateKey as Gost3410_2012_256CryptoServiceProvider;
                var cspParameters = new CspParameters();
                //копируем параметры csp из исходного контекста сертификата
                cspParameters.KeyContainerName = cert_key.CspKeyContainerInfo.KeyContainerName;
                cspParameters.ProviderType = cert_key.CspKeyContainerInfo.ProviderType;
                cspParameters.ProviderName = cert_key.CspKeyContainerInfo.ProviderName;
                cspParameters.Flags = cert_key.CspKeyContainerInfo.MachineKeyStore
                                                      ? (CspProviderFlags.UseExistingKey | CspProviderFlags.UseMachineKeyStore)
                                                      : (CspProviderFlags.UseExistingKey);

                var keyToUse = new Gost3410_2012_256CryptoServiceProvider(cspParameters);

                var sig1 = keyToUse.SignData(new byte[] { 0, 0, 0, 0 }, new Gost3411_2012_256CryptoServiceProvider());
                var sig2 = keyToUse.SignData(new byte[] { 0, 0, 0, 0 }, new Gost3411_2012_256CryptoServiceProvider());


Код один раз запрашивает пин-код через окошко csp и позволяет подписать дважды.

Другой вариант - запросить пин-код самостоятельно и передать в виде строки

Код:
var pin = "1";
            using (var store = new X509Store(StoreName.My, StoreLocation.LocalMachine))
            {
                store.Open(OpenFlags.ReadOnly);
                var cert = store.Certificates.Find(X509FindType.FindBySubjectName, "testCertWithContainer", false)[0];
                var cert_key = cert.PrivateKey as Gost3410_2012_256CryptoServiceProvider;
                var cspParameters = new CspParameters();
                //копируем параметры csp из исходного контекста сертификата
                cspParameters.KeyContainerName = cert_key.CspKeyContainerInfo.KeyContainerName;
                cspParameters.ProviderType = cert_key.CspKeyContainerInfo.ProviderType;
                cspParameters.ProviderName = cert_key.CspKeyContainerInfo.ProviderName;
                cspParameters.Flags = cert_key.CspKeyContainerInfo.MachineKeyStore
                                                      ? (CspProviderFlags.UseExistingKey | CspProviderFlags.UseMachineKeyStore)
                                                      : (CspProviderFlags.UseExistingKey);
                cspParameters.KeyPassword = new SecureString();
                // Задаём пин-код на контейнер. string pin - строка с пин кодом на контейнер
                foreach (var c in pin)
                {
                    cspParameters.KeyPassword.AppendChar(c);
                }

                var keyToUse = new Gost3410_2012_256CryptoServiceProvider(cspParameters);

                var sig1 = keyToUse.SignData(new byte[] { 0, 0, 0, 0 }, new Gost3411_2012_256CryptoServiceProvider());
                var sig2 = keyToUse.SignData(new byte[] { 0, 0, 0, 0 }, new Gost3411_2012_256CryptoServiceProvider());
            }


В этом сценарии окошко от csp не пробрасывается, пин задаём самостоятельно (как то получив от пользователя и сохраним в строку pin). Позволяет дважды подписать.

Оба сценарии должны отрабатываться. Если они оба работают и в вашем сценарии при повторной подписи вы ловите ошибку - возможно вы неявно некорректно работаете с контейнером, или какие то проблемы с контейнером (превышено количество попыток ввода пин-кода или прочее).

Если вы не хотите сами явно задавать пароль на контейнер и хотите видеть окошки csp - нет смысла пересобирать руками cspParams (ибо цель этого как раз задание пароля без проброса окошек), такой код тоже отработает как нужно (запросит пароль один раз):

Код:
 using (var store = new X509Store(StoreName.My, StoreLocation.LocalMachine))
            {
                store.Open(OpenFlags.ReadOnly);
                var cert = store.Certificates.Find(X509FindType.FindBySubjectName, "testCertWithContainer", false)[0];
var keyToUse = cert.PrivateKey as Gost3410_2012_256CryptoServiceProvider;

                var sig1 = keyToUse.SignData(new byte[] { 0, 0, 0, 0 }, new Gost3411_2012_256CryptoServiceProvider());
                var sig2 = keyToUse.SignData(new byte[] { 0, 0, 0, 0 }, new Gost3411_2012_256CryptoServiceProvider());


Аналогичные темы на форуме -
https://www.cryptopro.ru....aspx?g=posts&t=4190
https://www.cryptopro.ru....aspx?g=posts&t=1840
Техническую поддержку оказываем тут
Наша база знаний
Offline benchstyle  
#5 Оставлено : 19 ноября 2020 г. 13:17:47(UTC)
benchstyle

Статус: Участник

Группы: Участники
Зарегистрирован: 26.04.2020(UTC)
Сообщений: 10
Откуда: Муром

Сказал(а) «Спасибо»: 1 раз
Артем, спасибо за ответы, а есть вариант как при каждом подписании вызывать окошко CSP для ввода пина?
Offline Артём Макаров  
#6 Оставлено : 19 ноября 2020 г. 13:58:07(UTC)
Артём Макаров

Статус: Сотрудник

Группы: Участники
Зарегистрирован: 20.02.2017(UTC)
Сообщений: 205

Сказал(а) «Спасибо»: 4 раз
Поблагодарили: 54 раз в 53 постах
Можно, но очень аккуратно. CSP использует пины только при открытии контейнера с ключом. Контейнер закрывается когда мы зовём dispose. Можно dispose'ить контейнер и открывать его при каждый подписи. Для этого нужно сделать using на закрытый ключ.

Код:
using (var keyToUse = SomeHowGetPrivateKey())
{
    var sig1 = keyToUse.SignData(new byte[] { 0, 0, 0, 0 }, new Gost3411_2012_256CryptoServiceProvider());
}


Важный момент - когда вы зовёте Dispose на ключ (неявно при закрытии using), контейнер закрывается и ссылка на этот ключ портится. Т.е. если использовать

Код:
using (var keyToUse = cert.PrivateKey as Gost3410_2012_256CryptoServiceProvider)
{
    var sig1 = keyToUse.SignData(new byte[] { 0, 0, 0, 0 }, new Gost3411_2012_256CryptoServiceProvider());
}
using (var keyToUse = cert.PrivateKey as Gost3410_2012_256CryptoServiceProvider)
{
    var sig2 = keyToUse.SignData(new byte[] { 0, 0, 0, 0 }, new Gost3411_2012_256CryptoServiceProvider());
}


вторая подпись не пройдёт с ошибкой, ибо контейнер с ключом уже закрыт, объект сертификата однако всё ещё содержит на него ссылку.

Отсюда придётся каждый раз создавать объект сертификата для каждый подписи
Код:
using(var cert = var cert = store.Certificates.Find(X509FindType.FindBySubjectName, "testCertWithContainer", false)[0])
{
    using (var keyToUse = cert.PrivateKey as Gost3410_2012_256CryptoServiceProvider)
    {
        var sig = keyToUse1.SignData(new byte[] { 0, 0, 0, 0 }, new Gost3411_2012_256CryptoServiceProvider());
    }
}


или использовать что то такое хитрое
Код:
            CspParameters cspParameters;
            using (var store = new X509Store(StoreName.My, StoreLocation.CurrentUser))
            {
                // Открываем хранилище и читаем параметры сертификата
                store.Open(OpenFlags.ReadOnly);
                using (var cert = store.Certificates.Find(X509FindType.FindBySubjectName, "testCertWithContainer", false)[0])
                {
                    using (var cert_key = cert.PrivateKey as Gost3410_2012_256CryptoServiceProvider)
                    {
                        cspParameters = new CspParameters();
                        //копируем параметры csp из исходного контекста сертификата
                        cspParameters.KeyContainerName = cert_key.CspKeyContainerInfo.KeyContainerName;
                        cspParameters.ProviderType = cert_key.CspKeyContainerInfo.ProviderType;
                        cspParameters.ProviderName = cert_key.CspKeyContainerInfo.ProviderName;
                        cspParameters.Flags = cert_key.CspKeyContainerInfo.MachineKeyStore
                                                              ? (CspProviderFlags.UseExistingKey | CspProviderFlags.UseMachineKeyStore)
                                                              : (CspProviderFlags.UseExistingKey);
                    } // закрываем контейнер
                } // закрываем сертификат
            }


            using (var keyToUse = new Gost3410_2012_256CryptoServiceProvider(cspParameters))
            {
                // Так как контейнер закрыт - открываем его и запрашиваем пин-код
                var sig1 = keyToUse.SignData(new byte[] { 0, 0, 0, 0 }, new Gost3411_2012_256CryptoServiceProvider());

                // конетейнер открыт, пин не запросится
                var sig2 = keyToUse.SignData(new byte[] { 0, 0, 0, 0 }, new Gost3411_2012_256CryptoServiceProvider());
            } // закрываем контейнер
            using (var keyToUse1 = new Gost3410_2012_256CryptoServiceProvider(cspParameters))
            {
                // Так как контейнер закрыт - открываем его и запрашиваем пин-код
                var sig1 = keyToUse1.SignData(new byte[] { 0, 0, 0, 0 }, new Gost3411_2012_256CryptoServiceProvider());
            }

Отредактировано пользователем 19 ноября 2020 г. 13:59:27(UTC)  | Причина: Не указана

Техническую поддержку оказываем тут
Наша база знаний
Offline benchstyle  
#7 Оставлено : 19 ноября 2020 г. 14:11:04(UTC)
benchstyle

Статус: Участник

Группы: Участники
Зарегистрирован: 26.04.2020(UTC)
Сообщений: 10
Откуда: Муром

Сказал(а) «Спасибо»: 1 раз
пробовал Dispose'ить контейнер до того как обратился на форум и естественно как вы сказали ловил ошибку.
Попробую ваш крайний вариант, спасибо.
RSS Лента  Atom Лента
Пользователи, просматривающие эту тему
Быстрый переход  
Вы не можете создавать новые темы в этом форуме.
Вы не можете отвечать в этом форуме.
Вы не можете удалять Ваши сообщения в этом форуме.
Вы не можете редактировать Ваши сообщения в этом форуме.
Вы не можете создавать опросы в этом форуме.
Вы не можете голосовать в этом форуме.