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

Уведомление

Icon
Error

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

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

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

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

Код:

// заполняются снаружи:
// 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 г. 12:36:59(UTC)
Анатолий Беляев

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

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

Сказал(а) «Спасибо»: 3 раз
Поблагодарили: 174 раз в 152 постах
По вашему куску не понятно что и как ломается или нет. Выложите полный пример, который продемонстрирует ошибку.
Техническую поддержку оказываем тут.
Наша база знаний.
Наша страничка в Instagram.
Offline Ситдиков Денис  
#3 Оставлено : 8 ноября 2018 г. 12:44:22(UTC)
Ситдиков Денис

Статус: Администратор

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

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

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

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

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

Использую:
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 г. 10:08:46(UTC)
two_oceans

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

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

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

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

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

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

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

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

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

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

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

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

Сказал(а) «Спасибо»: 3 раз
Поблагодарили: 174 раз в 152 постах
У нас на вашем примере не воспроизводится. Можете сохранить данные которые подписывали в файлы 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
Техническую поддержку оказываем тут.
Наша база знаний.
Наша страничка в Instagram.
Offline sergsenta1995  
#9 Оставлено : 12 ноября 2018 г. 20:02:20(UTC)
sergsenta1995

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

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

Сохранил правильный и неправильный буферы данных в разные файлы. Подпись (одну и ту же) сохранил как 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 г. 15:29:03(UTC)
Анатолий Беляев

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

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

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