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

Уведомление

Icon
Error

Опции
К последнему сообщению К первому непрочитанному
Offline EgorOkhotin  
#1 Оставлено : 17 июня 2019 г. 10:44:12(UTC)
EgorOkhotin

Статус: Активный участник

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

Сказал(а) «Спасибо»: 4 раз
Параметры системы:
Linux 3.10.0-957.12.2.el7.x86_64
CentOS Linux Release 7.6.1810
CryptoPro установлена со стандартной лецензией на 3 месяца. Отдельно не активировалась.

Пишу код на .NET Core 2.1. Все вызовы делаю через P/Invoke. На Windows вызовы работают.

Проблема:
Вызываю в Linux из библиотеки "libcapi20" функцию CryptAcquireContext, с аналогичными параметрами как и в windows.

var provider = IntPtr.Zero;
var name = "TEST";
var PROV_GOST_2012_256 = 80U;
var CRYPT_NEWKEYSET = 8U;
var CP_GR3410_2012_PROV_A "Crypto-Pro GOST R 34.10-2012 Cryptographic Service Provider";
var szOID_CP_GOST_R3410_12_256 = "1.2.643.7.1.1.1.1";
var result = CryptAcquireContext(provider, name, null, PROV_GOST_2012_256, CRYPT_NEWKEYSET);


Пробовал менять точку входа на CryptAcquireContextA и CryptAcquireContextW (из документации CAPLite) но это не работает. Меняется после вызова только код ошибки. Код ошибки меняттся либо на 0 либо на -1;
Результат всегда false.

Менял флаги на 0, пробовал заналивать имя, но ничего не работает. (Я в одном примере работы с CryptAcquireContext(для создания нового контейнера) прочитал что второй параметр при первых вызовах присваивается null.)

Пробовал подставлять вместо null CP_GR3410_2012_PROV_A а также szOID_CP_GOST_R3410_12_256.

Прошу подсказать что я делаю не так?

Отредактировано пользователем 28 июня 2019 г. 8:50:56(UTC)  | Причина: Не указана

Offline Aleksandr G*  
#2 Оставлено : 17 июня 2019 г. 11:06:25(UTC)
Aleksandr G*

Статус: Активный участник

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

Сказал(а) «Спасибо»: 8 раз
Поблагодарили: 35 раз в 28 постах
Можно попробовать имя передать в формате: "\\.\HDIMAGE\TEST"
thanks 1 пользователь поблагодарил Aleksandr G* за этот пост.
EgorOkhotin оставлено 17.06.2019(UTC)
Offline migel  
#3 Оставлено : 17 июня 2019 г. 11:25:33(UTC)
migel

Статус: Активный участник

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

Сказал(а) «Спасибо»: 3 раз
Поблагодарили: 5 раз в 5 постах
Автор: EgorOkhotin Перейти к цитате
Параметры системы:
....
Прошу подсказать что я делаю не так?

Насколько я помню, в линуксе лучше использовать *W функции при условии что нужно передавать
UTF32 строки (в винде UTF16) стандартный маршаллер не в курсе и пытается строки передавать как в винде
Поэтому используем самописный маршалинг строк
Код:

   public static class EncodingUtils
    {
        internal static IntPtr StringToPtrUni(string value)
        {
            if (value == null)
            {
                return IntPtr.Zero;
            }

            if (Environment.OSVersion.Platform == PlatformID.Unix)
            {
                return StringToPtrUTF32(value);
            }

            return Marshal.StringToHGlobalUni(value);
        }

        private static IntPtr StringToPtrUTF32(string value)
        {
            var enc = Encoding.UTF32;
            var utf32Bytes = new byte[enc.GetByteCount(value) + 4];
            enc.GetBytes(value, 0, value.Length, utf32Bytes, 0);
            var ptr = Marshal.AllocHGlobal(utf32Bytes.Length);
            Marshal.Copy(utf32Bytes, 0, ptr, utf32Bytes.Length);
            return ptr;
        }
    }

    internal class UnmanageMemoryHolder : IDisposable
    {
        private List<IntPtr> _globalAllocations = new List<IntPtr>();
        public void Dispose()
        {
            foreach (var globalAllocation in _globalAllocations)
            {
                Marshal.FreeHGlobal(globalAllocation);
            }
           _globalAllocations.Clear();
        }

        public IntPtr GetUniStringPtr(string strValue)
        {
            IntPtr ptr = EncodingUtils.StringToPtrUni(strValue);
            _globalAllocations.Add(ptr);
            return ptr;
        }
    }


