| ||||
| ||||
Подскажите, пожалуйста, порядок действий - имеется подписанное сообщение, нужно от него "отцепить" подпись так, чтобы после можно было выполнить её проверку (фактически, получить detached signature). Что в каком порядке надо делать? | ||||
Ответы: | ||||
| ||||
Такое вообще возможно? | ||||
| ||||
"Подскажите, пожалуйста, порядок действий - имеется подписанное сообщение, нужно от него "отцепить" подпись так, чтобы после можно было выполнить её проверку (фактически, получить 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(¶m, 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( ¶m, 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( ¶m, 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; } | ||||
| ||||
К сожалению, это не то, что требуется. Проверку подписи вполне можно осуществить итак. Но у меня вопрос стоит именно о раздельном *хранении* документа и его подписей. А входными данными является именно файл с attached signature. Т.е. мне требуется именно "растащить" на кусочки файл так, чтобы потом подписи можно было проверить. | ||||
| ||||
На самом деле попробуйте поиграть с CryptMsgOpenToDecode и CryptMsgUpdate, так как в ASN.1 отличия detached от attached минимальны (флаг отделения данных и собственно данные). В OpenSSL это делается просто, через CryptMsg скорее всего тоже возможно | ||||