logo
Добро пожаловать, Гость! Чтобы использовать все возможности Вход или Регистрация.

Уведомление

Icon
Error

Опции
К последнему сообщению К первому непрочитанному
Offline sergsenta1995  
#1 Оставлено : 7 ноября 2018 г. 14:58:11(UTC)
sergsenta1995

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

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

Проверяю подпись на С++:

Код:

// заполняются снаружи:
// std::vector<uint8_t> sign - считанная из файла подпись CADES_BES
// std::vector<uint8_t> data - считанные из файла исходные данные, которые подписывались

const BYTE* rgpbToBeSigned[] = {&data[0]};
DWORD rgcbToBeSigned[1] = {(DWORD)data.size()};
if (!CadesVerifyDetachedMessage(&cades_verify_message_para, 0,
     &sign[0],(DWORD)sign.size(), 1, rgpbToBeSigned, rgcbToBeSigned, &verify_inform))
{
    std::cout << "Error. Not valid " << GetLastError() << std::endl;
    return false;
}


Пусть в data лежат данные из 'file1.txt', а в sign подпись для 'file.txt', обозначим как 'sign1.dat'. В этом случае проверка подписи проходит корректно.
Теперь пусть в data лежат данные из 'bad-file2.txt', а в sign подпись для 'file.txt', та которая 'sign1.dat'. Т. е. подпись взята не от того файла. Но и в этом случае проверка подписи проходит корректно.
Так ведь не должно быть? Ф-я должна проверять корректность подписанных данных, а не просто корректность подписи.
Каким образом можно проверить подписанные данные с отсоединенной подписью?
Offline cross  
#2 Оставлено : 8 ноября 2018 г. 9:36:59(UTC)
cross

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

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

Сказал(а) «Спасибо»: 3 раз
Поблагодарили: 136 раз в 126 постах
По вашему куску не понятно что и как ломается или нет. Выложите полный пример, который продемонстрирует ошибку.
Анатолий Беляев (cross на cryptopro.ru)
Offline Ситдиков Денис  
#3 Оставлено : 8 ноября 2018 г. 9:44:22(UTC)
Ситдиков Денис

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

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

Поблагодарили: 2 раз в 2 постах
Добрый день!
CadesVerifyDetachedMessage проверяет корректность подписанных данных. При несовпадении данных, для которых выполнялась подпись, и подаваемых в функцию данных проверка завершается ошибкой.
Проверьте, пожалуйста, действительно ли эти данные не совпадают?
Offline sergsenta1995  
#4 Оставлено : 8 ноября 2018 г. 11:51:21(UTC)
sergsenta1995

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

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

Спасибо за оперативный ответ.

Использую:
Distributor ID: Ubuntu
Description: Ubuntu 16.04.5 LTS
Release: 16.04
Codename: xenial
Thread model: posix
gcc version 5.4.0 20160609
Криптопро CSP 5.0
Криптопро ЭЦП SDK 2.0

Более подробный код будет выглядеть приблизительно так:
Код:

// на моей системе BYTE == unsigned char == uint8_t
std::vector<BYTE> data_ok(10, 25);     // это подписывается и проверяется
std::vector<BYTE> data_fail(30, 30);   // это НЕ подписывается

// Открываем хранилище сертификатов пользователя
HCERTSTORE hStoreHandle = CertOpenSystemStore(0, _TEXT("MY"));
if (!hStoreHandle)
{
    std::cout << "Store handle was not got" << std::endl;
    return;
}

// Получаем сертификат для подписания
PCCERT_CONTEXT context = get_recipient_certificaly(hStoreHandle);
// Если сертификат не найден, завершаем работу
if (!context)
{
    std::cout << "There is no certificate with a CERT_KEY_CONTEXT_PROP_ID \n"
        << "property and an AT_KEYEXCHANGE private key available. \n"
        << "While the message could be sign, in this case, it could \n"
        << "not be verify in this program. \n"
        << "For more information, read the documentation http://cpdn.cryptopro.ru/" << std::endl;
    return;
}


// Задаем параметры для подписи
CRYPT_SIGN_MESSAGE_PARA signPara = { sizeof(signPara) };
signPara.dwMsgEncodingType = X509_ASN_ENCODING | PKCS_7_ASN_ENCODING;
signPara.pSigningCert = context;
signPara.HashAlgorithm.pszObjId = (LPSTR) szOID_CP_GOST_R3411; // Хэш считается по ГОСТ Р 34.11-94

