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

Уведомление

Icon
Error

4 Страницы123>»
Опции
К последнему сообщению К первому непрочитанному
Offline Apofig  
#1 Оставлено : 1 апреля 2013 г. 12:06:10(UTC)
Apofig

Статус: Участник

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

Сказал(а) «Спасибо»: 1 раз
Здравствуйте, возникла следующая проблема:
Есть сторонний сервис, вложения в запросах к которому подписываются подписью в формате PKCS #7 Detached.
Реализованы средства для формирования подписи с использованием криптопровайдеров JCP и CSP.
Подпись, сделанная с помощью JCP корректно воспринимается сервисом.
Подпись же, сделанная с помощью CSP, не проходит проверку, и возвращается ошибка "Файл подписи не соответствует файлу данных".
Пробовал сделать подпись с помощью CryptoPro BrowserPlugin, подпись также не прошла проверку (ошибка была что-то вроде "Неизвестная ошибка при проверке подписи").

Также замечено, что подпись, сформированная CSP не проходит проверку через JCP.

Привожу пример исходного тестового кода программы формирования подписи средствами CSP (привожу код так, потому что не могу совладать с кодоформаттером форума):

#include <stdio.h>
#include <windows.h>
#include <wincrypt.h>
#include "crypto.h"
#include <iostream>
#include <fstream>

#pragma comment(lib, "Crypt32.Lib")
#define MY_TYPE (PKCS_7_ASN_ENCODING | X509_ASN_ENCODING)
#define CERT_STORE_NAME L"MY"

DWORD crypto() {
// Сообщение, которое мы подписываем
std::ifstream fl("D:\\req.xml");
fl.seekg(0, std::ios::end);
size_t len = fl.tellg();
char *ret = new char[len];
fl.seekg(0, std::ios::beg);
fl.read(ret, len);
fl.close();
BYTE* pbMessage = (BYTE*)ret;
DWORD cbMessage = (DWORD)strlen((char*) pbMessage)+1;

// Открываем хранилище сертификатов
HCERTSTORE hStoreHandle;

if (!(hStoreHandle = CertOpenStore(
CERT_STORE_PROV_SYSTEM,
0,
NULL,
CERT_SYSTEM_STORE_CURRENT_USER,
CERT_STORE_NAME)))
{
HandleError("Нельзя открыть хранилище MY.");
}

// Получаем указатель на наш сертификат
PCCERT_CONTEXT pSignerCert = NULL;

// берем для теста первый и единственный сертификат в хранилище
pSignerCert = CertEnumCertificatesInStore(hStoreHandle, pSignerCert);

// Переменные для указателя и длины подписи
BYTE *pbSignedMessageBlob;
DWORD cbSignedMessageBlob;

// Создаем и заполняем структуру для создания цифровой подписи
CRYPT_SIGN_MESSAGE_PARA SigParams;

SigParams.cbSize = sizeof(CRYPT_SIGN_MESSAGE_PARA);
SigParams.dwMsgEncodingType = MY_TYPE;
SigParams.pSigningCert = pSignerCert;
SigParams.HashAlgorithm.pszObjId = szOID_CP_GOST_R3411;
SigParams.HashAlgorithm.Parameters.cbData = NULL;
SigParams.cMsgCert = 1;
SigParams.rgpMsgCert = &pSignerCert;
SigParams.cAuthAttr = 0;
SigParams.dwInnerContentType = 0;
SigParams.cMsgCrl = 0;
SigParams.cUnauthAttr = 0;
SigParams.dwFlags = 0;
SigParams.pvHashAuxInfo = NULL;
SigParams.rgAuthAttr = NULL;


const BYTE* MessageArray[] = {pbMessage};
DWORD MessageSizeArray[1];
MessageSizeArray[0] = cbMessage;

// Получаем длину буфера подписи
if(CryptSignMessage(
&SigParams, // указатель на SigParams
TRUE, // подпись создается отдельно
1, // число сообщений
MessageArray, // сообщение
MessageSizeArray, // длина сообщения
NULL, // буфер для подписи
&cbSignedMessageBlob)) // размер буфера
{
printf("Размер подписи %d.\n",cbSignedMessageBlob);
}
else {
HandleError("Ошибка CryptSignMessage.");
}

// выделяем память под подпись
pbSignedMessageBlob = new BYTE[cbSignedMessageBlob];
if(!pbSignedMessageBlob) {
HandleError("Ошибка.");
}

// формируем подпись
if(!CryptSignMessage(
&SigParams, // указатель на SigParams
TRUE, // подпись создается отдельно
1, // число сообщений
MessageArray, // сообщение
MessageSizeArray, // длина сообщения
pbSignedMessageBlob, // буфер для подписи
&cbSignedMessageBlob)) // размер буфера
{
HandleError("Ошибка");
}

verifyMessage(pbMessage, pbSignedMessageBlob, cbSignedMessageBlob, pSignerCert);

if(pSignerCert)
CertFreeCertificateContext(pSignerCert);

if(CertCloseStore(hStoreHandle, CERT_CLOSE_STORE_CHECK_FLAG)) {
printf("\nХранилище закрыто. \n");
}
else {
printf("Ошибка!");
}

if(pbSignedMessageBlob) {
FILE *outfile_f = fopen("D:\\req.xml.sig", "wb");
if(!outfile_f) {
printf("Output file not found!");
}
fwrite(pbSignedMessageBlob, sizeof(BYTE), cbSignedMessageBlob, outfile_f);
fclose(outfile_f);
}

return cbSignedMessageBlob;
}

