15.08.2005 13:52:47"Отрыв" подписи от сообщения Ответов: 4
Дмитрий
Подскажите, пожалуйста, порядок действий - имеется подписанное сообщение, нужно от него "отцепить" подпись так, чтобы после можно было выполнить её проверку (фактически, получить detached signature). Что в каком порядке надо делать?
 
Ответы:
26.08.2005 9:34:26Дмитрий
Такое вообще возможно?
26.08.2005 9:53:38zebra
"Подскажите, пожалуйста, порядок действий - имеется подписанное сообщение, нужно от него "отцепить" подпись так, чтобы после можно было выполнить её проверку (фактически, получить detached signature). Что в каком порядке надо делать?"

По-моему, можно подпись можно и не "оцеплять".
а вообще, смотрите пример singlo.c либо singtsf.c
в singlo.c:
int do_low_verify (char *infile, char *certfile, char *signature_filename, int detached, int Cert_LM)
{
HCRYPTPROV hCryptProv = 0; /* Дескриптор провайдера*/
PCCERT_CONTEXT pUserCert = NULL; /* Сертификат, используемый для проверки ЭЦП*/

DWORD keytype = 0; /* Возвращаемый тип ключа*/
BOOL should_release_ctx = FALSE;
int ret = 0;
BYTE *mem_tbs = NULL;
size_t mem_len = 0;
BYTE *mem_signature = NULL;
size_t signature_len = 0;

HCRYPTMSG hMsg = 0; /* Дескриптор сообщения*/

DWORD cbDecoded;
BYTE *pbDecoded = NULL;
DWORD cbSignerCertInfo = 0;
PCERT_INFO pSignerCertInfo = NULL;
PCCERT_CONTEXT pSignerCertContext = NULL;
PCERT_INFO pSignerCertificateInfo = NULL;
HCERTSTORE hStoreHandle = NULL;
DWORD flags = 0; /* CMSG_DETACHED_FLAG */
/*--------------------------------------------------------------------*/
/* Используем сертификат из файла для инициализации контекста*/

if (! infile) {
fprintf (stderr, "No input file was specified\n");
goto err;
}

if (certfile)
{
if(Cert_LM) pUserCert = read_cert_from_MY(certfile);
else pUserCert = read_cert_from_my(certfile);

if (!pUserCert) {
printf ("Cannot find User certificate: %s\n", certfile);
goto err;
}
/* Программа по заданному сертификату определяет наличие секретного ключа*/
/* и загружает требуемый провайдер.*/
/* Для определения провайдера используется функция CryptAcquireCertificatePrivateKey, */
/* если она присутствует в crypt32.dll. Иначе производистя поиск ключа по сертификату в справочнике.*/
ret = CryptAcquireProvider ("my", pUserCert, &hCryptProv, &keytype, &should_release_ctx);
if (ret) {
printf("A CSP has been acquired. \n");
}
else {
HandleErrorFL("Cryptographic context could not be acquired.");
}
}
else {
fprintf (stderr, "No user cert specified. Cryptocontext will be opened automaticaly.\n");
}

/*--------------------------------------------------------------------*/
/* Прочитаем файл, который будем проверять.*/
ret = get_file_data_pointer (infile, &mem_len, &mem_tbs);
if (!ret) {
fprintf (stderr, "Cannot read input file\n");
goto err;
}

/*--------------------------------------------------------------------*/
/* Прочитаем файл подписи*/
if (detached && !signature_filename)
HandleErrorFL("Signature filename was not specified.");

if (signature_filename && detached) {
ret = get_file_data_pointer (signature_filename, &signature_len, &mem_signature );
flags = CMSG_DETACHED_FLAG;
if (!ret) {
fprintf (stderr, "Cannot read signature file\n");
goto err;
}
}
/*--------------------------------------------------------------------*/
/* Откроем сообщение для декодирования*/

hMsg = CryptMsgOpenToDecode(
TYPE_DER, /* Encoding type.*/
flags, /* Flags.*/
0, /* Use the default message type.*/
hCryptProv, /* Cryptographic provider.*/
NULL, /* Recipient information.*/
NULL); /* Stream information.*/
if (hMsg)
printf("The message to decode is open. \n");
else
HandleErrorFL("OpenToDecode failed");

if (flags == CMSG_DETACHED_FLAG) {
/*--------------------------------------------------------------------*/
/* Если установлен флаг detached, добавим подпись*/
ret = CryptMsgUpdate(
hMsg,
mem_signature,
signature_len,
TRUE);
if (ret)
printf("The signature blob has been added to the message. \n");
else
HandleErrorFL("Decode MsgUpdate failed");

ret = CryptMsgUpdate(
hMsg, /* Handle to the message*/
mem_tbs, /* Pointer to the encoded blob*/
mem_len, /* Size of the encoded blob*/
TRUE);
if (ret)
printf("The encoded blob has been added to the message. \n");
else
HandleErrorFL("Decode MsgUpdate failed");

}
else {
/*--------------------------------------------------------------------*/
/* Поместим в сообщение проверяемые данные*/
ret = CryptMsgUpdate(
hMsg, /* Handle to the message*/
mem_tbs, /* Pointer to the encoded blob*/
mem_len, /* Size of the encoded blob*/
TRUE); /* Last call*/
if (ret)
printf("The encoded blob has been added to the message. \n");
else
HandleErrorFL("Decode MsgUpdate failed");
}

/*--------------------------------------------------------------------*/
/* Определим длину подписанных данных*/

ret = CryptMsgGetParam(
hMsg, /* Handle to the message*/
CMSG_CONTENT_PARAM, /* Parameter type*/
0, /* Signed Index*/
NULL, /* Address for returned info*/
&cbDecoded); /* Size of the returned info*/
if (ret)
printf("The message parameter (CMSG_CONTENT_PARAM) has been acquired. Message size: %d\n", cbDecoded);
else
HandleErrorFL("Decode CMSG_CONTENT_PARAM failed");
/*--------------------------------------------------------------------*/
/* Резервируем память*/

pbDecoded = (BYTE *) malloc(cbDecoded);
if (!pbDecoded)
HandleErrorFL("Decode memory allocation failed");
/*--------------------------------------------------------------------*/
/* Вернем подписанные данные*/

ret = CryptMsgGetParam(
hMsg, /* Handle to the message*/
CMSG_CONTENT_PARAM, /* Parameter type*/
0, /* Signer Index*/
pbDecoded, /* Address for returned info*/
&cbDecoded); /* Size of the returned info*/
if (ret)
printf("The message param (CMSG_CONTENT_PARAM) returned. Length is %lu.\n",cbDecoded);
else
HandleErrorFL("Decode CMSG_CONTENT_PARAM #2 failed");
/*--------------------------------------------------------------------*/
/* Проверка ЭЦП*/
/* Сначала определим информация CERT_INFO об отправителе.*/

/*--------------------------------------------------------------------*/
/* Определеим требуемый размер для структуры*/
/* попробуем определим сертификат из сообщения*/

if (! pUserCert) {
ret = CryptMsgGetParam(
hMsg, /* Handle to the message*/
CMSG_SIGNER_CERT_INFO_PARAM, /* Parameter type*/
0, /* Signer Index*/
NULL, /* Address for returned info*/
&cbSignerCertInfo); /* Size of the returned info*/
if (ret)
printf("Try to get user cert. OK. Length %d.\n",cbSignerCertInfo);
else {
printf("No user certificate found in message.\n");
}
}

/*--------------------------------------------------------------------*/
/* Если сертификат задан параметром вызова функции, */
/* создадим справочник в памяти с этим сертификатом.*/
/* Это сделано только для того, чтобы затем вернуть сертификат функцией */
/* CertGetSubjectCertificateFromStore, которая также используется, если*/
/* сертификат отправителя находится в самом сообщении.*/
if (pUserCert) {
hStoreHandle = CertOpenStore(CERT_STORE_PROV_MEMORY, TYPE_DER, 0, CERT_STORE_CREATE_NEW_FLAG,NULL);
if (!hStoreHandle)
HandleErrorFL("Cannot create temporary store in memory.");
/* Добавим сертификат в справочник*/
if (pUserCert) {
ret = CertAddCertificateContextToStore(hStoreHandle, pUserCert, CERT_STORE_ADD_ALWAYS, NULL);
pSignerCertInfo = pUserCert->pCertInfo;
}
else
ret = 0;
if (!ret)
HandleErrorFL("Cannot add user certificate to store.");
}

/*--------------------------------------------------------------------*/
/* Если сертификат не задан, зарезервируем память*/

if (!pUserCert) {
pSignerCertInfo = (PCERT_INFO) malloc(cbSignerCertInfo);
if (!pSignerCertInfo)
HandleErrorFL("Verify memory allocation failed");
}

/*--------------------------------------------------------------------*/
/* Попробуем извлечь CERT_INFO из сообщения (если сертификат не задан).*/

if (! pUserCert) {
ret = CryptMsgGetParam(
hMsg, /* Handle to the message*/
CMSG_SIGNER_CERT_INFO_PARAM, /* Parameter type*/
0, /* Signer Index*/
pSignerCertInfo, /* Address for returned info*/
&cbSignerCertInfo); /* Size of the returned info*/
if (ret)
printf("The signer info has been returned. \n");
else
HandleErrorFL("Verify SIGNER_CERT_INFO #2 failed");
}
/*--------------------------------------------------------------------*/
/* Если сертификат не задан и не содан временный справочник в памяти*/
/* создадим справочник, используя сообщение (CERT_STORE_PROV_MSG),*/
/* который инициализируем сертификатом из сообщения.*/

if (! hStoreHandle) {
hStoreHandle = CertOpenStore(
CERT_STORE_PROV_MSG, /* Store provider type */
TYPE_DER, /* Encoding type*/
hCryptProv, /* Cryptographic provider*/
0, /* Flags*/
hMsg); /* Handle to the message*/
if (hStoreHandle)
printf("The message certificate store be used for verifying\n");
}

if (! hStoreHandle) {
HandleErrorFL("Cannot open certificate store form message\n");
}
/*--------------------------------------------------------------------*/
/* Найдем сертификат отправителя в справочнике*/

pSignerCertContext = CertGetSubjectCertificateFromStore(
hStoreHandle, /* Handle to store*/
TYPE_DER, /* Encoding type*/
pSignerCertInfo);
if(pSignerCertContext) /* Pointer to retrieved CERT_CONTEXT*/
{
printf("A signer certificate has been retrieved. \n");
}
else
{
HandleErrorFL("Verify GetSubjectCert failed");
}

/*--------------------------------------------------------------------*/
/* Используя структуру CERT_INFO проверяем ЭЦП сообщения*/

pSignerCertificateInfo = pSignerCertContext->pCertInfo;
if(CryptMsgControl(
hMsg, /* Handle to the message*/
0, /* Flags*/
CMSG_CTRL_VERIFY_SIGNATURE, /* Control type*/
pSignerCertificateInfo)) /* Pointer to the CERT_INFO*/
{
printf("\nSignature was VERIFIED.\n");
}
else
{
printf("\nThe signature was NOT VEIFIED.\n");
}


/*--------------------------------------------------------------------*/
/* Попробуем извлечь время формирования ЭЦП (для первой подписи) из сообщения.*/
/* Аналигично можно сделать для всех подписей сообщения.*/

ret = get_signing_time (hMsg, 0);

if(hStoreHandle)
CertCloseStore(hStoreHandle, CERT_CLOSE_STORE_FORCE_FLAG);

err:
if(hMsg)
CryptMsgClose(hMsg);
if(hCryptProv && should_release_ctx)
CryptReleaseContext(hCryptProv,0);

release_file_data_pointer (mem_tbs);
release_file_data_pointer (mem_signature);
return ret;

}