CADES_SIGN_PARA cadesSignPara = { sizeof(cadesSignPara) };
cadesSignPara.dwCadesType = CADES_BES; // Указываем тип усовершенствованной подписи CADES_BES

CADES_SIGN_MESSAGE_PARA para = { sizeof(para) };
para.pSignMessagePara = &signPara;
para.pCadesSignPara = &cadesSignPara;

// Формируем данные для подписания
const unsigned char *pbToBeSigned[] = { &data_ok[0] };
DWORD cbToBeSigned[] = { (DWORD)data_ok.size() };
PCRYPT_DATA_BLOB pSignedMessage = 0;
// Создаем подписанное сообщение
if (!CadesSignMessage(&para,
                      true, // отсоединенная подпись
                      1, pbToBeSigned, cbToBeSigned, &pSignedMessage))
{
    std::cout << "CadesSignMessage() failed" << std::endl;
}

// Получили подпись:
std::vector<unsigned char> detached_sign(pSignedMessage->cbData);
std::copy(pSignedMessage->pbData, pSignedMessage->pbData + pSignedMessage->cbData, detached_sign.begin());


// ======= проверка =========


CRYPT_VERIFY_MESSAGE_PARA crypt_verify_message_parameters = { sizeof(crypt_verify_message_parameters) };
crypt_verify_message_parameters.dwMsgAndCertEncodingType = X509_ASN_ENCODING | PKCS_7_ASN_ENCODING;
// по примерам эти параметры не заполнены
// но полазив по руководствам их можно заполнить вот так (толку от этого никакого):
//crypt_verify_message_parameters.cbSize = задан в конструкторе
// crypt_verify_message_parameters.hCryptProv = NULL; // т. к. не исп. согласно MSDN
// crypt_verify_message_parameters.pfnGetSignerCertificate = NULL; // так по умолчанию
// crypt_verify_message_parameters.pvGetArg = (void*)context;


CADES_VERIFICATION_PARA cades_verify_parameters = { sizeof(cades_verify_parameters) };
cades_verify_parameters.dwCadesType = CADES_BES;
cades_verify_parameters.hStore = hStoreHandle;
// по примерам эти параметры не заполнены
// но полазив по руководствам их можно заполнить вот так (толку от этого никакого):
//cades_verify_parameters.dwSize = задан в конструкторе
//cades_verify_parameters.pMessageContentHash = не исп.
//cades_verify_parameters.pProxyPara = не нужно
// cades_verify_parameters.bReserved2 = FALSE; // зарезервирован, но должен быть равет FALSE
// cades_verify_parameters.pReserved3 = NULL;// зарезервирован, но должен быть равет NULL

CADES_VERIFY_MESSAGE_PARA cades_verify_message_para = { sizeof(cades_verify_message_para) };
cades_verify_message_para.pVerifyMessagePara = &crypt_verify_message_parameters;
cades_verify_message_para.pCadesVerifyPara = &cades_verify_parameters;


PCADES_VERIFICATION_INFO verify_inform = 0;

// Корректные данные с корректной подписью:
const BYTE* rgpbToBeSignedOk[] = {&data_ok[0]};
DWORD rgcbToBeSignedOk[1] = {(DWORD)data_ok.size()};
if (CadesVerifyDetachedMessage(
            &cades_verify_message_para,
            0,
            &detached_sign[0],(DWORD)detached_sign.size(),
            1,
            rgpbToBeSignedOk, rgcbToBeSignedOk,
            &verify_inform) == false)
{
    std::cout << "Error. Not valid " << GetLastError() << std::endl;
}
else
{
    std::cout << "Is valid. " << GetLastError() << std::endl;
}

// Вывод аналогичен выводу выше.
if (verify_inform->dwStatus != CADES_VERIFY_SUCCESS)
    std::cout << "Message is not verified successfully." << std::endl;
else
    std::cout << "Message verified successfully." << std::endl;

