02.05.2007 19:04:23Сборка pkcs#7 Ответов: 10
UndeadKing
Есть encrypted digest. Вопрос: как его положить внутря сообщения pkcs#7?
 
Ответы:
02.05.2007 19:27:15UndeadKing
Немного конкретизирую вопрос: в стандарте RFC 2315 SignerInfo содержит поле EncryptedDigest... А в микрософтовской реализации этого поля не вывешивается (вероятно, чтобы не трогали руками). Есть ли более законный способ?..
02.05.2007 19:55:13UndeadKing
и снова извиняюсь, данное поле, насколько я понял, есть EncryptedHash.
Порядок действий такой:
CryptMessageOpenToEncode с параметром CMSG_DATA, туда добавляется CryptMsgUpdate'ом SignerInfo, дальше достается из сообщения с помощью CryptMsgGetParam с параметром CMSG_CONTENT_PARAM полученное сообщение, но оно какое-то короткое (288 байт вместо стандартных 322, при создании подписи нормальным путем), и, при попытке проверки на втором Update'e (которым добавляются данные для проверки в сообщение) приходит отрицательный ответ.. Что я делаю не так?
03.05.2007 11:31:51Kirill Sobolev
А откуда вообще такой странный способ взялся - собирать PKCS7 с помощью CryptMsg* функций?
04.05.2007 15:35:50UndeadKing
производственная необходимость =) А если серьезно, то есть готовые Digest'ы, есть нужда (для другой системы) соорудить из них сообщения. Переподписывать - совсем не вариант. Вот и приходится изгаляться
04.05.2007 17:17:55Kirill Sobolev
Не, я не спрашиваю зачем Вы это делаете - это вообщем Ваше дело :) Вопрос другой - почему Вы решили это делать через CryptMsg*?
06.05.2007 3:16:24Алексей
У меня тоже появилась производственная необходимость сделать pkcs#7
подпись из короткой. Штатных способов не нашел,
поэтому собрал наполовину вручную. Сделал недавно, могут быть ошибки,
но в предварительный тест прошла.
Будут вопросы, посылай на мэйл ap[собака]front.ru
(читай с конца)


#include "stdafx.h"
#include "Pkcs7.h"

Pkcs7::Pkcs7(byte_buffer& LongSign):
LongSign(LongSign), idx(0)
{
};

// кодирует длину (считаем что умещается в 2 байта)
void Pkcs7::EncodeSize(size_t dwSize) {
LongSign[idx--] = static_cast<BYTE>(dwSize);
if (dwSize > 0xFF) {
LongSign[idx--] = static_cast<BYTE>(dwSize >> 8);
LongSign[idx--] = 0x82;
} else
if (dwSize > 0x7F)
LongSign[idx--] = 0x81;
}

// добавляет данные как есть
void Pkcs7::AddData(const BYTE* pbyData, DWORD size) {
idx -= size;
::CopyMemory(&LongSign[idx + 1], pbyData, size);
}

/**
Кодирование OBJECT IDENTIFIER

BER-кодирование OBJECT IDENTIFIER является всегда примитивным. Октеты содержимого
представляют собой объединение n-1 строки октетов, где n число компонент объектного
идентификатора. Каждая октетная строка несет в себе целое число по модулю 128
(старшая часть первая). 8-ой бит каждого октета, кроме последнего, равен 1. Пусть
value1, …, valuen целые значения компонентов объектного идентификатора. Тогда n-1
субидентификаторов, из которых формируется октетная строка, будут иметь следующий вид.

Первый субидентификатор равен 40value1 + value2. (значение value1 лежит в пределах 0-2
включительно, а value2 в интервале 0-39, когда value1 равна 0 или 1.

i-ый субидентификатор равен valuei+1 ; 2 <= i<= n-1.

**/
bool Pkcs7::AddOID(const char* pszObjId) {
size_t startIdx = idx;
// сначала преобразуем строку вида value1.value2.... в массив __int64
unsigned __int64 comp[20];
size_t ncomp = 0;
comp[0] = 0;
for (const char* pch = pszObjId; *pch != 0; pch++) {
if (*pch == '.') {
ncomp++;
if (ncomp >= sizeof(comp))
return false;
comp[ncomp] = 0;
continue;
}
int by = *pch - '0';
if (by < 0 || by > 9)
return false;
comp[ncomp] = comp[ncomp]*10 + by;
}
// преобразуем в der с конца кроме первых двух
for (size_t i = ncomp; i > 1; i--) {
unsigned __int64 c = comp[i];
size_t carryflag = 0;
do {
if (idx < 3)
return false;
LongSign[idx--] = static_cast<BYTE>((c & 0x7F) | carryflag);
c >>= 7;
carryflag = 0x80;
} while (c > 0);
}
// Преобразуем первые два числа
BYTE byte0 = 40*static_cast<BYTE>(comp[0]);
if (comp[0] > 2)
return false; // value1 лежит в пределах 0-2 включительно
if (ncomp > 0)
byte0 += static_cast<BYTE>(comp[1]);
LongSign[idx--] = byte0; // Первый байт получается из двух первых чисел

EncodeSize(startIdx - idx); // размер
LongSign[idx--] = 0x06; // код OBJECT IDENTIFIER
return true;
}