Используем как то так
Код:

        [DllImport(s_Advapi32Dll, BestFitMapping = false, CharSet = CharSet.Auto, SetLastError = true,
                ThrowOnUnmappableChar = true, EntryPoint = "CryptAcquireContextW")]
        [return: MarshalAs(UnmanagedType.Bool)]
        private static extern bool CryptAcquireContext([In] [Out] ref SafeCryptProvHandle hProv,
                                                        [In] IntPtr pszContainer,
                                                        [In] IntPtr pszProvider,
                                                        [In] int dwProvType,
                                                        [In] [MarshalAs(UnmanagedType.U4)] AcqureContextFlags dwFlags);
        internal static bool AcquireContext([In] [Out] ref SafeCryptProvHandle hProv,
                                                        string pszContainer,
                                                        string pszProvider,
                                                        int dwProvType,
                                                        AcqureContextFlags dwFlags)
        {
            var containerPtr = IntPtr.Zero;
            var provPtr = IntPtr.Zero;
            using (var holder = new UnmanageMemoryHolder())
            {
                containerPtr = pszContainer == null 
                            ? IntPtr.Zero
                            : holder.GetUniStringPtr(pszContainer);
                provPtr = pszProvider == null 
                            ? IntPtr.Zero
                            : holder.GetUniStringPtr(pszProvider);
                bool ret = CryptAcquireContext(ref hProv, containerPtr, provPtr, dwProvType, dwFlags);
                if (ret)
                    return ret;

                int hr = Crypt32.GetLastError();
                if (hr == CryptErrors.ERROR_NO_UNICODE_TRANSLATION)
                {
                    containerPtr = pszContainer == null
                                ? IntPtr.Zero
                                : holder.GetAnsiStringPtr(pszContainer);
                    provPtr = pszProvider == null 
                                ? IntPtr.Zero
                                : holder.GetAnsiStringPtr(pszProvider);
                    return CryptAcquireContext(ref hProv, containerPtr, provPtr, dwProvType, dwFlags);
                }
            }
            return false;
        }
    } 
Offline two_oceans  
#4 Оставлено : 17 июня 2019 г. 11:49:31(UTC)
two_oceans

Статус: Эксперт

Группы: Участники
Зарегистрирован: 05.03.2015(UTC)
Сообщений: 1,602
Российская Федерация
Откуда: Иркутская область

Сказал(а) «Спасибо»: 110 раз
Поблагодарили: 394 раз в 366 постах
Я бы наверно для начала создал контекст с флагом CRYPT_VERIFYCONTEXT и перечислил какие провайдеры вообще видны и какие их правильные написания под конкретной ОС. Для линуха обычно в названии фигурирует КС1 или КС2 (чего нет для виндоуз). Перечислил какие контейнеры уже есть, может быть один раз контейнер уже создался и второй раз ошибка из-за совпадения имени с существующим контейнером.

Насчет прописывания 80 лучше бы особо не увлекаться - так как это означает что программа не сможет работать там где криптопро нет, у других компаний другие типы для провайдеров гост-2012.

Что качается A или W то это надо выбрать в зависимости от того какой у Вас проект. Если проект записывает строки в память как Unicode (то есть символ с кодом 0 после каждого латинского символа, двойной 0 для конца строки), то W иначе (0 только в конце строки, не встречается в середине строки) A. Если выбрать неправильно, то или считается только первый символ каждой строки или выйдет неправдоподобный символ Юникода вместо двух символов. Вот уже сообщением выше и свой обработчик порекомендовали.

Отредактировано пользователем 17 июня 2019 г. 11:57:04(UTC)  | Причина: Не указана

Offline EgorOkhotin  
#5 Оставлено : 17 июня 2019 г. 12:56:01(UTC)
EgorOkhotin

Статус: Активный участник

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

Сказал(а) «Спасибо»: 4 раз
Автор: migel Перейти к цитате
Автор: EgorOkhotin Перейти к цитате
Параметры системы:
....
Прошу подсказать что я делаю не так?