// НЕкорректные данные с корректной подписью:
const BYTE* rgpbToBeSignedFail[] = {&data_fail[0]};
DWORD rgcbToBeSignedFail[1] = {(DWORD)data_fail.size()};
if (CadesVerifyDetachedMessage(
            &cades_verify_message_para,
            0,
            &detached_sign[0],(DWORD)detached_sign.size(),
            1,
            rgpbToBeSignedFail, rgcbToBeSignedFail,
            &verify_inform) == false)
{
    std::cout << "Error. Not valid " << GetLastError() << std::endl;
}
else
{
    std::cout << "Is valid. " << GetLastError() << std::endl;
}

// Вывод аналогичен выводу выше.
if (verify_inform->dwStatus != CADES_VERIFY_SUCCESS)
    std::cout << "Message is not verified successfully." << std::endl;
else
    std::cout << "Message verified successfully." << std::endl;

// ======= освобождаем все =========

// Освобождаем структуру с закодированным подписанным сообщением
if (!CadesFreeBlob(pSignedMessage))
{
    std::cout << "CadesFreeBlob() failed" << std::endl;
}
// Закрываем хранилище
if (!CertCloseStore(hStoreHandle, 0))
{
    std::cout << "Certificate store handle was not closed." << std::endl;
}

if (!CadesFreeVerificationInfo(verify_inform))
{
    std::cout << "CadesFreeVerificationInfo() failed" << std::endl;
}

// Освобождаем контекст сертифката
if (context)
    CertFreeCertificateContext(context);


Соответственно, вывод в консоль будет следующим (учитывая ввод пароля для подписи):
Код:

Crypto-Pro GOST R 34.10-2001 KC1 CSP requests password
Please, type password:
Is valid. 2148081679
Message verified successfully.
Is valid. 2148081679
Message verified successfully.

(почему-то все проходит корректно, но появляется загадочная ошибка 2148081679 (0x8009200F) - CRYPT_E_PENDING_CLOSE следующего содержания:
'Final closure is pending until additional frees or closes.')
Offline two_oceans  
#5 Оставлено : 9 ноября 2018 г. 7:08:46(UTC)
two_oceans

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

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

Сказал(а) «Спасибо»: 8 раз
Поблагодарили: 33 раз в 33 постах
Что-то этот код кажется чистой магией на мой непривычный к Си взгляд, особенно в части копирования полученной подписи ("== false" особо порадовало). По вопросу: мое предположение, что несмотря на передачу detached = true подпись получается присоединенной и потому проверка берет исходные данные из самой подписи и игнорирует переданные извне исходные данные. Для проверки предположения попробуйте поискать исходные данные в подписи, в отсоединенной их быть не должно.

По коду ошибки - очень похоже на код, который возвращается когда остается открытый контекст сертификата при закрытии хранилища сертификатов. Для чистоты экспериментов на windows я вызываю setLastError(0) перед операцией для очистки прошлого кода ошибки. Суть в том, что если ошибки при последней операции не было, предыдущее значение ошибки сохраняется (не заменяется на 0 как можно подумать). Поэтому нужно перед операцией желательно поставить код ошибки 0 вручную, если нужен только код ошибки самой последней операции, а не той ошибки что была 100500 операций назад. Под *nix это особенно актуально, так как GetLastError() SetLastError() там не связаны с системой и система их не обнулит при какой-то еще операции.
Offline sergsenta1995  
#6 Оставлено : 9 ноября 2018 г. 7:36:31(UTC)
sergsenta1995

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

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

Нет, подпись создается как отсоединенная. Это особенно видно (протестировал), если подсунуть буфер заполненный данными из jpg-файла, а не игрушечный вектор с числами. При jpg-файле размером в 5,5 Кб присоединенная подпись == 6,5 Кб, а отсоединенная == 647 байт.
Код ошибки выставляется как раз после вызова CadesVerifyDetachedMessage(..), до этого он равен 0. Причем, в проекте это все разнесено по ф-ям: подпись отдельно, проверка отдельно. Переменные-указатели закрываются/освобождаются с помощью соответствующих ф-й. Остальные переменные на стеке и освобождаются автоматически. Собственно, там закрывать просто уже нечего.
Offline sergsenta1995  
#7 Оставлено : 12 ноября 2018 г. 12:36:16(UTC)
sergsenta1995

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

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

Есть ещё какие-нибудь идеи по этому вопросу?
Offline cross  
#8 Оставлено : 12 ноября 2018 г. 14:12:00(UTC)
cross

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

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

