logo Обзор КриптоПро NGate для защищённого доступа к корпоративным ресурсам
Добро пожаловать, Гость! Чтобы использовать все возможности Вход или Регистрация.

Уведомление

Icon
Error

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

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

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

Добрый день!

Проект на C#. Использую pinvoke. Привожу код, в котором CryptSignHash всегда false. Прошу подсказать в чем проблема.

Цитата:

// Открываем хранилище сертификатов
var hStoreHandle = CryptoAPI.CertOpenStore(CryptoAPI.CERT_STORE_PROV_SYSTEM, 0, (IntPtr)0, CryptoAPI.CERT_SYSTEM_STORE_CURRENT_USER, "MY");
if (hStoreHandle != null)
{
Console.WriteLine($"Хендл хранилища сертификатов: {hStoreHandle.ToString()}");
}
else
{
var er = CryptoAPI.GetLastError();
Console.WriteLine(er.ToString());
}

//получаем указатель на сертификат
var pSignerCert = CryptoAPI.CertFindCertificateInStore(hStoreHandle, CryptoAPI.MY_TYPE, 0, CryptoAPI.CERT_FIND_SUBJECT_STR, null, (IntPtr)0);
if (pSignerCert != null)
{

Console.WriteLine($"Хендл сертификата: {pSignerCert}");
}
else
{
var er = CryptoAPI.GetLastError();
Console.WriteLine(er.ToString());
}

// через функцию CryptAcquireCertificatePrivateKey получаем доступ к CSP
IntPtr phCryptProv = (IntPtr)0;
Int32 pdwKeySpec = 0;
bool pfCallerFreeProv = false;
if (CryptoAPI.CryptAcquireCertificatePrivateKey(pSignerCert, 0, IntPtr.Zero, ref phCryptProv, ref pdwKeySpec, ref pfCallerFreeProv))
{
Console.WriteLine($"Дескриптор: {phCryptProv.ToString()}");
}
else
{
var er = CryptoAPI.GetLastError();
Console.WriteLine(er.ToString());
}
// сообщение
string massage = "тут у нас исходный текст подписанного сообщения";
byte[] pbMessage = System.Text.Encoding.UTF8.GetBytes(massage);
Int32 cbMessage = pbMessage.Length;
// base64 подписанного сообщения
string massage_sign = "MIICOQYJKoZIhvcNAQcCoIICKjCCAiYCAQExDDAKBgYqhQMCAgkFADALBgkqhkiG9w0BBwExggIEMIICAAIBATBzMGUxIDAeBgkqhkiG9w0BCQEWEWluZm9AY3J5cHRvcHJvLnJ1MQswCQYDVQQGEwJSVTETMBEGA1UEChMKQ1JZUFRPLVBSTzEfMB0GA1UEAxMWVGVzdCBDZW50ZXIgQ1JZUFRPLVBSTwIKZTIq5AACAAQ+1DAKBgYqhQMCAgkFAKCCASowGAYJKoZIhvcNAQkDMQsGCSqGSIb3DQEHATAcBgkqhkiG9w0BCQUxDxcNMTMwNTIxMDY0NTQ4WjAvBgkqhkiG9w0BCQQxIgQgjw/LhkSZj8f2gna5q4fu4mAPjGwX7jUSfjYbO8Sm4Dwwgb4GCyqGSIb3DQEJEAIvMYGuMIGrMIGoMIGlMAgGBiqFAwICCQQgoWW4uwCURIWOSFtKX308YR7jXyGHH/1y2MDECzxn7FIwdzBppGcwZTEgMB4GCSqGSIb3DQEJARYRaW5mb0BjcnlwdG9wcm8ucnUxCzAJBgNVBAYTAlJVMRMwEQYDVQQKEwpDUllQVE8tUFJPMR8wHQYDVQQDExZUZXN0IENlbnRlciBDUllQVE8tUFJPAgplMirkAAIABD7UMAoGBiqFAwICEwUABECTQoUA/SuSEEh7h+KudbAQKKMmrzvXDp62N79u42WcvxYqB272qg5SIz7Y+OZ1mcQSQeDCYX3aAhKJBtrL9qYX";
byte[] pbArray = Convert.FromBase64String(massage_sign);
Int32 cbArray = pbArray.Length;