DWORD HandleError(DWORD dwErr = -5) {
return ::HandleError(dwErr, L"Невозможно закодировать объект в PKCS#7");
}

/*

ContentInfo ::= SEQUENCE {
contentType ContentType,
content
[0] EXPLICIT ANY DEFINED BY contentType OPTIONAL }

ContentType ::= OBJECT IDENTIFIER

SignedData ::= SEQUENCE {
version CMSVersion,
digestAlgorithms DigestAlgorithmIdentifiers,
encapContentInfo EncapsulatedContentInfo,
certificates [0] IMPLICIT CertificateSet OPTIONAL,
crls [1] IMPLICIT CertificateRevocationLists OPTIONAL,
signerInfos SignerInfos
}

DigestAlgorithmIdentifiers ::= SET OF DigestAlgorithmIdentifier

DigestAlgorithmIdentifier ::= AlgorithmIdentifier

AlgorithmIdentifier ::= SEQUENCE {
algorithm OBJECT IDENTIFIER,
parameter ANY DEFINED BY algorithm OPTIONAL
}

SignerInfos ::= SET OF SignerInfo

SignerInfo ::= SEQUENCE {
version CMSVersion,
sid SignerIdentifier,
digestAlgorithm DigestAlgorithmIdentifier,
signedAttrs [0] IMPLICIT SignedAttributes OPTIONAL,
signatureAlgorithm SignatureAlgorithmIdentifier,
signature SignatureValue,
unsignedAttrs [1] IMPLICIT UnsignedAttributes OPTIONAL
}
*/

/**
/* 0 291: SEQUENCE {* /0x30,0x82,0x01,0x23,
/* 4 9: OBJECT IDENTIFIER signedData * / 0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x07,0x02,
/* 15 276: [0] { * / 0xA0,0x82,0x01,0x14,
/* 19 272: SEQUENCE { * / 0x30,0x82,0x01,0x10,
/* 23 1: INTEGER 1 * /0x02,0x01,0x01,
/* 26 12: SET { * / 0x31,0x0C,
/* 28 10: SEQUENCE { * / 0x30,0x0A,
/* 30 6: OBJECT IDENTIFIER alg oid * / 0x06,0x06,0x2A,0x85,0x03,0x02,0x02,0x09,
/* 38 0: NULL * / 0x05,0x00,
/* : } * /
/* : } * /
/* 40 11: SEQUENCE { * / 0x30,0x0B,
/* 42 9: OBJECT IDENTIFIER data * / 0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x07,0x01,
/* : } */
/* 53 239: SET {...* / 0x31,0x81,0xEF
Дальше идет signerInfo
**/

const BYTE gEncapsulatedContentInfo[] = {0x30,0x0B,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x07,0x01};
const BYTE gNullVal[] = {0x05, 0x00};
const BYTE gVersionVal[] = {0x02, 0x01, 0x01};
const BYTE gSignDataType[] = {0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x07,0x02};

