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

Уведомление

Icon
Error

Опции
К последнему сообщению К первому непрочитанному
Offline dmitry_kudin  
#1 Оставлено : 19 октября 2018 г. 16:17:48(UTC)
dmitry_kudin

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

Группы: Участники
Зарегистрирован: 19.10.2018(UTC)
Сообщений: 1
Российская Федерация
Откуда: Санкт-Петербург

Сказал(а) «Спасибо»: 1 раз
Здравствуйте!
На компьютер установлена КриптоПро CSP 4.0 КС2.
Занимаюсь разработкой утилиты для экспорта сертификатов. До этого поддерживались только сертификаты с алгоритмом подписи ГОСТ Р 34.10-2001. Сейчас реализую поддержку сертификатов с алгоритмом подписи ГОСТ Р 34.10-2012.
Есть следующий метод, который экспортирует сертификат и сохраняет в файл (WinCapi - класс, в который импортированы методы из библиотек crypt32.dll, advapi32.dll):
Код:

static uint PROV_GOST_2001_DH = 75;
static uint PROV_GOST_2012_256 = 80;
static uint PROV_GOST_2012_512 = 81;

static uint CRYPT_SILENT = 0x00000040;
static int CRYPT_EXPORTABLE = 0x00000001;

static uint CALG_GR3411 = 32798;
static uint CALG_GR3411_2012_256 = 32801;
static uint CALG_GR3411_2012_512 = 32802;

static uint PP_KEYEXCHANGE_PIN = 32;
static uint AT_KEYEXCHANGE = 1;

static uint CALG_G28147 = 26142;
static uint CALG_PRO_EXPORT = 26143;

static int KP_ALGID = 7;
static uint KP_CERTIFICATE = 26
static int PRIVATEKEYBLOB = 0x7;

public static bool exportKey(String containerName, String filePath, String transitPass, String containerPass, String certFilePath, ContainerTypeEnum type)
        {
            IntPtr hProvResponder = IntPtr.Zero;
            IntPtr hResponderKey = IntPtr.Zero;
            IntPtr hash = IntPtr.Zero;
            IntPtr sessionKey = IntPtr.Zero;
            int keyBufLen = 0;
            uint calg;

            bool result;
            switch (type)
            {
                case ContainerTypeEnum.GOST_2001:
                    result = WinCapi.CryptAcquireContext(ref hProvResponder, containerName, null, PROV_GOST_2001_DH, CRYPT_SILENT);
                    calg = CALG_GR3411;
                    break;
                case ContainerTypeEnum.GOST_2012_256:
                    result = WinCapi.CryptAcquireContext(ref hProvResponder, containerName, null, PROV_GOST_2012_256, CRYPT_SILENT);
                    calg = CALG_GR3411_2012_256;
                    break;
                case ContainerTypeEnum.GOST_2012_512:
                    result = WinCapi.CryptAcquireContext(ref hProvResponder, containerName, null, PROV_GOST_2012_512, CRYPT_SILENT);
                    calg = CALG_GR3411_2012_512;
                    break;
                default:
                    result = false;
                    calg = 0;
                    break;
            }

            if (!result) 
            {
                HandleError("не удалось найти или подсоединиться к контейнеру");
                return false;
            }

            if (!WinCapi.CryptSetProvParam(hProvResponder, PP_KEYEXCHANGE_PIN, Utilities.StrToByteArray(containerPass), 0)) 
            {
                WinCapi.CryptReleaseContext(hProvResponder, 0);
                return false;
            }
            
            if (!WinCapi.CryptGetUserKey(hProvResponder, AT_KEYEXCHANGE, ref hResponderKey))
            {
                HandleError("Error during CryptGetUserKey public key.");
                WinCapi.CryptReleaseContext(hProvResponder, 0);
                return false;
            }

            if (!WinCapi.CryptCreateHash(hProvResponder, calg, IntPtr.Zero, 0, ref hash))
            {
                WinCapi.CryptReleaseContext(hProvResponder, 0);
                WinCapi.CryptDestroyKey(hResponderKey);
                HandleError("hach not created");
                return false;
            }
            if (!WinCapi.CryptHashData(hash, Utilities.StrToByteArray(transitPass), transitPass.Length, 0))
            {
                HandleError("data not hashed");
                WinCapi.CryptDestroyHash(hash);
                WinCapi.CryptReleaseContext(hProvResponder, 0);
                WinCapi.CryptDestroyKey(hResponderKey);
                return false;
            }
                      
            if (!WinCapi.CryptDeriveKey(hProvResponder, CALG_G28147, hash, CRYPT_EXPORTABLE, ref sessionKey))
            {
                HandleError("derive key fail");
                WinCapi.CryptDestroyHash(hash);
                WinCapi.CryptReleaseContext(hProvResponder, 0);
                WinCapi.CryptDestroyKey(hResponderKey);
                return false;
            }
            
            uint algid_export = CALG_PRO_EXPORT;
            if (WinCapi.CryptSetKeyParam(sessionKey, KP_ALGID, BitConverter.GetBytes(algid_export), 0))
            {
                //Console.WriteLine("ключ экспорта настроен");
            }
            else
            {
                HandleError("ошибка при установке ключа экспорта");
                WinCapi.CryptDestroyKey(sessionKey);
                WinCapi.CryptDestroyHash(hash);
                WinCapi.CryptReleaseContext(hProvResponder, 0);
                WinCapi.CryptDestroyKey(hResponderKey);
                return false;
            }

            if (!WinCapi.CryptExportKey(hResponderKey, sessionKey, PRIVATEKEYBLOB, 0, null, ref keyBufLen))
            {
                HandleError("ошибка при получении размера закрытого ключа");
                WinCapi.CryptDestroyKey(sessionKey);
                WinCapi.CryptDestroyHash(hash);
                WinCapi.CryptReleaseContext(hProvResponder, 0);
                WinCapi.CryptDestroyKey(hResponderKey);
                return false;
            }
            Byte[] keyBuff = new Byte[keyBufLen];
            if (WinCapi.CryptExportKey(hResponderKey, sessionKey, PRIVATEKEYBLOB, 0, keyBuff, ref keyBufLen))
            {
                //Console.WriteLine("ключ экспортирован");
            }
            else
            {
                HandleError("ошибка при экспорте закрытого ключа 1");
                WinCapi.CryptDestroyKey(sessionKey);
                WinCapi.CryptDestroyHash(hash);
                WinCapi.CryptReleaseContext(hProvResponder, 0);
                WinCapi.CryptDestroyKey(hResponderKey);
                return false;
            }

            bool res = true;
            //экспорт сертификата
            uint certLen = 0;
            Byte[] certData = null;
            if (WinCapi.CryptGetKeyParam(hResponderKey, KP_CERTIFICATE, null, ref certLen, 0))
            {
                certData = new Byte[certLen];
                if (WinCapi.CryptGetKeyParam(hResponderKey, KP_CERTIFICATE, certData, ref certLen, 0))
                {
                    File.WriteAllBytes(certFilePath, certData);
                }
            }
            else
            {
                HandleError("не удалось получить сертификат, привязанный к контейнеру");
                res = false;
            }
            File.WriteAllBytes(filePath, keyBuff);
            //temp          
                  
            return res;
        }

