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

Уведомление

Icon
Error

Опции
К последнему сообщению К первому непрочитанному
Offline boa85  
#1 Оставлено : 19 февраля 2019 г. 21:29:56(UTC)
boa85

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

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

Собственно сравнение двух наборов данных.[/img]Добрый вечер. Возможно не совсем корректно сформулировал тему, но постараюсь объяснить суть проблемы.
При поиске сертификата по CERT_ID получаю на вход данные по издателю и серийный номер сертификата. Сертификат в виде строки. Полученную строку конвертирую в ASN.1 для заполнения структуры CERT_ISSUER_SERIAL_NUMBER .
И тут начинается темная магия. Полученные мной данные издателя отличаются от тех, что хранятся в самом сертификате. Но только тем, что в оригинале у нас NumericString для ИНН и ОГРН. В полученной вручную строке - это OCTET STRING. Соответственно поиск не срабатывает. Как быть? Ниже работающий код и скриншот из ASN.1 редактора.
Код:

#include <cstdio>
#include <cstdlib> 
#include <iostream>
#include <string>
#include <wtypes.h>
#include <wincrypt.h>
#include <fstream>
#include <Windows.h>
#include <vector>
#pragma comment(lib, "crypt32.lib")

#define MY_ENCODING_TYPE (PKCS_7_ASN_ENCODING | X509_ASN_ENCODING)
#define MY_STRING_TYPE (CERT_SIMPLE_NAME_STR)
using ByteArray = std::vector<uint8_t>;

static std::vector<uint8_t> read_file(const std::string& path)
{
	std::ifstream file(path, std::ifstream::binary);
	if (!file)
		throw std::runtime_error("Couldn't open file: " + path);


	file.seekg(0, std::ios::end);
	std::vector<uint8_t> data(static_cast<size_t>(file.tellg()));

	file.seekg(0, std::ios::beg);
	file.read(reinterpret_cast<char*>(data.data()), data.size());
	if (!file)
		throw std::runtime_error("Couldn't read file: " + path);

	file.close();
	return data;
}// read_file

static ByteArray string2asn1(const LPCSTR pszString, const int str_type)
{
	ByteArray result;
	DWORD cbSize;
	CERT_NAME_BLOB cryptoapi_blob;


	if (!(CertStrToNameA(
		MY_ENCODING_TYPE,
		pszString,
		str_type,
		NULL,
		NULL,
		&cbSize,
		NULL)))
	{
		std::cout << "\nCould not get the length of the BLOB.\n";
		return  result;
	}

	if (!(cryptoapi_blob.pbData = (LPBYTE)malloc(cbSize)))
	{
		std::cout << "\nMemory Allocation for the BLOB failed.\n";
		return  result;
	}
	cryptoapi_blob.cbData = cbSize;

	if (!(CertStrToNameA(
		MY_ENCODING_TYPE | CERT_NAME_STR_ENABLE_UTF8_UNICODE_FLAG,
		pszString,
		str_type,
		NULL,
		cryptoapi_blob.pbData,
		&cryptoapi_blob.cbData,
		NULL)))
	{
		std::cout << "\nCould not write the blob.\n";
		return  result;
	}
	return ByteArray(cryptoapi_blob.pbData, cryptoapi_blob.pbData + cryptoapi_blob.cbData);
}// wstring2asn1


static void write_file(IN const std::string& path, IN const void* data, int size)
{
	std::ofstream file(path, std::ofstream::binary | std::ofstream::trunc);
	if (!file) {
		throw std::runtime_error("Couldn't open file: " + path);
	}
	file.write(reinterpret_cast<const char*>(data), size);
	file.close();
}// write_file

