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

Уведомление

Icon
Error

2 Страницы12>
Опции
К последнему сообщению К первому непрочитанному
Offline ___Алексей_Васильев___  
#1 Оставлено : 10 июля 2019 г. 17:59:44(UTC)
___Алексей_Васильев___

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

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

Добрый день. Занимаюсь встраиванием метки времени на C# (ответ на запрос к TSP серверу обрабатываю через Bouncy Castle C# из него получаю токен) в уже существующую подпись (полученную через вызов CryptSignMessage) с использованием CryptoApi на данный момент делаю реализацию под платформу Windows.

Поискав информацию на форумах (новом и старом) понял что алгоритм получения подписи должен иметь следующий вид:
1) формируем запрос к tsp серверу, в качестве данных указываем хэш вычисленный от значения подписи;
2) получаем ответ, извлекаем токен, а именно нас интересует TimeStampToken.GetEncoded() массив байт в закодированном виде

Из полученного массива байт извлекаю информацию о подписанте как говорилось в обсуждении https://www.cryptopro.ru...aspx?g=posts&t=11317

var cryptMsgTs = CApiExtWin.CryptMsgOpenToDecode(
PKCS_7_OR_X509_ASN_ENCODING,
0,
0,
IntPtr.Zero,
IntPtr.Zero,
IntPtr.Zero
);

if (!CApiExtWin.CryptMsgUpdate(cryptMsgTs, timestamp, (uint)timestamp.Length, true))
throw new Exception("CryptMsgUpdate");

uint signer_info_len = 0;

if (!CApiExtWin.CryptMsgGetParam(cryptMsgTs, CMSG_SIGNER_INFO_PARAM, 0, null, ref signer_info_len))
{
var CryptMsgGetParamError = Marshal.GetHRForLastWin32Error();
throw new Exception($"CryptMsgGetParam error: {CryptMsgGetParamError}");
}

var pSignerInfoBin = new byte[signer_info_len];

if (!CApiExtWin.CryptMsgGetParam(cryptMsgTs, CMSG_SIGNER_INFO_PARAM, 0, pSignerInfoBin, ref signer_info_len))
{
var CryptMsgGetParamError = Marshal.GetHRForLastWin32Error();
throw new Exception($"CryptMsgGetParam error: {CryptMsgGetParamError}");
}

var pSignerInfoPtr = Marshal.AllocHGlobal(pSignerInfoBin.Length);
Marshal.Copy(pSignerInfoBin, 0, pSignerInfoPtr, pSignerInfoBin.Length);

CApiExtWin.CryptMsgClose(cryptMsgTs);


uint encoded_signer_len = 0;

if (!CApiExtWin.CryptEncodeObject(PKCS_7_OR_X509_ASN_ENCODING, new IntPtr(PKCS7_SIGNER_INFO), pSignerInfoPtr, null, ref encoded_signer_len))
{
var CryptMsgGetParamError = Marshal.GetHRForLastWin32Error();
throw new Exception($"CryptMsgGetParam error: {CryptMsgGetParamError}");
}

// Данные атрибута подписи
var encoded_signer_data = new byte[encoded_signer_len];

if (!CApiExtWin.CryptEncodeObject(PKCS_7_OR_X509_ASN_ENCODING, new IntPtr(PKCS7_SIGNER_INFO), pSignerInfoPtr, encoded_signer_data, ref encoded_signer_len))
{
var CryptMsgGetParamError = Marshal.GetHRForLastWin32Error();
throw new Exception($"CryptMsgGetParam error: {CryptMsgGetParamError}");
}
На этом этапе все отрабатывает корректно. Далее я формирую значение атрибута:

var encoded_signer_data_ptr = Marshal.AllocHGlobal(pSignerInfoBin.Length);
Marshal.Copy(encoded_signer_data, 0, encoded_signer_data_ptr, encoded_signer_data.Length);

// Открываем закодированное сообщение(подпись)
var cryptMsg = CApiExtWin.CryptMsgOpenToDecode(
PKCS_7_OR_X509_ASN_ENCODING,
0,
0,
IntPtr.Zero,
IntPtr.Zero,
IntPtr.Zero
);

if (cryptMsg == IntPtr.Zero)
throw new Exception("Failed to open messsage to decode");

if(!CApiExtWin.CryptMsgUpdate(cryptMsg, sign, (uint)sign.Length, true))
throw new Exception("Failed to add signature block to message");

CRYPT_ATTR_BLOB cryptBlob;
cryptBlob.cbData = (uint)encoded_signer_data.Length; //размер закодированной информации о подписанте;
cryptBlob.pbData = encoded_signer_data_ptr; //закодированная информация о подписанте;

