Автор: admin@uprio.ru 
:) можете рассказать алгоритм действий?
Конечно, насколько подробно надо описать? В общих чертах вроде как правильно описали в ответе поддержки СМЭВ. Момент с которым которым обманули происходит внутри криптопровайдера (как "черный ящик" куда закинули открытый ключ, хэш SignedInfo и RAW значение подписи) и на самом деле любая программа, СМЭВ в том числе, получает от криптопровайдера только логический результат x1=x2 или x1!=x2.
Итак, подписание:
1) определяетесь с фрагментом который должен быть подписан и алгоритмами подписи. Фрагмент должен иметь атрибут типа xs:ID обычно это или просто Id или wsu:Id, если второе то не забыть объявление xmlns:wsu
Важно чтобы в документе не было других тегов с таким же значением атрибута Id.
Подписывать надо сначала самую дальную от корня ЭП и постепенно подниматься к корню (сначала ЭП-СП если нужна, потом ЭП-ОВ). Это важно потому что при проверке ЭП-ОВ, в тексте остается все содержимое ЭП-СП. Если наложить ЭП-СП после ЭП-ОВ, то ЭП-ОВ будет вычислена без присутствия ЭП-СП, а проверяться уже с присутствием ЭП-СП. Насчет того, поможет ли с этим трансформ enveloped-signature я не проверял, но скорее нет чем да. Поверх этого СМЭВ наложит еще и ЭП-СМЭВ.
2) заполняете шаблон Signature - SignedInfo в соответствии с алгоритмами CanonicalizationMethod (желательно эксклюзивная каноникализация вообще всегда, не только в СМЭВ, чтобы не зависеть от места вставки подписи) SignatureMethod (гост-2012 сейчас)
3) добавляете Reference c URI указывающим на подписываемый фрагмент, указываете алгоритм хэша DigestMethod (гост-2012 сейчас), трансформы - состав зависит от целевой системы, порядок трансформов важен (эксклюзивная каноникализация и смэвовский, если речь о смэв 3). Если есть enveloped-signature он идет в начало, а специфичный для системы как смэвовский в конец.
4) выбираете подписываемый фрагмент (с контекстом из документа, подробнее ниже) и применяете к нему по порядку трансформы из Reference (если трансформов было не указано, то может применяться каноникализация)
5) трансформированный в 4 фрагмент идет на вычисление хэша (алгоритм хэша по Reference).
Создается "пустой" экземпляр криптопровайдера без ключей и на нем открывается хэш-объект, добавляются данные, вычисляется и запрашивается значение хэша, хэш-объект закрывается.
6) полученное значение при необходимости переворачивается (о том ниже), потом кодируется в Base64 и записывается в Reference - DigestValue.
7) шаги 3-6 повторяются если несколько подписываемых одной подписью фрагментов (в смэв только один, а вот если нужна подпись xades-bes или xades-t, то данные xades добавляются как еще один Reference, но это немного другая история).
8) после того как все Reference добавлены все изменения SignedInfo блокируются, выбирается SignedInfo (с контекстом из документа), применяется CanonicalizationMethod (иные трансформы не применяются)
9) от каноничного SignedInfo считается хэш по алгоритму согласованному с SignatureMethod (для алгоритма подписи гост-2012 возможен только хэш гост-2012, хэш также указан как часть в идентификаторе алгоритма подписи)
Если брать голое CryptoApi, то есть разница между вызовом подсчета хэша в шаге 5 и 7, связанная с тем нужно подписывать хэш или нет.
Более точно про хэш: создается экземпляр криптопровайдера с конкретным контейнером закрытого ключа (при этом запрашивается пин-код). На этом экземпляре открывается хэш-объект, добавляются данные, вычисляется (значение хэша запрашивать не обязательно), хэш-объект НЕ закрывается.
10) хэш-объект подписывается закрытым ключом (значение подписи возвращается сразу без дополнительного запроса), хэш закрывается.
11) полученное значение при необходимости переворачивается (о том ниже), потом кодируется в Base64 и записывается в SignatureValue.
12) к Signature добавляется KeyInfo (информация о сертификате) и при необходимости обертка wsse:Security - это тоже немного другая история.
13) если Signature формировалась отдельно, вставляется в документ.
Проверка:
1) документ сканируется на наличие подписей (не уверен ФЛК до проверки подписей или после). Проверка начинается с ближней от корня подписи. То есть сначала если есть ЭП-СМЭВ, потом ЭП-ОВ, потом если есть ЭП-СП.
2) c SignedInfo делаются те же действия что на шаге 8 подписания.
3) хэш от каноничного SignedInfo
Создается "пустой" экземпляр криптопровайдера без ключей и на нем открывается хэш-объект, добавляются данные, вычисляется, хэш-объект НЕ закрывается (значение хэша запрашивать не обязательно).
4) из подписи выделяется сертификат, декодируется из base64, из сертификата выделяется структура с описанием открытого ключа (для этого можно импортировать сертификат и получить контекст сертификата в котором соответствующее место будет вычислено автоматически, но можно и просто вырезать соответствующую часть сертификата если известно место), структура с открытым ключом импортируется в "пустой" экземпляр криптопровайдера (может быть тот же что в шаге 2 или новый), получается дескриптор открытого ключа;
5) из подписи выделяется SignatureValue, декодируется из base64. Если перед записью был переворот значения, то после декодирования нужно опять же перевернуть значение.
6) полученный в шагах 3-5 данные (хэш-объект от каноничного SignedInfo, дескриптор открытого ключа, декодированное значение подписи) передаются в функцию проверки, откуда только получается логическое значение верна математически подпись или нет. Если любые из данных искажены - изменилось SignedInfo, неправильно было каноникализировано при подписи, нет тот сертификат или неправильное значение подписи, то ответ будет что подпись неверна и проверка подписи прерывается.
7) Если же проверка SignatureValue успешна, то для каждого Reference выполняются шаги 4-5 подписания и полученный хэш сравнивается с декодированным значением DigestValue. Если перед записью DigestValue был переворот значения, то после декодирования нужно опять же перевернуть значение. Если значения не сошлись проверка прерывается.
Для Reference с особым типом (при подписи xades, например) запускается отдельные проверки - это уже другая история.
Тонкий момент в том, что xades это расширение формата xmldsig и в зависимости от выбранного формата проверки ошибка в xades может быть не выявлена проверкой xmldsig, что ведет к тому что разные программы дают разный результат проверки.
8) Если на шаге 7 ошибок не выявлено подпись считается математически корректной. На этом моменте тестовая страничка выдаст что ЭП верна, но СМЭВ продолжит проверять сертификат.
9) Для каждого сертификата получается вышестоящий и проверяется подпись в самом сертификате открытым ключом вышестоящего сертификата. Для корневого сертификата открытый ключ берется в нем самом. Далее проверяется что корневой сертификат есть в списке доверенных, и нет информации об отзыве любого сертификата в цепочке. Если все верно, то ЭП признается верной.
Добавлю, что не пугайтесь когда проверяете ответ СМЭВ и получаете результат ЭП-СМЭВ верна, ЭП-ОВ не верна, ЭП-СП верна. Это означает, что версия пространств имен у Вас и отвечающего разная и СМЭВ сделала медвежью услугу преобразовав ответ к Вашей версии и нарушила этим ЭП-ОВ. Для значимости ответа нужно восстановить версию отвечающего. Или сразу запрашивать по той же версии.
Автор: admin@uprio.ru 
т.е. хотите сказать, что надо смотреть в сторону каноникализации?
Как правило да. Если вызываете штатные функции сертифицированного криптопровайдера и указываете правильно все алгоритмы (предопределенные константы должны быть с 2012 в названии), то с самим вычислением значения подписи и хэша проблем не должно быть. Остаются 3 важных момента:
1) соблюдать аккуратность и порядок действий чтобы не нарушить готовую верную подпись при вставке в документ. Например, если вырезали фрагмент из документа для каноникализации, важно перенести в него вышестоящие объявления пространств имен перед каноникализацией иначе каноническая форма вырезанного фрагмента будет отличаться от канонической формы этого же фрагмента в документе и при вставке подписи в документ получится неверная подпись. Штатные средства работы с XML должны это сделать автоматически, а вот если копируете документ как строку, то придется вручную добавлять. Если тег без префикса каноникализация даже не ругнется, что пространства имен нет.
2) каноникализация текста что подается в криптопровайдер (да, тут немало сюрпризов и мелких отличий от стандарта. В одной из тем экспериментами дошли до того что СМЭВ исключает символы с кодом 13 в значениях атрибутов даже в форме 
 коды 9 и 10 	 
 заменяются на пробел, при этом если получается несколько пробелов подряд они не заменяются на один пробел);
3) порядок байт значений из криптопровайдера (криптопровайдер криптопро возвращает в Little Endian, а стандарт требует Big Endian), тут надо оговориться что некоторые среды программирования и промежуточные программные "прокладки" уже учитывают это и переворачивать не надо. Однако другие не учитывают и приходится отзеркалить значение как массив байтов (первый байт меняется местами с последним, второй с предпоследним и т.д.)
Отредактировано пользователем 24 июля 2020 г. 5:04:55(UTC)
| Причина: Не указана