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

Уведомление

Icon
Error

Опции
К последнему сообщению К первому непрочитанному
Offline domian12  
#1 Оставлено : 14 марта 2012 г. 1:05:16(UTC)
domian12

Статус: Новичок

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

Доброго времени суток, коллеги.

Для работы с КриптоПро CSP использую Sharpei.
Имеется клиент Web Service'а, отсылающий подписанные ЭЦП запросы на сервер. Клиент в ОС работает как сервис, а значит в нём не должно быть никакого взаимодействия с GUI от КриптоПро CSP.

1) Перед тем как создать ЭЦП необходимо получить доступ к сертификату.
2) Я получаю следующие данные:
- имя контейнера, в котором находится сертификат
- пароль к данному контейнеру
3) По полученным данным получаю доступ к сертификату и дальше как нужно работаю.
Код выглядит так:
Код:

CspParameters cspParameters = BuildCertificateCspParameters()
Gost3410CryptoServiceProvider provider = new Gost3410CryptoServiceProvider(cspParameters);
X509Certificate2 certificate2 = provider.ContainerCertificate;// достаем сертификат из контейнера
...
        private CspParameters BuildCertificateCspParameters()
        {
            var cspParameters = new CspParameters();
            cspParameters.KeyContainerName = _сontainerName; //имя используемого контейнера
            cspParameters.ProviderType = 75;//номер используемого провайдера
            cspParameters.Flags = CspProviderFlags.NoPrompt; //отключение GUI-форм КриптоПро CSP
            cspParameters.KeyPassword = BuildCspParametersPassword();
            return cspParameters;
        }
         ...
        private SecureString BuildCspParametersPassword()
        {
            var secure = new SecureString();
            foreach (char charPass in _сontainerPass)
            {
                secure.AppendChar(charPass);
            }
            return secure;
        }



Проблема в том, что данный код работает (по умолчанию) только если ключевой контейнер КриптоПро установлен для "пользователя" (StoreLocation.CurrentUser). Если контейнер установлен для компьютера (StoreLocation.LocalMachine), то код не работает и падает с ошибкой:
Цитата:
System.Security.Cryptography.CryptographicException: Неправильный параметр набора ключей.
в CryptoPro.Sharpei.CPUtils.CreateProvHandle(CspParameters parameters, Boolean randomKeyContainer)
в CryptoPro.Sharpei.CPUtils.GetKeyPairHelper(CPCspAlgorithmType keyType, CspParameters parameters, Boolean randomKeyContainer, Int32 dwKeySize, SafeProvHandleCP& safeProvHandle, SafeKeyHandleCP& safeKeyHandle)
в CryptoPro.Sharpei.Gost3410CryptoServiceProvider.GetKeyPair()
...



Вопрос: можно ли где-то в настройках явно указать расположение контейнера (StoreLocation.LocalMachine или StoreLocation.CurrentUser). Ничего подходящего для себя пока в CspParameters или в Gost3410CryptoServiceProvider не нашёл.

---
Для решения проблемы использую такой код.
Код:

X509Store store = new X509Store(StoreName.My, StoreLocation.CurrentUser);
store.Open(OpenFlags.OpenExistingOnly | OpenFlags.ReadWrite);
509Certificate2Collection fcollection = (X509Certificate2Collection)store.Certificates;

...<произвожу поиск в fcollection>...

X509Certificate2 certificate = collection[i];
CspKeyContainerInfo info = ((ICspAsymmetricAlgorithm)certificate.PrivateKey).CspKeyContainerInfo;
var provider = new Gost3410CryptoServiceProvider();

Если нужный сертификат не найден, то повторяю поиск уже в StoreLocation.LocalMachine.

Проблемы:
- нужны данные о сертификате, с которым производится работа (то что его будет идентифицировать);
- раньше в коде я не замарачивался о том с каким сертификатом работаю, ибо контейнер КриптоПро CSP содержит всегда только 1 сертификат