CRYPT_ATTRIBUTE cryptAttribute = new CRYPT_ATTRIBUTE();
cryptAttribute.pszObjId = "1.2.840.113549.1.9.16.1.4"; /*"1.2.840.113549.1.9.16.2.14"*/
cryptAttribute.cValue = 1;
cryptAttribute.rgValue = cryptBlob;

Далее пытаюсь закодировать данный атрибут

uint encoded_len = 0;
if (!CApiExtWin.CryptEncodeObject(PKCS_7_OR_X509_ASN_ENCODING, new IntPtr(PKCS_ATTRIBUTE), cryptAttribute, null, ref encoded_len))
throw new Exception($"Sizing of CryptEncodeObject error. {Marshal.GetHRForLastWin32Error()}");

Тут возникает ошибка. Marshal.GetHRForLastWin32Error() - возвращает HRESULT: 0xC0000005.
Подскажите пожалуйста в чем может быть проблема, или я что то делаю не так?

Описание структур:
[StructLayout(LayoutKind.Sequential)]
public struct CRYPT_ATTRIBUTE
{
[MarshalAs(UnmanagedType.LPStr)]
public string pszObjId;
public uint cValue;
public CRYPT_ATTR_BLOB rgValue;
}

[StructLayout(LayoutKind.Sequential)]
public struct CRYPT_ATTR_BLOB
{
public uint cbData;
public IntPtr pbData;
}

Offline two_oceans  
#2 Оставлено : 11 июля 2019 г. 5:41:22(UTC)
two_oceans

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

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

Сказал(а) «Спасибо»: 110 раз
Поблагодарили: 393 раз в 366 постах
Добрый день. Заранее извинюсь, я на другом языке программирования работаю, поэтому маршаллинг мне кажется подозрительным в целом. Судя по коду ошибки, произошел отказ при обращении к памяти, так что где-то передан неверный указатель. Либо неверная длина из-за которой происходит обращение к невыделенной памяти (например, ставите в блоб длину одной переменной, в указатель другую переменную). Частный случай - отсутствие #0 в конце строки из-за чего также может неверно определиться длина возникнуть обращение к невыделенной памяти (в этом смысле стоит проверить cryptAttribute.pszObjId).