................а в singtsf.c
static int do_verify (char *infile, char *certfile, char *outfile, char *signature_filename, int detached, int ask, int base64, int Cert_LM)
{
HCRYPTPROV hCryptProv = 0; /* Дескриптор провайдера*/
BOOL should_release_ctx = FALSE;

PCCERT_CONTEXT pUserCert = NULL; /* Сертификат, используемый для проверки ЭЦП*/

DWORD keytype = 0;
int ret = 0;
int mem_tbs_need_free = 0;
BYTE *mem_tbs = NULL;
size_t mem_len = 0;

int mem_signature_need_free = 0;
BYTE *mem_signature = NULL;
size_t signature_len = 0;

CRYPT_VERIFY_MESSAGE_PARA param;
DWORD MessageSizeArray[1];
const BYTE* MessageArray[1];

DWORD signed_len = 0;
BYTE *signed_mem = NULL;

/*--------------------------------------------------------------------*/
/* Используем сертификат из файла для инициализации контекста*/

if (! infile) {
DebugErrorFL("No input file was specified");
goto err;
}
if (detached && signature_filename == NULL) {
DebugErrorFL("No detached signature file was specified");
goto err;
}

if (certfile)
{
if(Cert_LM) pUserCert = read_cert_from_MY(certfile);
else pUserCert = read_cert_from_my(certfile);

if (!pUserCert) {
DebugErrorFL("read_cert_from_my");
printf ("Cannot find User certificate: %s\n", certfile);
goto err;
}
/* Программа по заданному сертификату определяет наличие секретного ключа*/
/* и загружает требуемый провайдер.*/
/* Для определения провайдера используется функция CryptAcquireCertificatePrivateKey, */
/* если она присутствует в crypt32.dll. Иначе производистя поиск ключа по сертификату в справочнике.*/
if (ask) {
ret = CryptAcquireProvider ("my", pUserCert, &hCryptProv, &keytype, &should_release_ctx);
if (ret) {
printf("A CSP has been acquired. \n");
}
else {
HandleErrorFL("Cryptographic context could not be acquired.");
}
}
}
else {
DebugErrorFL("No user cert specified. Cryptocontext will be opened automaticaly.");
}

/*--------------------------------------------------------------------*/
/* Прочитаем файл, который будем проверять.*/
ret = get_file_data_pointer (infile, &mem_len, &mem_tbs);
if (!ret) {
DebugErrorFL("Cannot read input file");
goto err;
}

if (base64 && !detached) {
BYTE *pbDer;
DWORD cbDer;

cbDer = mem_len;
pbDer = malloc(mem_len);
if (!base64_decode(mem_tbs, mem_len, pbDer, &cbDer) ||
cbDer >= mem_len) {
HandleErrorFL("Base64 conversion error");
goto err;
} else {
release_file_data_pointer(mem_tbs);
mem_tbs = pbDer;
mem_tbs_need_free = 1;
mem_len = cbDer;
}
}

/*--------------------------------------------------------------------*/
/* Прочитаем файл подписи*/
if (detached) {
if (signature_filename) {
ret = get_file_data_pointer (signature_filename, &signature_len, &mem_signature );
if (!ret) {
DebugErrorFL("Cannot read signature file");
goto err;
}
}
if (base64) {
BYTE *pbDer;
DWORD cbDer;

cbDer = signature_len;
pbDer = malloc(signature_len);
if (!base64_decode(mem_signature, signature_len, pbDer, &cbDer) ||
cbDer >= signature_len) {
HandleErrorFL("Base64 conversion error");
goto err;
} else {
release_file_data_pointer(mem_signature);
mem_signature = pbDer;
mem_signature_need_free = 1;
signature_len = cbDer;
}
}
}

/*--------------------------------------------------------------------*/
/* Установим параметры структуры CRYPT_VERIFY_MESSAGE_PARA */

memset(&param, 0, sizeof(CRYPT_VERIFY_MESSAGE_PARA));
param.cbSize = sizeof(CRYPT_VERIFY_MESSAGE_PARA);
param.dwMsgAndCertEncodingType = TYPE_DER;

param.hCryptProv = hCryptProv;
if(Cert_LM)
param.pfnGetSignerCertificate = global_MY_get_cert;
else
param.pfnGetSignerCertificate = global_my_get_cert; /* этот callback должен возвернуть сертификат на котором проверяем сообщение*/
param.pvGetArg = (void*) certfile; /* передадим имя файла сертификата в функцию*/

/*------------------------------------------------*/
/* Различные ветки для проверки ЭЦП*/
/* Для проверки detached используется функция CryptVerifyDetachedMessageSignature*/
/* В противном случае используется CryptVerifyMessageSignature*/

if (detached == 0) {
DWORD dwSignerIndex = 0; /* Используется вцикле если подпись не одна.*/
/* Пока только 0*/
signed_mem = (BYTE*)malloc(signed_len = mem_len);
if (!signed_mem) {
HandleErrorFL("Memory allocation error allocating decode blob.");
}

ret = CryptVerifyMessageSignature(
&param,
dwSignerIndex,
mem_tbs, /* подписанное сообщение*/
mem_len, /* длина*/
signed_mem, /* если нужно сохранить вложение BYTE *pbDecoded,*/
&signed_len, /* куда сохраняет вложение DWORD *pcbDecoded,*/
NULL); /* возвращаемый сертификат на котором проверена ЭЦП (PCCERT_CONTEXT *ppSignerCert)*/

if (ret) {
printf("Signature was verified OK\n");
}
else
{
HandleErrorFL("Signature was NOT verified\n");
goto err;
}

/* запись вложения в файл*/
if (outfile && signed_mem && signed_len) {
if (write_file (outfile, signed_len, signed_mem))
printf ("Output file (%s) has been saved\n", outfile);
}
} else { /* detached подпись*/

DWORD dwSignerIndex = 0; /* Используется вцикле если подпись не одна.*/

MessageArray[0] = mem_tbs;
MessageSizeArray[0] = mem_len;

/* Проверка ЭЦП*/
ret = CryptVerifyDetachedMessageSignature(
&param,
dwSignerIndex,
(const BYTE*) mem_signature, /* detached signature*/
signature_len, /* ее длина*/
1, /* количество проверяемых исходных файлов*/
MessageArray, /* список исходных файлов*/
MessageSizeArray, /* список размеров исходных файлов*/
&pUserCert); /* возвращаемый сертификат на котором проверена ЭЦП (PCCERT_CONTEXT *ppSignerCert)*/

if (ret) {
printf("Detached Signature was verified OK\n");
}
else
{
HandleErrorFL("Detached Signature was NOT verified\n");
}
}


err:
if (mem_signature_need_free) {
free(mem_signature);
} else {
release_file_data_pointer(mem_signature);
}
mem_signature = NULL;
if (mem_tbs_need_free) {
free(mem_tbs);
} else {
release_file_data_pointer(mem_tbs);
}
mem_tbs = NULL;
if (signed_mem) {
free(signed_mem);
signed_mem = NULL;
}
if (should_release_ctx) CryptReleaseContext(hCryptProv, 0);
return ret;
}
26.08.2005 10:06:32Дмитрий
К сожалению, это не то, что требуется. Проверку подписи вполне можно осуществить итак. Но у меня вопрос стоит именно о раздельном *хранении* документа и его подписей. А входными данными является именно файл с attached signature. Т.е. мне требуется именно "растащить" на кусочки файл так, чтобы потом подписи можно было проверить.
10.09.2005 22:21:19SiM
На самом деле попробуйте поиграть с CryptMsgOpenToDecode и CryptMsgUpdate, так как в ASN.1 отличия detached от attached минимальны (флаг отделения данных и собственно данные). В OpenSSL это делается просто, через CryptMsg скорее всего тоже возможно