Сказал(а) «Спасибо»: 3 раз
Поблагодарили: 136 раз в 126 постах
У нас на вашем примере не воспроизводится. Можете сохранить данные которые подписывали в файлы data.bin и data_bad.bin а подпись в два файла(одинакового содержимого) data.bin.sgn data_bad.bin.sgn. Далее выполнить команды находясь в директории в которой лежат сохраненные файлы

/opt/cprocsp/bin/amd64/cryptcp -vsignf -f ./data.bin.sgn ./data.bin
/opt/cprocsp/bin/amd64/cryptcp -vsignf -f ./data_bad.bin.sgn ./data_bad.bin

/opt/cprocsp/bin/amd64/cryptcp -vsignf -cadesbes -f ./data.bin.sgn ./data.bin
/opt/cprocsp/bin/amd64/cryptcp -vsignf -cadesbes -f ./data_bad.bin.sgn ./data_bad.bin
Анатолий Беляев (cross на cryptopro.ru)
Offline sergsenta1995  
#9 Оставлено : 12 ноября 2018 г. 17:02:20(UTC)
sergsenta1995

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

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

Сохранил правильный и неправильный буферы данных в разные файлы. Подпись (одну и ту же) сохранил как data.bin.sgn, а потом как data_bad.bin.sgn. Единственное, добавил критерий поиска сертификатов. Выполнил:
Код:

/opt/cprocsp/bin/amd64/cryptcp -vsignf -uMY -f ./data_bad.bin.sgn ./data_bad.bin

Безрезультатно:
Код:

CryptCP 5.0 (c) "КРИПТО-ПРО", 2002-2018.
Утилита командной строки для подписи и шифрования файлов.

Найдено сертификатов: 2
Цепочки сертификатов проверены.
Папка './':
./data_bad.bin... Проверка подписи...     
    
Автор подписи: cas@altlinux.org, Test User5
 Подпись проверена.
[ErrorCode: 0x00000000]


Я вроде нашел выход: подписываю хеш от данных с помощью CadesSignHash, а потом проверяю с помощью CadesVerifyHash. Т. е. нужно иметь дело с низкоуровневыми вызовами. Но ведь это должна делать функция CadesVerifyDetachedMessage автоматически, т. к. в CADES-BES есть параметр с хешом от данных (согласно этой статье).

Все-таки интересно в чем причина неправильной работы функции CadesVerifyDetachedMessage.
Offline cross  
#10 Оставлено : 13 ноября 2018 г. 12:29:03(UTC)
cross

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

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

Сказал(а) «Спасибо»: 3 раз
Поблагодарили: 136 раз в 126 постах
А можете архив с этими файликами приложить в топик.
Анатолий Беляев (cross на cryptopro.ru)
Offline sergsenta1995  
#11 Оставлено : 14 ноября 2018 г. 7:52:32(UTC)
sergsenta1995

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

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

Конечно:
test.zip (23kb) загружен 1 раз(а).
Offline cross  
#12 Оставлено : 14 ноября 2018 г. 8:02:01(UTC)
cross

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

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

Сказал(а) «Спасибо»: 3 раз
Поблагодарили: 136 раз в 126 постах
Можете еще сертификат подписчика приложить. В подписи его не оказалось.
Анатолий Беляев (cross на cryptopro.ru)
Offline sergsenta1995  
#13 Оставлено : 14 ноября 2018 г. 8:26:18(UTC)
sergsenta1995

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

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

cert.zip (1kb) загружен 1 раз(а).
Offline cross  
#14 Оставлено : 14 ноября 2018 г. 12:11:44(UTC)
cross

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

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

Сказал(а) «Спасибо»: 3 раз
Поблагодарили: 136 раз в 126 постах
Мне кажется сообщение другим сертификатом подписано. Там внутри серийник подписанта другой. 12 00 2Е 45 ... В приложенном сертификате другой серийник.

Отредактировано пользователем 14 ноября 2018 г. 13:06:43(UTC)  | Причина: Не указана

Анатолий Беляев (cross на cryptopro.ru)
Offline sergsenta1995  
#15 Оставлено : 15 ноября 2018 г. 8:31:26(UTC)
sergsenta1995

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

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