//создаем пустой hash объект
IntPtr handle = IntPtr.Zero;
if (CryptoAPI.CryptCreateHash(phCryptProv, 32798 /*CALG_GR3411*/, IntPtr.Zero, 0, ref handle))
{
Console.WriteLine($"Пустой hash: {handle.ToString()}");
}
else
{
var er = CryptoAPI.GetLastError();
Console.WriteLine(er.ToString());
}
// Вычисляем hash для нашего сообщения
if (CryptoAPI.CryptHashData(handle, pbMessage, (int)pbMessage.Length, 0))
{
int i = 0;
foreach (var el in pbMessage)
{
Console.WriteLine($"Hash c данными: {i} - {el.ToString()}");
i++;
}
}
else
{
var er = CryptoAPI.GetLastError();
Console.WriteLine(er.ToString());
}
// Переменные для указателя и длины подписи
uint pdwSigLen = 0;
byte[] pbSignature = new byte[pdwSigLen];

if (CryptoAPI.CryptSignHash(handle, CryptoAPI.AT_KEYEXCHANGE, IntPtr.Zero, 0, ref pbSignature, ref pdwSigLen))
{
Console.WriteLine($"Длина подписи: {pdwSigLen}");
}
else
{
Console.WriteLine(CryptoAPI.GetLastError().ToString());
}


Уж рядил и так и сяк, а CryptSignHash всегда false. В чем дело?
Offline Андрей Писарев  
#2 Оставлено : 16 июля 2019 г. 19:50:35(UTC)
Андрей Писарев

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

Группы: Участники
Зарегистрирован: 26.07.2011(UTC)
Сообщений: 8,349
Мужчина

Сказал «Спасибо»: 292 раз
Поблагодарили: 1172 раз в 922 постах
Здравствуйте.

GetLastError молчит?
Техническую поддержку оказываем тут
Наша база знаний
Offline Андрей Писарев  
#3 Оставлено : 16 июля 2019 г. 20:02:10(UTC)
Андрей Писарев

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

Группы: Участники
Зарегистрирован: 26.07.2011(UTC)
Сообщений: 8,349
Мужчина

Сказал «Спасибо»: 292 раз
Поблагодарили: 1172 раз в 922 постах
pSignerCert и 32798 /*CALG_GR3411*/

соответствуют действительности или сертификат с ГОСТ-2012?

CryptoAPI.AT_KEYEXCHANGE - а если его не будет? Необходимо либо требовать такое условие работы кода, либо в коде обращаться и к ключу подписи, если ключа обмена нет.
Техническую поддержку оказываем тут
Наша база знаний
Offline AANNTTOONN  
#4 Оставлено : 16 июля 2019 г. 20:02:41(UTC)
AANNTTOONN

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

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

По сути молчит. Может я неверно настроил маршаллинг, но GetLastError выдает ахинею типа "0" - вот мое исполнение:
[DllImport("kernel32.dll", EntryPoint = "GetLastError")]
public static extern uint GetLastError();
Offline AANNTTOONN  
#5 Оставлено : 16 июля 2019 г. 20:10:40(UTC)
AANNTTOONN

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

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

1) Тестовый сертификат: CN=CRYPTO-PRO Test Center 2, O=CRYPTO-PRO LLC, L=Moscow, C=RU, E=support@cryptopro.ru
2) CryptoAPI.CryptSignHash(handle, 0, IntPtr.Zero, 0, ref pbSignature, ref pdwSigLen) - если вместо CryptoAPI.AT_KEYEXCHANGE - 0, или CryptoAPI.AT_SIGNATURE - ничего не меняется: false
Offline Андрей Писарев  
#6 Оставлено : 16 июля 2019 г. 21:05:43(UTC)
Андрей Писарев

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

Группы: Участники
Зарегистрирован: 26.07.2011(UTC)
Сообщений: 8,349
Мужчина

