31.10.2005 12:47:58Проверка подписи сообщения Ответов: 18
Алексей
Делаю проверку подписи с помощью низкоуровневых функций (само сообщение в формате PKCS#7). Вот с чем столкнулся - получаю открытый ключ получателя из этого сообщения. Но CryptImportKey его не хочет принимать ("Плохие данные"). Потом я посмотрел что выдает CryptExportKey и обнаружил, что открытого ключа эти данные не содержат. Точнее там содержится какая-то структура. Дак вот, как я могу импортировать открытый ключ, имея только его? Или мне необходимо, чтобы для проверки подписи был установлен в хранилище сертификат открытого ключа?
 
Ответы:
31.10.2005 13:00:16Алексей
Вот еще один вопрос по этой теме:
Даже если я импортирую ключ, полученный с помощьюCryptExportKey, то у меня выдается "Неправильная подпись".
Вот код, которым я проверяю подпись:
CryptAcquireContext(&hCryptProv, NULL, csp_name, csp_type, CRYPT_VERIFYCONTEXT);
if (!CryptImportKey(hCryptProv, (BYTE*)pubkey, ps, 0, 0, &hPubKey))
{
WriteLog("Не могу импортировать открытый ключ.");
}

if (!CryptCreateHash(hCryptProv, algid, 0, 0, &hHash))
{
WriteLog("Не могу создать хэш-объект.");
}

if(kl && !CryptHashData(hHash, (BYTE*)data, ds, 0))
{ WriteLog("Не могу хэшировать данные.");
}

if(CryptVerifySignature(hHash, (BYTE*)sign, ss, hPubKey, NULL, 0))
{
WriteLog("Подпись верифицирована.");
}
else
{
WriteLog("Подпись не верифицирована.");
}
В pubkey содержатся выходные данные CryptExportKey; data - данные, которые подписывались; sign - зашифрованный хэш.
01.11.2005 10:06:22Kirill Sobolev
Экспортируете как PUBLICKEYBLOB? А перед экспортом подпись проверяется?
По поводу 1го вопроса - не совсем ясно как вы из PKCS7 получаете открытый ключ, но скорее всего Вам поможет CryptImportPublicKeyInfo.
01.11.2005 12:52:55Алексей
По первому вопросу - просто вытаскиваю этот ключ из PKCS#7-сообщения. Так и не разобрался как импортировать этот ключ. Вот что я делаю:
char *pubkey = NULL;
//тут считываю в pubkey открытый ключ из сообщения. этот ключ сходится с тем, который указан в сертификате открытого ключа
memset(&pInfo, 0, sizeof(CERT_PUBLIC_KEY_INFO));
pInfo.Algorithm.pszObjId = oid;
pInfo.PublicKey.cbData = ps;
pInfo.PublicKey.pbData = (BYTE *) pubkey;
if (!CryptImportPublicKeyInfo(hCryptProv, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, &pInfo, &hPubKey))
{
WriteLog("Не могу импортировать открытый ключ.");
}
Выдается сообщение "Встречено неверное значение тега ASN1".

Теперь относительно самой проверки подписи. На время эксперимента, я просто экспортировал открытый ключ в файл. Если я подписываю данные низкоуровневыми функциями, а затем проверяю подпись также низкоуровневыми функциями, то все нормально. Если же я подписываю данные с помощью функции CryptSignMessage, а проверку делаю функциями CryptCreateHash, CryptHashData и т.д., то мне выдается сообщение, что подпись неверна. И еще, когда я сформировал подпись низкоуровневыми функциями, то оказалось, что данной последовательности байтов (зашифрованный хэш) не содержится в PKCS#7-сообщении, полученном из CryptSignMessage. Как это можно понять?
01.11.2005 12:59:33Алексей
Вот что еще хотел уточнить. Я просмотрел с помощью программы dumpasn1 данные, которые получил с помощью функции CryptSignMessage:
0 30 5217: SEQUENCE {
4 06 9: OBJECT IDENTIFIER ’1 2 840 113549 1 7 2’
15 A0 5202: [0] {
19 30 5198: SEQUENCE {
23 02 1: INTEGER 1
26 31 12: SET {
28 30 10: SEQUENCE {
30 06 6: OBJECT IDENTIFIER ’1 2 643 2 2 9’
38 05 0: NULL
: }
: }
40 30 4044: SEQUENCE {
44 06 9: OBJECT IDENTIFIER ’1 2 840 113549 1 7 1’
55 A0 4029: [0] {
59 04 4025: OCTET STRING
: ’================================================’
: ’================================.. MICROSOFT ’
: ’FOUNDATION CLASS LIBRARY : Shifr Project Overvie’
: ’w..=============================================’
: ’==================================....The applic’
: ’ation wizard has created this Shifr application ’
: ’for ..you. This application not only demonstrat’
: ’es the basics of using the Microsoft ..Foundatio’
: [ Another 3641 characters skipped ]
: }
: }
4088 A0 903: [0] {
4092 30 899: SEQUENCE {
4096 30 816: SEQUENCE {
4100 A0 3: [0] {
4102 02 1: INTEGER 2
: }
4105 02 10: INTEGER
: 79 8C DD 8A 00 00 00 00 35 D4
4117 30 10: SEQUENCE {
4119 06 6: OBJECT IDENTIFIER ’1 2 643 2 2 3’
4127 05 0: NULL
: }
4129 30 111: SEQUENCE {
4131 31 30: SET {
4133 30 28: SEQUENCE {
4135 06 9: OBJECT IDENTIFIER ’1 2 840 113549 1 9 1’
4146 16 15: IA5String ’’
: }
: }
4163 31 11: SET {
4165 30 9: SEQUENCE {
4167 06 3: OBJECT IDENTIFIER ’2 5 4 6’
4172 13 2: PrintableString ’RU’
: }
: }
4176 31 21: SET {
4178 30 19: SEQUENCE {
4180 06 3: OBJECT IDENTIFIER ’2 5 4 7’
4185 13 12: PrintableString ’’
: }
: }
4199 31 19: SET {
4201 30 17: SEQUENCE {
4203 06 3: OBJECT IDENTIFIER ’2 5 4 10’
4208 13 10: PrintableString ’’
: }
: }
4220 31 20: SET {
4222 30 18: SEQUENCE {
4224 06 3: OBJECT IDENTIFIER ’2 5 4 3’
4229 13 11: PrintableString ’’
: }
: }
: }
4242 30 30: SEQUENCE {
4244 17 13: UTCTime ’041005041600Z’
4259 17 13: UTCTime ’051005042500Z’
: }
4274 30 272: SEQUENCE {
4278 31 35: SET {
4280 30 33: SEQUENCE {
4282 06 9: OBJECT IDENTIFIER ’1 2 840 113549 1 9 1’
4293 16 20: IA5String ’’
: }
: }
4315 31 11: SET {
4317 30 9: SEQUENCE {
4319 06 3: OBJECT IDENTIFIER ’2 5 4 6’
4324 13 2: PrintableString ’RU’
: }
: }
4328 31 31: SET {
4330 30 29: SEQUENCE {
4332 06 3: OBJECT IDENTIFIER ’2 5 4 7’
4337 1E 22: BMPString ’’
: }
: }
4361 31 53: SET {
4363 30 51: SEQUENCE {
4365 06 3: OBJECT IDENTIFIER ’2 5 4 10’
4370 1E 44: BMPString
: ’’
: }
: }
4416 31 19: SET {
4418 30 17: SEQUENCE {
4420 06 3: OBJECT IDENTIFIER ’2 5 4 3’
4425 13 10: PrintableString ’’
: }
: }
4437 31 48: SET {
4439 30 46: SEQUENCE {
4441 06 9: OBJECT IDENTIFIER ’1 2 840 113549 1 9 2’
4452 13 33: PrintableString ’’
: }
: }
4487 31 61: SET {
4489 30 59: SEQUENCE {
4491 06 3: OBJECT IDENTIFIER ’2 5 4 4’
4496 1E 52: BMPString
: ’’
: ’.0’
: }
: }
: }
4550 30 99: SEQUENCE {
4552 30 28: SEQUENCE {
4554 06 6: OBJECT IDENTIFIER ’1 2 643 2 2 19’
4562 30 18: SEQUENCE {
4564 06 7: OBJECT IDENTIFIER ’1 2 643 2 2 36 0’
4573 06 7: OBJECT IDENTIFIER ’1 2 643 2 2 30 1’
: }
: }
4582 03 67: BIT STRING 0 unused bits, encapsulates {
4585 04 64: OCTET STRING
: D3 F5 27 BF 62 EE 89 20 8F E6 4E F9 D2 48 C1 52
: D9 D5 4E A4 01 A3 35 59 ED EB 58 94 3D E1 D2 C7
: B3 1E 42 BF 0D 2A 77 A3 31 40 77 D3 F7 D8 1B C2
: 0A A8 45 01 DF 53 7C 34 7A 5B 8F AF C2 5C 0E 23
: }
: }
4651 A3 261: [3] {
4655 30 257: SEQUENCE {
4659 30 14: SEQUENCE {
4661 06 3: OBJECT IDENTIFIER ’2 5 29 15’
4666 01 1: BOOLEAN TRUE
4669 04 4: OCTET STRING, encapsulates {
4671 03 2: BIT STRING 4 unused bits
: ’1111’B
: }
: }
4675 30 46: SEQUENCE {
4677 06 3: OBJECT IDENTIFIER ’2 5 29 37’
4682 04 39: OCTET STRING, encapsulates {
4684 30 37: SEQUENCE {
4686 06 8: OBJECT IDENTIFIER ’1 3 6 1 5 5 7 3 2’
4696 06 8: OBJECT IDENTIFIER ’1 3 6 1 5 5 7 3 4’
4706 06 7: OBJECT IDENTIFIER ’1 2 643 2 2 34 6’
4715 06 6: OBJECT IDENTIFIER ’1 2 643 3 7 1’
: }
: }
: }
4723 30 29: SEQUENCE {
4725 06 3: OBJECT IDENTIFIER ’2 5 29 14’
4730 04 22: OCTET STRING, encapsulates {
4732 04 20: OCTET STRING
: B0 7F 9F 24 D8 8D C7 0C 23 4C 25 BE 69 2C C6 AD
: 34 6C 87 70
: }
: }
4754 30 31: SEQUENCE {
4756 06 3: OBJECT IDENTIFIER ’2 5 29 35’
4761 04 24: OCTET STRING, encapsulates {
4763 30 22: SEQUENCE {
4765 80 20: [0]
: 48 01 19 9F C4 DF 2B 64 7C FC 45 E3 5B 2F AF 02
: 81 C8 C5 94
: }
: }
: }
4787 30 59: SEQUENCE {
4789 06 3: OBJECT IDENTIFIER ’2 5 29 31’
4794 04 52: OCTET STRING, encapsulates {
4796 30 50: SEQUENCE {
4798 30 48: SEQUENCE {
4800 A0 46: [0] {
4802 A0 44: [0] {
4804 86 42: [6]
: ’’
: }
: }
: }
: }
: }
: }
4848 30 66: SEQUENCE {
4850 06 8: OBJECT IDENTIFIER ’1 3 6 1 5 5 7 1 1’
4860 04 54: OCTET STRING, encapsulates {
4862 30 52: SEQUENCE {
4864 30 50: SEQUENCE {
4866 06 8: OBJECT IDENTIFIER ’1 3 6 1 5 5 7 48 2’
4876 86 38: [6] ’’
: }
: }
: }
: }
: }
: }
: }
4916 30 10: SEQUENCE {
4918 06 6: OBJECT IDENTIFIER ’1 2 643 2 2 3’
4926 05 0: NULL
: }
4928 03 65: BIT STRING 0 unused bits
: E6 F0 FE CA 95 F8 D8 85 1E B4 7D 18 17 D4 72 5C
: 02 FB 2B 71 20 5C D8 53 07 F6 80 6F C0 1A 44 2A
: D3 B3 57 09 D0 89 77 FD B3 0D F9 22 86 E7 69 2C
: 03 A4 E9 21 93 F5 0A 0C 35 E9 47 32 61 A5 04 DC
: }
: }
4995 31 223: SET {
4998 30 220: SEQUENCE {
5001 02 1: INTEGER 1
5004 30 125: SEQUENCE {
5006 30 111: SEQUENCE {
5008 31 30: SET {
5010 30 28: SEQUENCE {
5012 06 9: OBJECT IDENTIFIER ’1 2 840 113549 1 9 1’
5023 16 15: IA5String ’’
: }
: }
5040 31 11: SET {
5042 30 9: SEQUENCE {
5044 06 3: OBJECT IDENTIFIER ’2 5 4 6’
5049 13 2: PrintableString ’RU’
: }
: }
5053 31 21: SET {
5055 30 19: SEQUENCE {
5057 06 3: OBJECT IDENTIFIER ’2 5 4 7’
5062 13 12: PrintableString ’’
: }
: }
5076 31 19: SET {
5078 30 17: SEQUENCE {
5080 06 3: OBJECT IDENTIFIER ’2 5 4 10’
5085 13 10: PrintableString ’’
: }
: }
5097 31 20: SET {
5099 30 18: SEQUENCE {
5101 06 3: OBJECT IDENTIFIER ’2 5 4 3’
5106 13 11: PrintableString ’’
: }
: }
: }
5119 02 10: INTEGER
: 79 8C DD 8A 00 00 00 00 35 D4
: }
5131 30 10: SEQUENCE {
5133 06 6: OBJECT IDENTIFIER ’1 2 643 2 2 9’
5141 05 0: NULL
: }
5143 30 10: SEQUENCE {
5145 06 6: OBJECT IDENTIFIER ’1 2 643 2 2 19’
5153 05 0: NULL
: }
5155 04 64: OCTET STRING
: 5E 10 01 81 A5 D1 A8 D5 99 8A 2D 3A 05 8C FA 81
: 76 77 66 1E 7C AC B4 25 08 32 2C 9D 5E 93 D6 9F
: 1C 53 3C F6 56 04 D4 82 30 DB B4 C2 1A C1 66 F9
: 02 75 EE CA DE D8 7D 20 EB 60 D8 C3 A3 2B 23 1C
: }
: }
: }
: }
: }
Правильно ли я понимаю, что зашифрованный хэш - это последний OCTET STRING?
01.11.2005 13:43:59Kirill Sobolev
1)Как именно Вы считываете ОК из сообщения?
2)Как Вы вытаскиваете хэш из PKCS7 для проверки?
3)Зашифрованный хэш ОБЯЗАТЕЛЬНО содержится в PKCS7. Почему Вы решили что его там нет? :)
4)Да, совершенно верно.
01.11.2005 14:00:13Алексей
1. и 2. - Я просто вытаскиваю последовательность байтов из PKCS#7-сообщения, т.е. своими функциями я считываю данные из этого сообщения. Причем, считанные мною данные эквивалентны данным, которые показывает dumpasn1, за исключением того, что я представляю открытыый ключ (D3 F5 27...) и зашифрованный хэш (5E 10 01...) не в 16-й системе счисления, а в виде последовательности байтов, как они и шли в исходном сообщении.
3. - Уже сам осознал, что глупость сморозил :) Но: я низкоуровневыми функциями сформировал зашифрованную хэш-последовательность и попробовал найти ее в PKCS#7-сообщении (просто открыл в блокноте это сообщение/файл и через поиск попробовал искать). Или это мое заблуждение, что так можно найти хэш?
01.11.2005 14:12:13Kirill Sobolev
Не надо поля PKCS7 вытаскивать своими функциями :) Есть набор низкоуровневых функций CryptMsg* которые позволяют все это сделать.
Кстати можете попробовать ими и сравнить с результатом, который возвращает Ваш разбор.
Найти можно, но тут надо иметь ввиду такой факт - если 2 раза подписать один и тот же текст гостовой подписью с одним и тем же ключем - получатся разные подписи.
01.11.2005 14:44:52Алексей
Я знаю, что есть низкоуровневые функции для работы с PKCS#7 сообщениями, но мне пришлось от них отказаться по некоторым причинам. Я вот заметил, что хэш получается разным. А как тогда проверяет подпись, например, CryptVerifyMessage. Я понимаю, что с помощью CryptMsg***, но ведь эти функции основаны на самых низкоуровневых криптографических функциях. И как-то ведь проверяется подпись, сформированная с ГОСТовским алгоритмом.
И еще: что значит "подписать готовой подписью"?
01.11.2005 15:12:32Kirill Sobolev
Если не вдаваться в математику, то проверяются след. образом: расшифровывается зашифрованный хэш, еще один хэш считается от сообщения и они сраниваются.
"подписать гостовой подписью" - я имел ввиду по алгоритму ГОСТ Р3411/Р3410.
01.11.2005 15:13:24Алексей
Извените, не так прочитал (я про "готовую" подпись :).
01.11.2005 15:26:50Алексей
В принципе это я так и представлял :)
Но ведь не суть каким функциями я пользуюсь - CryptVerifyMessage или CryptVerifySignature. Конечный результат ведь должен быть одним и тем же.
У меня все-равно первостепенная задача - импортировать открытый ключ. Пожалуйста, подскажите что можно сделать с этим.
01.11.2005 15:46:13Kirill Sobolev
Конечно одним и тем же.
Я бы воспользовался функциями CryptMsg*.
dumpasn1 показывает раскодированный ASN.1 ОК (что логично), в то время как CryptImportPublicKeyInfo хочет закодированный.
01.11.2005 16:22:58Алексей
Смотрите, если я буду пользоваться CryptMsg**, то: что будет, если я буду открывать сообщение (CryptMsgOpenToDecode) не передавая криптопровайдер. Просто у меня выдается "Неизвестный криптографический алгоритм" (или что-то в этом роде - я уже не помню). Именно по этим причинам я и отказался от этих функций.
01.11.2005 16:29:44Kirill Sobolev
А туда и не надо передавать хэндл провайдера, без этого работает, если конечно провайдер присутствует в системе.
01.11.2005 18:46:25Алексей
Нет, Вы не правы. Если в системе установлено несколько криптопровайдеров, то эти функции попросту не работают - "Указан неправильный алгоритм".
02.11.2005 10:11:28Kirill Sobolev
Работают... посмотрите наш csptest для примера.
02.11.2005 12:57:40Алексей
Я ничего не говорил про ваш пример. Конечно, он будет работать с Вашим криптопровайдером. Но я же сказал, что эти функции не работают, когда на компьютере стоит два или более российских криптопровайдера. Если бы все было так просто, то я бы и не поднимал эту тему :)
02.11.2005 15:48:21Алексей
И так, продолжим нашу беседу :)
На компьютере стоит несколько российских криптопровайдеров, КриптоПРО установлен последним. Функции CryptMsg** и CryptVerifyMessageSignature не работают. Поэтому я вручную вытаскиваю зашифрованный хэш из сообщения, данные, которые подписывались. Заново формирую хэш, потом вызываю функцию проверки подписи (код этого момента указан во втором сообщении). Хэш, который я сам вытаскиваю из сообщения совпадает с хэшем, который возвращает функция CryptMsgGetParam. В чем может быть причина того, что функция CryptVerifySignature возвращает "Неправильная подпись". Заранее благодарен.