PS:
Мне необходимо обязательно задавать перед началом работы с криптопровайдером: CspProviderFlags.NoPrompt;
для того, чтобы не появлялись GUI окна.
Я проверяю полученный пароль на валидность
Код:
            var dummyHash = new byte[32];
            try
            {
                provider.SignHash(dummyHash); //если ошибка - пароль не верный
            }
            catch (Exception)
            {
                throw new Exception("текст");
            }

И если он неверен, то отдаю соответствующую ошибку.

PSS:
1) Нашёл, что можно задать через CspParameters: cspParameters.Flags = CspProviderFlags.UseMachineKeyStore;
Но это не подходит ибо подавление GUI-окон не действует и в случае неправильно введенного пароля в представленном выше коде всплывет окно с предложением повторить ввод пароля. Т.е. по сути мне нужно:
Код:
cspParameters.Flags = CspProviderFlags.UseMachineKeyStore;
и
cspParameters.Flags = CspProviderFlags.NoPrompt;

а это одновременно сейчас невозможно.

2) Нашёл ещё: Gost3410CryptoServiceProvider.UseMachineKeyStore = true;
но судя по описанию кода класса Gost3410CryptoServiceProvider получается:
Код:
    public static bool UseMachineKeyStore
    {
      get
      {
        return Gost3410CryptoServiceProvider.useMachineKeyStore_ == CspProviderFlags.UseMachineKeyStore;
      }
      set
      {
        Gost3410CryptoServiceProvider.useMachineKeyStore_ = value ? CspProviderFlags.UseMachineKeyStore : CspProviderFlags.NoFlags;
      }
    }

что теряет свою суть.

Прошу вашей помощи в решении возникшей проблемы.
Offline Максим Коллегин  
#2 Оставлено : 14 марта 2012 г. 12:33:56(UTC)
Максим Коллегин

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

Группы: Администраторы
Зарегистрирован: 12.12.2007(UTC)
Сообщений: 6,405
Мужчина
Откуда: КРИПТО-ПРО

Сказал «Спасибо»: 37 раз
Поблагодарили: 720 раз в 624 постах
Я правильно понял: не получается сделать так:
Код:
cspParameters.Flags = CspProviderFlags.NoPrompt | CspProviderFlags.UseMachineKeyStore
?
Знания в базе знаний, поддержка в центре поддержки
Offline domian12  
#3 Оставлено : 14 марта 2012 г. 14:43:27(UTC)
domian12

Статус: Новичок

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

Такой вариант я не пробовал. Он работает, спасибо!
Offline domian12  
#4 Оставлено : 14 марта 2012 г. 17:57:39(UTC)
domian12

Статус: Новичок

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

Сделал как вы сказали - сперва в cspParameters.Flags записываю (CspProviderFlags.NoPrompt), если провайдер не инициализируется (не найден контейнер), то переключаюсь на новые настройки с значением (CspProviderFlags.NoPrompt | CspProviderFlags.UseMachineKeyStore).

На тестовой машине (Windows Server 2003 R2 SP2 EE (RUS) + патчи от MS) обработка проходит. Всё корректно: не найден в CurrentUser, ищет в LocalMachine, не найден во втором -> я выдаю свою ошибку
На боевой машине (Windows Server 2003 R2 SP2 EE (ENG)) - в случае провала первого варианта с (CspProviderFlags.NoPrompt) всплывает окно генерации биологического ключа.

Т.е. получается, что во время инициализации: new Gost3410CryptoServiceProvider(cspParameters);
контейнер не находится и происходит его создание с просьбой генерации закрытого ключа.
Есть какие-то мнения на данный счёт?
Используется: .Net Framework 3.5, КриптоПро Sharpei 1.0.4363.0

Отредактировано пользователем 14 марта 2012 г. 18:10:44(UTC)  | Причина: Не указана

Offline Максим Коллегин  
#5 Оставлено : 14 марта 2012 г. 19:09:53(UTC)
Максим Коллегин

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

Группы: Администраторы
Зарегистрирован: 12.12.2007(UTC)
Сообщений: 6,405
Мужчина
Откуда: КРИПТО-ПРО