Сказал «Спасибо»: 292 раз
Поблагодарили: 1172 раз в 922 постах
Автор: AANNTTOONN Перейти к цитате
1) Тестовый сертификат: CN=CRYPTO-PRO Test Center 2, O=CRYPTO-PRO LLC, L=Moscow, C=RU, E=support@cryptopro.ru
2) CryptoAPI.CryptSignHash(handle, 0, IntPtr.Zero, 0, ref pbSignature, ref pdwSigLen) - если вместо CryptoAPI.AT_KEYEXCHANGE - 0, или CryptoAPI.AT_SIGNATURE - ничего не меняется: false


КриптоПРО CSP\Сервис\Протестировать\По сертификату

Алгоритм у сертификата ГОСТ-2001?
Ошибок нет?

Если всё нормально - смотрим внимательно код и описание функций.
Техническую поддержку оказываем тут
Наша база знаний
Offline Андрей Писарев  
#7 Оставлено : 16 июля 2019 г. 21:06:28(UTC)
Андрей Писарев

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

Группы: Участники
Зарегистрирован: 26.07.2011(UTC)
Сообщений: 8,349
Мужчина

Сказал «Спасибо»: 292 раз
Поблагодарили: 1172 раз в 922 постах
Автор: AANNTTOONN Перейти к цитате
1) Тестовый сертификат: CN=CRYPTO-PRO Test Center 2, O=CRYPTO-PRO LLC, L=Moscow, C=RU, E=support@cryptopro.ru


Это сертфикат тестового УЦ.
Где клиентский сертификат?

Техническую поддержку оказываем тут
Наша база знаний
Offline two_oceans  
#8 Оставлено : 17 июля 2019 г. 5:59:07(UTC)
two_oceans

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

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

Сказал(а) «Спасибо»: 42 раз
Поблагодарили: 147 раз в 140 постах
Автор: AANNTTOONN Перейти к цитате
2) CryptoAPI.CryptSignHash(handle, 0, IntPtr.Zero, 0, ref pbSignature, ref pdwSigLen) - если вместо CryptoAPI.AT_KEYEXCHANGE - 0, или CryptoAPI.AT_SIGNATURE - ничего не меняется: false
Прочитайте пожалуйста справку внимательно, в Вашем коде уже получено функцией CryptAcquireCertificatePrivateKey значение pdwKeySpec связанное с ключом выбранного сертификата. Его и нужно передать в CryptSignHash вместо жестко прописанного AT_KEYEXCHANGE или AT_SIGNATURE.

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

Подбирать может потребоваться только если сначала получали имя контейнера для сертификата (тут dwKeySpec насколько помню не возвращается), затем получали контейнер по имени. При переборе желательно сверить открытый ключ из контейнера и из сертификата на соответствие. В Вашем случае (получение контейнера через CryptAcquireCertificatePrivateKey) перебор не нужен, dwKeySpec известно, открытый ключ сверяется внутри функции CryptAcquireCertificatePrivateKey при указании соответствующего флага.

Автор: AANNTTOONN Перейти к цитате
GetLastError выдает ахинею типа "0"
По ошибке: для получения длины нужно передать вместо pbSignature пустой указатель IntPtr.Zero (то есть null), иначе функция CryptSignHash будет трактовать длину как уже указанную Вами, проверять указатель на буфер через IsBadWritePtr и споткнется на значении длины 0. Не то чтобы я смотрел код самой функции, но это базовая логика во всех WinApi функциях связанных с получением значений/длины. Передаете вместо буфера null в случае успеха записывается требуемая длина буфер, передаете буфер и длину буфера - записывается использованная длина буфера.

Предположительно при этом внутренний вызов IsBadWritePtr с нулевой длиной буфера перезапишет код ошибки нулем. Заметьте: если Вы запросите длину, а потом подпишите, то у пользователя дважды запросит пин-код на контейнер (один раз для запроса длины, второй раз для собственно подписи).

Либо ставьте предопределенную для алгоритма длину сразу (без дополнительного запроса длины), 130 байтов достаточно для гост-2001 и гост-2012 256 бит - тогда пользователя не будет дважды спрашивать пин-код. В моей программе используется такое решение, потому что немного странно запрашивать пин-код чтобы узнать длину фиксированную для алгоритмов гост.

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

Offline AANNTTOONN  
#9 Оставлено : 17 июля 2019 г. 22:42:20(UTC)
AANNTTOONN

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

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

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