Насколько я помню, в линуксе лучше использовать *W функции при условии что нужно передавать
UTF32 строки (в винде UTF16) стандартный маршаллер не в курсе и пытается строки передавать как в винде
Поэтому используем самописный маршалинг строк


Спасибо за ответ, но не работает.
Offline EgorOkhotin  
#6 Оставлено : 17 июня 2019 г. 13:02:18(UTC)
EgorOkhotin

Статус: Активный участник

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

Сказал(а) «Спасибо»: 4 раз
Автор: two_oceans Перейти к цитате
Я бы наверно для начала создал контекст с флагом CRYPT_VERIFYCONTEXT и перечислил какие провайдеры вообще видны и какие их правильные написания под конкретной ОС. Для линуха обычно в названии фигурирует КС1 или КС2 (чего нет для виндоуз). Перечислил какие контейнеры уже есть, может быть один раз контейнер уже создался и второй раз ошибка из-за совпадения имени с существующим контейнером.

Насчет прописывания 80 лучше бы особо не увлекаться - так как это означает что программа не сможет работать там где криптопро нет, у других компаний другие типы для провайдеров гост-2012.

Что качается A или W то это надо выбрать в зависимости от того какой у Вас проект. Если проект записывает строки в память как Unicode (то есть символ с кодом 0 после каждого латинского символа, двойной 0 для конца строки), то W иначе (0 только в конце строки, не встречается в середине строки) A. Если выбрать неправильно, то или считается только первый символ каждой строки или выйдет неправдоподобный символ Юникода вместо двух символов. Вот уже сообщением выше и свой обработчик порекомендовали.


С флагом CRYPT_VERIFYCONTEXT он возвращает true. Но перечисление выполнить не удалось, при попытке обратиться к CryptEnumProviders почему-то просто вылетает с кодо 0 немотря на точки остановки.
Мне надо разобраться первоначально на линукс, а потом добавлю други возможности. А сейчас я мало представляю в чем дело.

Я перечислял провайдеры с помощью утилиты cpcconfig и там 3 провайдера R 34-10. Коды 75, 80, 81
Offline EgorOkhotin  
#7 Оставлено : 17 июня 2019 г. 13:05:51(UTC)
EgorOkhotin

Статус: Активный участник

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

Сказал(а) «Спасибо»: 4 раз
Автор: Aleksandr G* Перейти к цитате
Можно попробовать имя передать в формате: "\\.\HDIMAGE\TEST"


Я не знаю что это за магия но сработало. Я просто не представляю обращение по такому пути в линукс, большое спасибо.
Offline Aleksandr G*  
#8 Оставлено : 17 июня 2019 г. 13:41:17(UTC)
Aleksandr G*

Статус: Активный участник

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

Сказал(а) «Спасибо»: 8 раз
Поблагодарили: 35 раз в 28 постах
Автор: EgorOkhotin Перейти к цитате

Я не знаю что это за магия но сработало. Я просто не представляю обращение по такому пути в линукс, большое спасибо.


Это имя контейнера с путем на жестком диске (хранилище КриптоПро). По идее если указывать короткое, то должно появляться окно с выбором (аналогичное windows, где реестр, диск, токен и т.д.), но не работает в 4 версии, в 5 можно и по короткому (могу ошибаться, лучше протестировать)



Offline cross  
#9 Оставлено : 18 июня 2019 г. 10:44:06(UTC)
Анатолий Беляев

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

Группы: Администраторы, Участники
Зарегистрирован: 24.11.2009(UTC)
Сообщений: 965
Откуда: Crypto-Pro

Сказал(а) «Спасибо»: 3 раз
Поблагодарили: 174 раз в 152 постах
https://github.com/CryptoProLLC/corefx
вот тут можно посмотреть некоторые вызовы. Базовая работа с ключами, хешами, и криптографическими подписями работает на Linux и Mac.
Техническую поддержку оказываем тут.
Наша база знаний.
Наша страничка в Instagram.
RSS Лента  Atom Лента
Пользователи, просматривающие эту тему
Guest
Быстрый переход  
Вы не можете создавать новые темы в этом форуме.
Вы не можете отвечать в этом форуме.
Вы не можете удалять Ваши сообщения в этом форуме.
Вы не можете редактировать Ваши сообщения в этом форуме.
Вы не можете создавать опросы в этом форуме.
Вы не можете голосовать в этом форуме.