Теперь вроде оно:
cert.cer.zip (1kb) загружен 1 раз(а).

Проверил с помощью команды (тот сертификат выдавал ошибку)
Код:

/opt/cprocsp/bin/amd64/cryptcp -vsignf -f ./cert.cer -f ./data_bad.bin.sgn ./data_bad.bin
Offline cross  
#16 Оставлено : 15 ноября 2018 г. 10:55:36(UTC)
cross

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

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

Сказал(а) «Спасибо»: 3 раз
Поблагодарили: 136 раз в 126 постах
С подписью все ок. Хеш там правильный указан. Но это не приблизило нас к причине почему у вас она проверяется с плохими данными. Есть ли возможность предоставить доступ к этой машине? (ssh или teamviewer). Какая то крайне странная ситуация в которой хочется разобратся.

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

Анатолий Беляев (cross на cryptopro.ru)
Offline sergsenta1995  
#17 Оставлено : 15 ноября 2018 г. 11:36:48(UTC)
sergsenta1995

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

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

Хм..... Я могу подготовить образ виртуальной машины с установленными пакетами/исходниками/тестируемыми файлами, который можно будет скачать из облака по общедоступной ссылке. Такой вариант Вам подходит?
Offline cross  
#18 Оставлено : 15 ноября 2018 г. 12:13:01(UTC)
cross

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

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

Сказал(а) «Спасибо»: 3 раз
Поблагодарили: 136 раз в 126 постах
Да. Было бы отлично.
Анатолий Беляев (cross на cryptopro.ru)
Offline sergsenta1995  
#19 Оставлено : 15 ноября 2018 г. 19:39:30(UTC)
sergsenta1995

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

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

virtual -это ссылка на архив с виртуальной машиной. Установлена Ubuntu 18.04 server x64.
пользователь - cryptopro
пароль (криптопро, но в англ. раскладке) - rhbgnjghj
пароль (от контейнера) при подписании - rhbgnjghj
Все необходимое находится в папке /home/cryptopro:
build - собранный проект для тестирования подписи (исполняемый файл - cryptopro-test); в этой папке также находятся тестовые файлы
certnew.p7b - цепочка сертификатов
cryptopro-installer - тут находятся все пакеты криптопро, которые устанавливались на систему
cryptopro-installer.zip - аналогично, но архивом
cryptopro-test - исходники тестового проекта
run - инструкции, которые последовательно запускались для начала работы с криптопро (запрос сертификатов, инициализация ридеров и т. д.)
test5.req - запрос на сертификат

Да, забыл упомянуть. Криптопро не понимает что он установлен не на Windows. Поэтому подключаемый файл WinCryptEx.h пытается найти wincrypt.h вместо CSP_WinCrypt.h. Поэтому я с помощью команды
Код:
sudo sed  -i '33i\#define UNIX' /opt/cprocsp/include/cpcsp/WinCryptEx.h

заставил найти нужный файл. Но я не думаю, что эта правка является причиной ошибки. А если это так, то новый вопрос: как указать криптопро, что нужно искать файл CSP_WinCrypt.h?

Отредактировано пользователем 16 ноября 2018 г. 8:46:53(UTC)  | Причина: указал пароль от контейнера

Offline cross  
#20 Оставлено : 16 ноября 2018 г. 10:15:50(UTC)
cross

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

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

Сказал(а) «Спасибо»: 3 раз
Поблагодарили: 136 раз в 126 постах
Увидел версию билда для 5.0 и стало понятно. В этой версии сломано получения параметра CMSG_COMPUTED_HASH_PARAM. что приводит к таким печальным последствиям. Можете обновить версию провайдера (https://wwww.cryptopro.ru/sites/d...1262/linux-amd64_deb.tgz).

Для того что бы хедеры не трогать при компиляции вашего кода можно добавить -DUNIX в флаги компиляции.
Анатолий Беляев (cross на cryptopro.ru)
RSS Лента  Atom Лента
Пользователи, просматривающие эту тему
Быстрый переход  
Вы не можете создавать новые темы в этом форуме.
Вы не можете отвечать в этом форуме.
Вы не можете удалять Ваши сообщения в этом форуме.
Вы не можете редактировать Ваши сообщения в этом форуме.
Вы не можете создавать опросы в этом форуме.
Вы не можете голосовать в этом форуме.