Проблема возникает в вызове метода CryptExportKey, возвращается ошибка 2146893813 "Ключ не может быть использован в указанном состоянии". Поиск по форуму показал, что это связано с разрешением на экспорт ключа. Разрешение проверил в CSP->Сервис->Протестировать, результат ниже:

Проверка завершена успешно ошибок не обнаружено
Контейнер закрытого ключа пользователя
имя c6a48e71-12d3-460d-ad2d-733d614b32b9
уникальное имя REGISTRY\\c6a48e71-12d3-460d-ad2d-733d614b32b9
FQCN \\.\REGISTRY\c6a48e71-12d3-460d-ad2d-733d614b32b9
проверка целостности контейнера успешно
Ключ обмена доступен
длина ключа 512 бит
экспорт открытого ключа успешно
вычисление открытого ключа успешно
импорт открытого ключа успешно
подпись успешно
проверка успешно
создание ключа обмена успешно
экспорт ключа разрешен
алгоритм ГОСТ Р 34.10-2012 DH 256 бит
ГОСТ Р 34.10-2001, параметры обмена по умолчанию
ГОСТ Р 34.11-2012 256 бит
ГОСТ 28147-89, параметры шифрования ТК26 Z
сертификат в контейнере отсутствует
Ключ подписи отсутствует
загрузка ключей успешно
Версия контейнера 2

Как можно решить эту проблему? Допускаю, что для поддержки сертификатов с ГОСТ Р 34.10-2012 требуется еще как то изменить код, но информации об этом не нашел. Может ли эта ошибка говорить не только о запрете на экспорт?
Offline Максим Коллегин  
#2 Оставлено : 19 октября 2018 г. 23:20:11(UTC)
Максим Коллегин

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

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

Сказал «Спасибо»: 32 раз
Поблагодарили: 706 раз в 614 постах
Попробуйте заменить
uint algid_export = CALG_PRO_EXPORT;
на CALG_PRO12_EXPORT
Знания в базе знаний, поддержка в техподдержке
thanks 1 пользователь поблагодарил Максим Коллегин за этот пост.
dmitry_kudin оставлено 25.10.2018(UTC)
RSS Лента  Atom Лента
Пользователи, просматривающие эту тему
Быстрый переход  
Вы не можете создавать новые темы в этом форуме.
Вы не можете отвечать в этом форуме.
Вы не можете удалять Ваши сообщения в этом форуме.
Вы не можете редактировать Ваши сообщения в этом форуме.
Вы не можете создавать опросы в этом форуме.
Вы не можете голосовать в этом форуме.