void HandleError(char *s) {
printf("Ошибка. \n");
printf("%s\n",s);
printf("Ошибка N %x.\n", GetLastError());
printf("Программа завершена с ошибкой. \n");
exit(1);
}

// функция обратного вызова для структуры
// CRYPT_VERIFY_MESSAGE_PARA VerifyParams;
PCCERT_CONTEXT WINAPI MyGetSignerCertificateCallback(
void *pvGetArg, // in
DWORD dwCertEncodingType, // in
PCERT_INFO pSignerId, // in
HCERTSTORE hMsgCertStore // in
)
{
return PCCERT_CONTEXT(pvGetArg);
};

void verifyMessage(BYTE* pbMessage, BYTE* pbArray, DWORD cbArray, PCCERT_CONTEXT pSignerCert) {
DWORD cbMessage = (DWORD)strlen((char*) pbMessage)+1;

CRYPT_VERIFY_MESSAGE_PARA VerifyParams;

// Заполнение структуры для верификации

VerifyParams.cbSize = sizeof(CRYPT_VERIFY_MESSAGE_PARA);
VerifyParams.dwMsgAndCertEncodingType = MY_TYPE;
VerifyParams.hCryptProv = 0;
VerifyParams.pfnGetSignerCertificate = MyGetSignerCertificateCallback;
VerifyParams.pvGetArg = (void*)pSignerCert;

const BYTE* MessageArray[] = {pbMessage};
DWORD MessageSizeArray[1];
MessageSizeArray[0] = cbMessage;

// верификация подписи
if(CryptVerifyDetachedMessageSignature(
&VerifyParams, // указатель на структуру VerifyParams
0, //
pbArray, // указатель на подпись
cbArray, // длина подписи
1, // число сообщений
MessageArray, // сообщение
MessageSizeArray, // длина сообщения
&pSignerCert)) // указатель на сертификат
{
printf("\nВерификация прошла успешно!.\n");
}
else
{
HandleError("Верификация не прошла.");
}
}

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

Хотелось бы узнать мнение более опытных пользователей по данному вопросу.
Может что-то в вышеприведенном коде не верно... И вообще, по вопросу совместимости CSP и JCP.
Offline i25061  
#2 Оставлено : 3 апреля 2013 г. 8:24:08(UTC)
i25061

Статус: Участник

Группы: Участники
Зарегистрирован: 09.03.2011(UTC)
Сообщений: 28
Мужчина
Откуда: Екатеринбург

Сказал «Спасибо»: 2 раз
Поблагодарили: 1 раз в 1 постах
В поставке JCP есть файл с примерами samples_src.jar.
Посмотрите в каталоге CMS_samples пример cms.java.
Обратите внимание на проверку отсоединённой подписи в Java. У Вас так?
В ГВЦ РЖД с такой проблемой сталкивались...им помогло.
Offline Apofig  
#3 Оставлено : 4 апреля 2013 г. 9:05:51(UTC)
Apofig

Статус: Участник

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

Сказал(а) «Спасибо»: 1 раз
Основная проблема в том, что мы не можем добиться внятного ответа от техподдержки стороннего сервиса, к которому отправляются запросы. Когда мы им прислали примеры наших отсоединенных подписей, созданных с помощью JCP и CryptoPro BrowserPlugin, c вопросом, почему первая проходит, а вторая нет, они ответили дословно:
Цитата:
Сертификат должен подписываться только средствами КриптоПро JCP. Во вложении документ по требованию к формированию ЭЦП файла пункт 3.4 и ниже.

В пункте 3.5 написано:
Цитата:
ЭЦП передаются при помощи контейнера PKCS #7 (RFC 2315, http://www.ietf.org/rfc/rfc2315.txt).
Для сохранения в файл используется DER-кодировка.
ЭЦП передаются в виде структуры ContentInfo со структурой SignedData в качестве содержимого. ЭЦП должна включать в себя сертификат и не должна включать подписанное содержимое.
Контейнер PKCS #7 должен быть совместим с контейнером, формируемым криптопровайдером КриптоПро CSP версии 3.6.

Не совсем понятна логика их ответа в сравнении с последним предложением, указанным выше.
Хотя бы подскажите, в коде, приведенном в первом посте (там, где происходит формирование отсоединенной подписи), все ли верно?
Offline Kirill Sobolev  
#4 Оставлено : 5 апреля 2013 г. 9:02:15(UTC)
Кирилл Соболев

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

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