int main(int argc, char* argv[])
{
	{
		std::string issuer = R"(CN="Тестовый УЦ ООО \"КРИПТО-ПРО\"", O="ООО \"КРИПТО-ПРО\"", C=RU, EMAILADDRESS=info@cryptopro.ru, L=Москва, ST=77 г. Москва, STREET="ул. Сущёвский вал, д. 18", OID.1.2.643.3.131.1.1=#120C303037373137313037393931, OID.1.2.643.100.1=#120D31303337373030303835343434)";
		std::string email = "EMAILADDRESS";
		issuer.replace(issuer.find(email), email.length(), "E");
		ByteArray result((BYTE*)issuer.data(), (BYTE*)issuer.data() + issuer.length());
		auto counter = 0;
		for (auto& symbol : result)
		{
			if (symbol == '\\')
				symbol = '\"';
		}
		for (auto& symbol : issuer)
		{
			if (symbol == '\\')
				symbol = '\"';
		}
		auto calc_issuer = string2asn1(issuer.data(), CERT_X500_NAME_STR | CERT_NAME_STR_REVERSE_FLAG);



		write_file("C:\\test\\issuer_new.txt", calc_issuer.data(), calc_issuer.size());
		CERT_ISSUER_SERIAL_NUMBER cert_issuer_serial_number;
		auto serial_orig = read_file("C:\\test\\serial.txt");// здесь оригинальный серийник

		cert_issuer_serial_number.Issuer.pbData = calc_issuer.data();
		cert_issuer_serial_number.Issuer.cbData = static_cast<DWORD>(calc_issuer.size());
		cert_issuer_serial_number.SerialNumber.pbData = (BYTE*)(serial_orig.data());
		cert_issuer_serial_number.SerialNumber.cbData = static_cast<DWORD>(serial_orig.size());
		CERT_ID cert_id;
		cert_id.dwIdChoice = 1;
		cert_id.IssuerSerialNumber = cert_issuer_serial_number;
		HCERTSTORE store_handle;
		// Открываем хранилище сертификатов
		if (!(store_handle = CertOpenStore(
			CERT_STORE_PROV_SYSTEM,
			0,
			NULL,
			CERT_SYSTEM_STORE_CURRENT_USER,
			L"MY")))
		{
			std::cout << "cert not found";
		}

		PCCERT_CONTEXT pCertCtx = CertFindCertificateInStore(
			store_handle,
			MY_ENCODING_TYPE,
			0,
			CERT_FIND_CERT_ID,
			&cert_id,
			nullptr);
		
		
		if (!pCertCtx)
		{
			std::cout << "cert not found\n";			
		}
		else
			std::cout << "cert found\n";

		system("pause");
		return 0;
	}
}

данные по издателю, полученные из строки
Оригинал
Сравнение двух наборов данных

Отредактировано пользователем 19 февраля 2019 г. 21:54:21(UTC)  | Причина: Не указана

Offline two_oceans  
#2 Оставлено : 20 февраля 2019 г. 6:36:49(UTC)
two_oceans

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

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

Сказал(а) «Спасибо»: 30 раз
Поблагодарили: 118 раз в 113 постах
Автор: boa85 Перейти к цитате
Добрый вечер. Возможно не совсем корректно сформулировал тему, но постараюсь объяснить суть проблемы.
При поиске сертификата по CERT_ID получаю на вход данные по издателю и серийный номер сертификата. Сертификат в виде строки. Полученную строку конвертирую в ASN.1 для заполнения структуры CERT_ISSUER_SERIAL_NUMBER .
И тут начинается темная магия. Полученные мной данные издателя отличаются от тех, что хранятся в самом сертификате. Но только тем, что в оригинале у нас NumericString для ИНН и ОГРН. В полученной вручную строке - это OCTET STRING. Соответственно поиск не срабатывает. Как быть? Ниже работающий код и скриншот из ASN.1 редактора.

Какое-то непонятное объяснение, но в целом смысл понятен - функция преобразования "не знает" о нужном для ИНН/ОГРН типе кодирования. И тут бы указать версию криптопровайдера и ОС.
Как быть - очевидно, получить блоб издателя из сертификата и сохранить его вместо того чтобы мучаться со строкой издателя. Собственно, лучше всего искать сертификат по отпечатку, а не по издателю с серийным номером.

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

Если же все-таки настаиваете на поиске - в этом случае "обходным маневром" является замена автоматического поиска на перечисление сертификатов и извлечение из них строки издателя и сравнение строки Вашей программой. В этом случае тип кодирования не будет проблемой.

Еще вариант (раз уж Вы знаете о таком баге кодирования): пропатчить полученное в блобе значение изменив OCTET STRING на NUMERIC STRING. Так как место может быть разное сначала нужно найти оиды ИНН и огрн по двоичному представлению, а потом заменить байт типа кодирования.

Итого - ситуация далеко не безвыходная, даже если программа будет эксплуатированться на компьютере где нет соответствующего исправления криптопровайдера.
Offline AlexEr81  
#3 Оставлено : 21 февраля 2019 г. 9:34:44(UTC)
AlexEr81

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

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

Сказал(а) «Спасибо»: 8 раз
Поблагодарили: 1 раз в 1 постах
может оно - https://www.cryptopro.ru....aspx?g=posts&t=6201

хотя могут быть же какие-то нестандартные OIDы
тогда закодировать с помощью CryptEncodeObject(...X509_NAME...)

Отредактировано пользователем 21 февраля 2019 г. 15:22:43(UTC)  | Причина: Не указана

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