DWORD Pkcs7::ShortSignToPkcs7Sign(CMSG_SIGNER_INFO* pSignerInfo)
{
// Кодируем SignerInfo с помощью CryptoApi
DWORD dwSize = 1000;
byte_buffer Signer(dwSize);
if (!::CryptEncodeObject(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, PKCS7_SIGNER_INFO,
pSignerInfo, Signer.c_ptr(), &dwSize))
{
if (GetLastError() != ERROR_MORE_DATA)
return HandleError(GetLastError());
LongSign.reserve(dwSize);
if (!::CryptEncodeObject(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, PKCS7_SIGNER_INFO,
pSignerInfo, Signer.c_ptr(), &dwSize))
return HandleError(GetLastError());
}
Signer.resize(dwSize);

// Кодируем остальное вручную
idx = 100;
dwSize += static_cast<DWORD>(idx);
LongSign.reserve(dwSize);
LongSign.resize(idx);
idx--;
// SET OF DigestAlgorithmIdentifier
EncodeSize(Signer.size());
LongSign[idx--] = 0x31;
// EncapsulatedContentInfo
AddData(gEncapsulatedContentInfo, sizeof(gEncapsulatedContentInfo));

// DigestAlgorithmIdentifier
size_t sz = idx;
AddData(gNullVal, sizeof(gNullVal)); // algorithm parameter

if (!AddOID(pSignerInfo->HashAlgorithm.pszObjId))
return HandleError();
if (idx < 30)
return HandleError();

sz = sz - idx;

// SEQUENCE { * / 0x30, size of DigestAlgorithmIdentifier,
LongSign[idx--] = (BYTE) sz;
LongSign[idx--] = 0x30;

// digestAlgorithms SET { * / 0x31, size
LongSign[idx--] = (BYTE) (sz + 2);
LongSign[idx--] = 0x31;

// CMSVersion
AddData(gVersionVal, sizeof(gVersionVal));

//SignedData ::= SEQUENCE { * / 0x30, size
EncodeSize(dwSize - idx - 1);
LongSign[idx--] = 0x30;

// content [0]
EncodeSize(dwSize - idx - 1);
LongSign[idx--] = 0xA0;

// contentType
AddData(gSignDataType, sizeof(gSignDataType));

// ContentInfo ::= SEQUENCE {
EncodeSize(dwSize - idx - 1);
LongSign[idx] = 0x30;

::CopyMemory(LongSign.c_ptr(), &LongSign[idx], LongSign.size() - idx);
::CopyMemory(&LongSign[LongSign.size() - idx], Signer.c_ptr(), Signer.size());
LongSign.resize(LongSign.size() - idx + Signer.size());
return 0;
}

// static
DWORD Pkcs7::ShortSignToPkcs7Sign(WinCryptoConfig& config, PCCERT_CONTEXT pCertCxt,
byte_buffer& ShortSign, byte_buffer& LongSign)
{
CMSG_SIGNER_INFO SignerInfo;
::ZeroMemory(&SignerInfo, sizeof(SignerInfo));
SignerInfo.dwVersion = CMSG_SIGNER_INFO_PKCS_1_5_VERSION;
SignerInfo.Issuer = pCertCxt->pCertInfo->Issuer;
SignerInfo.SerialNumber = pCertCxt->pCertInfo->SerialNumber;

CRYPT_ALGORITHM_IDENTIFIER HashAlg = {config.GetHashAlgOID(), {0, 0}};
SignerInfo.HashAlgorithm = HashAlg;
SignerInfo.HashEncryptionAlgorithm = pCertCxt->pCertInfo->SubjectPublicKeyInfo.Algorithm;
// Подпись хэша в обратном должна идти в обратном порядке
byte_buffer ReverseShortSign;
ShortSign.reverse_to(ReverseShortSign);
SignerInfo.EncryptedHash.cbData = ReverseShortSign.size();
SignerInfo.EncryptedHash.pbData = ReverseShortSign.c_ptr();
Pkcs7 pkcs7(LongSign);
return pkcs7.ShortSignToPkcs7Sign(&SignerInfo);
};
07.05.2007 15:56:48UndeadKing
самому реализовывать стандарт не хочется, потому что сразу возникнут вопросы на тему "а правильно ли оно реализовано" и тому подобные... то есть вы хотите сказать, что собрать его просто используя микрософтовские функции - дохлый номер?..
07.05.2007 16:48:33Алексей
Я хочу сказать, что microsoft'овских способов сборки pkcs#7 я не нашел, кроме как кодирования SignerInfo, которая входит в pkcs#7 с помощью EncodeObject. И подозреваю что их нет.
07.05.2007 17:25:11Kirill Sobolev
Документированных нет, недокументированные пока неизвестны :)
07.05.2007 19:23:53UndeadKing
ясно, остается только реализовывать все ручками, а так не хотелось... Жаль =(
Спасибо за ответы =)