Для ясности можно все потенциальные указатели проверить функциями IsBadReadPtr и IsBadWritePtr, где длина не ясна указать хотя бы 4 (это отсеет неверные указатели и можно будет проверять длины и наличие #0). IsBadReadPtr возвращает TRUE если указатель неверный, FALSE если все в порядке.
Автор: ___Алексей_Васильев___ Перейти к цитате
Код:
                        var encoded_signer_data_ptr = Marshal.AllocHGlobal(pSignerInfoBin.Length);
			Marshal.Copy(encoded_signer_data, 0, encoded_signer_data_ptr, encoded_signer_data.Length);
Вот эти строки мне кажутся немного подозрительными. Маршаллинг похоже не всегда работает правильно. Если уже есть данные, полученные из CryptoAPI, лучше именно их и использовать, чтобы избежать ошибок маршаллинга.
Автор: ___Алексей_Васильев___ Перейти к цитате
Код:
CRYPT_ATTR_BLOB cryptBlob;
В принципе нет необходимости объявлять отдельную переменную, потом ее копировать. Полагаю должно сработать и так (чтобы избежать потенциальных ошибок копирования CRYPT_ATTR_BLOB):
Код:
			cryptAttribute.rgValue.cbData = (uint)encoded_signer_data.Length;
			cryptAttribute.rgValue.pbData = encoded_signer_data_ptr;
Копируя указатели из переменной в переменную не стоит забывать что если где-то освободили память это возможно повлияет на несколько переменных. Вообще тут конечно возможна проблема с длиной: для encoded_signer_data_ptr выделяли насколько я понимаю pSignerInfoBin.Length, а указываете encoded_signer_data.Length. Не проще ли тогда взять указатель на encoded_signer_data и отказаться от encoded_signer_data_ptr?
Автор: ___Алексей_Васильев___ Перейти к цитате
[MarshalAs(UnmanagedType.LPStr)]public string pszObjId;
Немного подозрительная строка в объявлении типа.

Offline ___Алексей_Васильев___  
#3 Оставлено : 11 июля 2019 г. 9:19:22(UTC)
___Алексей_Васильев___

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

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

Автор: two_oceans Перейти к цитате
Добрый день. Заранее извинюсь, я на другом языке программирования работаю, поэтому маршаллинг мне кажется подозрительным в целом. Судя по коду ошибки, произошел отказ при обращении к памяти, так что где-то передан неверный указатель. Либо неверная длина из-за которой происходит обращение к невыделенной памяти (например, ставите в блоб длину одной переменной, в указатель другую переменную). Частный случай - отсутствие #0 в конце строки из-за чего также может неверно определиться длина возникнуть обращение к невыделенной памяти (в этом смысле стоит проверить cryptAttribute.pszObjId).

Для ясности можно все потенциальные указатели проверить функциями IsBadReadPtr и IsBadWritePtr, где длина не ясна указать хотя бы 4 (это отсеет неверные указатели и можно будет проверять длины и наличие #0). IsBadReadPtr возвращает TRUE если указатель неверный, FALSE если все в порядке.
Автор: ___Алексей_Васильев___ Перейти к цитате
Код:
                        var encoded_signer_data_ptr = Marshal.AllocHGlobal(pSignerInfoBin.Length);
			Marshal.Copy(encoded_signer_data, 0, encoded_signer_data_ptr, encoded_signer_data.Length);
Вот эти строки мне кажутся немного подозрительными. Маршаллинг похоже не всегда работает правильно. Если уже есть данные, полученные из CryptoAPI, лучше именно их и использовать, чтобы избежать ошибок маршаллинга.
Автор: ___Алексей_Васильев___ Перейти к цитате
Код:
CRYPT_ATTR_BLOB cryptBlob;
В принципе нет необходимости объявлять отдельную переменную, потом ее копировать. Полагаю должно сработать и так (чтобы избежать потенциальных ошибок копирования CRYPT_ATTR_BLOB):
Код:
			cryptAttribute.rgValue.cbData = (uint)encoded_signer_data.Length;
			cryptAttribute.rgValue.pbData = encoded_signer_data_ptr;
Копируя указатели из переменной в переменную не стоит забывать что если где-то освободили память это возможно повлияет на несколько переменных. Вообще тут конечно возможна проблема с длиной: для encoded_signer_data_ptr выделяли насколько я понимаю pSignerInfoBin.Length, а указываете encoded_signer_data.Length. Не проще ли тогда взять указатель на encoded_signer_data и отказаться от encoded_signer_data_ptr?
Автор: ___Алексей_Васильев___ Перейти к цитате
[MarshalAs(UnmanagedType.LPStr)]public string pszObjId;
Немного подозрительная строка в объявлении типа.



Спасибо за ответ. Убрал из кода все что связанно с выделением памяти через Marshal. В параметры функций передаю массивы как есть. Так же убрал атрибут [MarshalAs(UnmanagedType.LPStr)]. Ситуация не изменилась, ошибка та же...Пробовал менять значение pszObjId, указал пустое значение получил ошибку 0x80070057, указал значение "1" получил ошибку 0x80093109 - это чтобы проверить что функция реагирует на изменение этого параметра.

Может ли проблема быть в том что BouncyCastle.TimeStampToken.GetEncoded() - возвращает массив байт который не может переварить CryptoApi, может быть при кодировании структуры полученной из этого массива байт что то идет не так...и далее из-за этого не получается закодировать атрибут...Планирую сейчас разобраться с добавлением какого-нибудь другого неподписанного атрибута, чтобы отладить этот процесс...В остальном вопрос остается открытым, буду признателен за любую помощь
Offline two_oceans  
#4 Оставлено : 11 июля 2019 г. 10:11:46(UTC)
two_oceans

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

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

Сказал(а) «Спасибо»: 110 раз
Поблагодарили: 393 раз в 366 постах
Автор: ___Алексей_Васильев___ Перейти к цитате
Так же убрал атрибут [MarshalAs(UnmanagedType.LPStr)].
Немного поясню мысль, тут уже надо разбираться с конкретной средой и языком программирования. Смысл моего подозрения был в том, что LPStr это указатель на буфер строки и потому объявлять его строкой нужно только со 100% уверенностью в особенностях компилятора и маршаллинга. Возможно видели что развели на хабре, когда Майкрософт выложили исходники калькулятора из Десятки. На некоторых компилятора Си часть кода была недостижимой потому что условие всегда ложно.
Хотя если реакция есть и стабильно воспроизводится, возможно тут все в порядке (компилятор что-то автоматически исправил).
Цитата:
Может ли проблема быть в том что BouncyCastle.TimeStampToken.GetEncoded() - возвращает массив байт, который не может переварить CryptoApi.
Тут наверно нужен пример массива байт (файлом или шестнадцатиричной записью).

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

Offline ___Алексей_Васильев___  
#5 Оставлено : 11 июля 2019 г. 10:34:13(UTC)
___Алексей_Васильев___

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

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

timestamp_getencoded.txt (4kb) загружен 12 раз(а).
Автор: two_oceans Перейти к цитате
Автор: ___Алексей_Васильев___ Перейти к цитате
Так же убрал атрибут [MarshalAs(UnmanagedType.LPStr)].
Немного поясню мысль, тут уже надо разбираться с конкретной средой и языком программирования. Смысл моего подозрения был в том, что LPStr это указатель на буфер строки и потому объявлять его строкой нужно только со 100% уверенностью в особенностях компилятора и маршаллинга. Возможно видели что развели на хабре, когда Майкрософт выложили исходники калькулятора из Десятки. На некоторых компилятора Си часть кода была недостижимой потому что условие всегда ложно.
Хотя если реакция есть и стабильно воспроизводится, возможно тут все в порядке (компилятор что-то автоматически исправил).
Цитата:
Может ли проблема быть в том что BouncyCastle.TimeStampToken.GetEncoded() - возвращает массив байт, который не может переварить CryptoApi.
Тут наверно нужен пример массива байт (файлом или шестнадцатиричной записью).


Offline two_oceans  
#6 Оставлено : 11 июля 2019 г. 13:12:39(UTC)
two_oceans

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

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

Сказал(а) «Спасибо»: 110 раз
Поблагодарили: 393 раз в 366 постах
Интересно, Asn1 Editor дает ошибку "Длина данных слишком большая".
Offline ___Алексей_Васильев___  
#7 Оставлено : 11 июля 2019 г. 13:29:59(UTC)
___Алексей_Васильев___

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

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

Автор: two_oceans Перейти к цитате
Интересно, Asn1 Editor дает ошибку "Длина данных слишком большая".


Распарсил байты токена в asn1 time_stamp_token_asn1.txt (23kb) загружен 8 раз(а).
Offline ___Алексей_Васильев___  
#8 Оставлено : 11 июля 2019 г. 15:12:57(UTC)
___Алексей_Васильев___

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

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

Еще хотелось бы уточнить, для добавления штампа в подпись нам действительно нужно из токена получить информацию о структуре CMSG_SIGNER_INFO? и пытаться на основе нее (после кодирования CryptEncodeObject) формировать CRYPT_ATTRIBUTE (с pszObjId = "1.2.840.113549.1.9.16.1.4"), единственная информация которую мне удалось найти по данной теме это ссылка из 1 поста. Может кто то объяснить почему так нужно/не нужно делать или хотя бы подсказать где об этом можно почитать
Offline two_oceans  
#9 Оставлено : 12 июля 2019 г. 7:13:29(UTC)
two_oceans

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

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

Сказал(а) «Спасибо»: 110 раз
Поблагодарили: 393 раз в 366 постах
Детально в стандарт я не вчитывался, но идея думаю правдоподобная. Почитать наверно RFC 5126 (cades), RFC 3161 (Time-Stamp Protocol) и https://www.cryptopro.ru/products/pki/tsp/tasks для общей картины.
Offline ___Алексей_Васильев___  
#10 Оставлено : 12 июля 2019 г. 11:24:26(UTC)
___Алексей_Васильев___

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

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

Разобрался с проблемой. Штамп добавляется для этого структуры описал следующим образом:

[StructLayout(LayoutKind.Sequential)]
public struct CRYPT_ATTRIBUTE
{
public string pszObjId;
public uint cValue;
public IntPtr rgValue;
}

[StructLayout(LayoutKind.Sequential)]
public struct CRYPT_ATTR_BLOB
{
public uint cbData;
public IntPtr pbData;
}

Перед кодированием атрибута все оборачиваю в IntPtr

CRYPT_ATTR_BLOB blb = new CRYPT_ATTR_BLOB();
blb.cbData = encoded_signer_len;
blb.pbData = hSignerInfoEncoded;

var blobLocalAllocHandle = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(CRYPT_ATTR_BLOB)));
Marshal.StructureToPtr(blb, blobLocalAllocHandle, false);

var cryptAttribute = new CRYPT_ATTRIBUTE();
cryptAttribute.pszObjId = "1.2.840.113549.1.9.16.1.4";
cryptAttribute.cValue = 1;
cryptAttribute.rgValue = blobLocalAllocHandle;

IntPtr hAttr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(CRYPT_ATTRIBUTE)));
Marshal.StructureToPtr(cryptAttribute, hAttr, false);

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