Сказал «Спасибо»: 37 раз
Поблагодарили: 720 раз в 624 постах
Версии CSP на машинах одинаковые?
Приведите пример открытия контейнера целиком.
Добавьте флаг
Код:
CspProviderFlags.UseExistingKey

Отредактировано пользователем 14 марта 2012 г. 19:12:58(UTC)  | Причина: Не указана

Знания в базе знаний, поддержка в центре поддержки
Offline domian12  
#6 Оставлено : 14 марта 2012 г. 20:22:48(UTC)
domian12

Статус: Новичок

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

maxdm написал:
Версии CSP на машинах одинаковые?

Да. КриптоПро CSP 3.6 R2.
maxdm написал:
Приведите пример открытия контейнера целиком.

Целиком много. Ключевые моменты работы:
Код:
        
         internal X509Certificate2 GetCertificate()
        {
            //true - MachineKeyStore; false - UserKeyStore
            _provider = BuildCryptoProviderByKeyStore(false) ?? BuildCryptoProviderByKeyStore(true);

            if (_provider == null)
            {
                throw new Exception("... текст ...");
            }
            CheckCertificatePassword(_provider);
            return GetX509Certificate2(_provider);
        }
         ...

        private Gost3410CryptoServiceProvider BuildCryptoProviderByKeyStore(bool isMachineKeyStore)
        {
            CspParameters cspParameters = BuildCertificateCspParameters(isMachineKeyStore); // открываем контейнер.
            try
            {
                _provider = new Gost3410CryptoServiceProvider(cspParameters);
                return _provider;
            }
            catch (Exception)
            {
                return null; //крипто-провайдер не создан
            }
        }
         ...

        //конфигурация для Gost3410CryptoServiceProvider
        private CspParameters BuildCertificateCspParameters(bool isMachineKeyStore)
        {
            var cspParameters = new CspParameters();
            cspParameters.KeyContainerName = _сontainerName; //имя используемого контейнера
            cspParameters.ProviderType = 75; //номер Crypto-Pro GOST R 34.10-2001 Cryptographic Service Provider
            cspParameters.Flags = isMachineKeyStore
                                      ? (CspProviderFlags.NoPrompt|CspProviderFlags.UseExistingKey|CspProviderFlags.UseMachineKeyStore)
                                      : (CspProviderFlags.NoPrompt|CspProviderFlags.UseExistingKey);
            cspParameters.KeyPassword = BuildCspParametersPassword();
            return cspParameters;
        }
         ...

        //присвоение пароля
        private SecureString BuildCspParametersPassword()
        {
            var secure = new SecureString();
            foreach (char charPass in _сontainerPass)
            {
                secure.AppendChar(charPass);
            }
            return secure;
        }
         ...

        //проверка пароля контейнера
        private static void CheckCertificatePassword(Gost3410CryptoServiceProvider provider)
        {
            var dummyHash = new byte[32];
            try
            {
                provider.SignHash(dummyHash); //если ошибка - пароль не верный
            }
            catch (Exception)
            {
                throw new Exception("... текст ...");
            }
        }
         ...

        // достаем сертификат из контейнера
        private static X509Certificate2 GetX509Certificate2(Gost3410CryptoServiceProvider provider)
        {
            X509Certificate2 certificate2 = provider.ContainerCertificate;
            if (certificate2 == null)
            {
                throw new Exception("... текст ...");
            }
            return certificate2;
        }



maxdm написал:
Добавьте флаг
Код:
CspProviderFlags.UseExistingKey

Получилось. Окно на боевой машине с генерацией биологического ключа не появляется. Работа на наличие/отсутствие контейнера в том или ином хранилище проходит удачно.
Интересно почему на тестовой машине это не проявлялось.
RSS Лента  Atom Лента
Пользователи, просматривающие эту тему
Guest
Быстрый переход  
Вы не можете создавать новые темы в этом форуме.
Вы не можете отвечать в этом форуме.
Вы не можете удалять Ваши сообщения в этом форуме.
Вы не можете редактировать Ваши сообщения в этом форуме.
Вы не можете создавать опросы в этом форуме.
Вы не можете голосовать в этом форуме.