Поблагодарили: 177 раз в 168 постах
Цитата:
Хотелось бы узнать мнение более опытных пользователей по данному вопросу.
Может что-то в вышеприведенном коде не верно...

Может быть. А подпись, созданная с помощью CSP, хоть чем-нибудь кроме Вашего кода проверяется? csptest, cryptcp, КриптоАРМ?
Техническую поддержку оказываем тут
Наша база знаний
Offline Apofig  
#5 Оставлено : 17 апреля 2013 г. 16:35:16(UTC)
Apofig

Статус: Участник

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

Сказал(а) «Спасибо»: 1 раз
Автор: Kirill Sobolev Перейти к цитате
Может быть. А подпись, созданная с помощью CSP, хоть чем-нибудь кроме Вашего кода проверяется? csptest, cryptcp, КриптоАРМ?

Крипто-АРМ выдает ошибку при проверке. Смотрел подписи CSP и ARM в ASN.1 редакторе... есть небольшие различия, но, насколько я вижу, все основные компоненты совпадают.

П.С. неужели никто не может оценить правильность кода? наверняка многие сталкивались с данной задачей.

Offline Юрий  
#6 Оставлено : 17 апреля 2013 г. 16:51:03(UTC)
Юрий

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

Группы: Участники
Зарегистрирован: 22.01.2008(UTC)
Сообщений: 671
Мужчина
Российская Федерация
Откуда: Йошкар-Ола

Сказал «Спасибо»: 3 раз
Поблагодарили: 93 раз в 67 постах
Какой код возвращает GetLastError после CryptVerifyDetachedMessageSignature?
С уважением,
Юрий Строжевский
Offline Apofig  
#7 Оставлено : 17 апреля 2013 г. 17:14:02(UTC)
Apofig

Статус: Участник

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

Сказал(а) «Спасибо»: 1 раз
Автор: Юрий Перейти к цитате
Какой код возвращает GetLastError после CryptVerifyDetachedMessageSignature?

В коде, приведенном в первом посте:
GetLastError: 2
Ошибки нет, выводится сообщение "Верификация прошла успешно". Не понятно, почему эта подпись не проходит валидность в КриптоАРМ (и на стороне сервиса).

Отредактировано пользователем 17 апреля 2013 г. 17:14:49(UTC)  | Причина: Не указана

Offline Юрий  
#8 Оставлено : 17 апреля 2013 г. 17:17:56(UTC)
Юрий

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

Группы: Участники
Зарегистрирован: 22.01.2008(UTC)
Сообщений: 671
Мужчина
Российская Федерация
Откуда: Йошкар-Ола

Сказал «Спасибо»: 3 раз
Поблагодарили: 93 раз в 67 постах
Автор: Apofig Перейти к цитате
Автор: Юрий Перейти к цитате
Какой код возвращает GetLastError после CryptVerifyDetachedMessageSignature?

В коде, приведенном в первом посте:
GetLastError: 2
Ошибки нет, выводится сообщение "Верификация прошла успешно". Не понятно, почему эта подпись не проходит валидность в КриптоАРМ (и на стороне сервиса).

Сертификат, которым подписали, проходит валидацию?
С уважением,
Юрий Строжевский
Offline Kirill Sobolev  
#9 Оставлено : 17 апреля 2013 г. 21:17:41(UTC)
Кирилл Соболев

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

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

Поблагодарили: 177 раз в 168 постах
Цитата:
П.С. неужели никто не может оценить правильность кода? наверняка многие сталкивались с данной задачей.

Вот тут у Вас проблема. Распространенная ошибка.
Код:
DWORD cbMessage = (DWORD)strlen((char*) pbMessage)+1;

Кстати, ее можно было бы легко локализовать попробовав создать для начала присоединенную подпись.
Техническую поддержку оказываем тут
Наша база знаний
Offline Apofig  
#10 Оставлено : 18 апреля 2013 г. 9:03:34(UTC)
Apofig

Статус: Участник

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

Сказал(а) «Спасибо»: 1 раз
Автор: Kirill Sobolev Перейти к цитате
Вот тут у Вас проблема. Распространенная ошибка.
Код:
DWORD cbMessage = (DWORD)strlen((char*) pbMessage)+1;

Кстати, ее можно было бы легко локализовать попробовав создать для начала присоединенную подпись.

Поменял код на
DWORD cbMessage = (lstrlen((TCHAR*) pbMessage) + 1) * sizeof(TCHAR);
Размер подписи стал очень близок к размеру подписи, сделанной АРМ (2636 и 2638 байт).
Результат проверки:
Верификация прошла успешно!.
GetLastError: 2.

Однако АРМ все равно выдает ошибку на проверке.

Автор: Юрий Перейти к цитате
Сертификат, которым подписали, проходит валидацию?

В АРМ у этого сертификата стоит статус "неизвестен". Или вы про какую валидацию?

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

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