Коллега, надпись "...здесь правильный сертификат..." не поможет проверить значение подписи. По факту нет описанной процедуры для получения хэша гост-2012 из значения подписи, есть только проверка с участием открытого ключа сертификата.
Суть проверки в том, что значение подписи делиться на случайно выбранное число и проверочное значение, после операций с их участием (плюс открытого ключа и значения хэша) должно снова получиться проверочное значение. Не получилось - выдается "Неправильная ЭП" (неправильная математически). Причина может быть в любом из участвующих значений (не тот хэш, не тот ключ, не то проверочное число, перевернуто значение подписи). Из-за разного случайного значения значение подписи будет разное (если использовать КриптоПро или другие сертифицированные СКЗИ). Своя же реализация подписания гост-2012, естественно может "чисто случайно" выбирать одинаковое "случайное" значение.
Если нет желания светить "боевой" сертификат на форуме, для тестов пойдет и тестовый сертификат. Или можно прикрепить только открытый ключ из сертификата (СМЭВ это не примет из-за более тонкой проверки сертификата, но вообще стандартом допустимо указание открытого ключа вместо сертификата и есть давний черновик RFC с учетом специфики гост).
Фрагменты лучше оформлять кнопочкой над сообщением "Выберите тип подсветки кода" - Plain Text, без этого форум их может исказить. В идеале - сохранить фрагменты в отдельные txt файлы и прикрепить архивом.
По исходному коду вроде бы не вижу ничего критичного, для AT_KEYEXCHANGE должно работать. Получения значения хэша, к слову, там нет. Мысли по улучшению после отладки:
Код:if (!pKeyInfo->dwKeySpec == AT_KEYEXCHANGE) {
LogLastError("NOT AT_KEYEXCHANGE");
return false;
}
...
DWORD cbSignature;
if (!CryptSignHash(hHash, AT_KEYEXCHANGE, NULL, 0, NULL, &cbSignature)) {
LogLastError("Error CryptSignHash");
return false;
}
BYTE* pbSignature = new BYTE[cbSignature];
if (!CryptSignHash(hHash, AT_KEYEXCHANGE, NULL, 0, pbSignature, &cbSignature)) {
LogLastError("Error CryptSignHash");
return false;
}
Если все равно получили pKeyInfo->dwKeySpec, то более универсально не вываливаться с ошибкой при неравенстве AT_KEYEXCHANGE, а использовать pKeyInfo->dwKeySpec вместо AT_KEYEXCHANGE в CryptSignHash. Кроме того, можно учитывать это при установке PP_KEYEXCHANGE_PIN/PP_SIGNATURE_PIN.
Шаг с вычислением размера хэша/подписи можно пропускать - для известного алгоритма гост и на самом нижнем уровне CryptoApi CryptSignHash (такая подпись также известна как RawSignature) они не меняются.
Алгоритм хэша желательно указывать без жесткого прописывания в коде константы - подстраивать под алгоритм открытого ключа в сертификате.
Получение HP_OID для гост-2012 не очень актуально. В том смысле, что алгоритм гост-94 имел параметры и набор их значений описывался оидом, но гост-2012 параметров не имеет, все константы заданы прямо в стандарте. HP_OID для гост-2012 возвращается, но всегда имеет фиксированное значение (для определенной длины и dwKeySpec), попытка изменения его возвращает ошибку.
Вообще мне кажется, что тут есть немного "хождения кругами" в логике "контейнер - сертификат и обратно". В своей программе я не использую имя контейнера как входной параметр, сразу передается отпечаток сертификата, сертификат ищется в хранилище Личные потом получается по ссылке связанный с сертификатом контейнер. В частности, это позволяет использовать контейнер без установленного сертификата.
UPD: Скопировал каноникализированное SignedInfo из сообщения выше в файл и у меня другой хэш. Полагаю, дело именно в нем:
F18A1CC23BE1F1D8E3D605427B8324DF56AAEEFC58949BF250C9FEAEE32293B0
8Yocwjvh8djj1gVCe4Mk31aq7vxYlJvyUMn+ruMik7A=
FLIP RESULT:
B09322E3AEFEC950F29B9458FCEEAA56DF24837B4205D6E3D8F1E13BC21C8AF1
sJMi467+yVDym5RY/O6qVt8kg3tCBdbj2PHhO8IcivE=
Вставил свой сертификат вместо "...здесь правильный сертификат...", естественно ошибка проверки вышла:
Код:...
Canonic time (si):933 msec
urn:ietf:params:xml:ns:cpxmlsec:algorithms:gostr34102012-gostr34112012-256
di=2 Len=88 Prov=1 key=1
Base64 decode...
di=2 Len=64 Prov=1 key=1
CS ======================================
GetHashInternalKeyed_ 32801
RAW Public Key Info: 24
1.2.643.7.1.1.1.1
3013 0607 2A85 0302 0224 0006 082A 8503 0701 0102 02
0440 F98A 361B 84BC 35BC 9F31 7A68 2F01 4BF4 0059 2900 FDD9 53FC B6F2 DBE9 CCC0
3992 130C 8F41 4377 E0F2 A7CE E249 B2DB 72FB C379 E238 4512 18D7 5F7F 04BC 6464
B499
GetPubkeyFromCert_ 00000000 3086184
<ds:SignedInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#"><ds:Canonicalizatio
nMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"></ds:Canonicalizatio
nMethod><ds:SignatureMethod Algorithm="urn:ietf:params:xml:ns:cpxmlsec:algorithm
s:gostr34102012-gostr34112012-256"></ds:SignatureMethod><ds:Reference URI="#SIGN
ED_BY_CONSUMER"><ds:Transforms><ds:Transform Algorithm="http://www.w3.org/2001/1
0/xml-exc-c14n#"></ds:Transform><ds:Transform Algorithm="urn://smev-gov-ru/xmlds
ig/transform"></ds:Transform></ds:Transforms><ds:DigestMethod Algorithm="urn:iet
f:params:xml:ns:cpxmlsec:algorithms:gostr34112012-256"></ds:DigestMethod><ds:Dig
estValue>LB4lt2Dmd+ToGO0hNMDKDmloiCDhFT22+utRriw2thY=</ds:DigestValue></ds:Refer
ence></ds:SignedInfo>
CHECK LEN 0141F5AC 031B8E28 1023 1024 0 -> 17
HASH OID = 1.2.643.7.1.1.2.2
F18A 1CC2 3BE1 F1D8 E3D6 0542 7B83 24DF 56AA EEFC 5894 9BF2 50C9 FEAE E322 93B0
[8Yocwjvh8djj1gVCe4Mk31aq7vxYlJvyUMn+ruMik7A=]
CS_ 80090006
TR_CS_AllMs 80090006
CheckSign time (si):144 msec
=== INFO: Signature fail
Хэш от каноничного SignedInfo тем не менее тот же, что и от текста в сообщении выше, то есть с каноникализацией проблем нет, что-то с вычислением хэша.
Какой текст и длина текста в этом месте? У меня длина 741.