Цитата:UPD: Попробовал разные варианты, и если я правильно понимаю, то это RawSignature, в которой подписали хеш SignedInfo. Так ли это?
Добрый день. По стандарту да.
Идентификатор "GOST3411_2012_256withGOST3410DH_2012_256" говорит о том, что хэш и подпись гост-2012, вычисляются одной операцией, формат результата старший байт по младшему адресу. Для сравнения: когда фигурирует "CryptoPro" это младший байт по младшему адресу, а если хэш неизвестен, то значит хэш вычислен одной операцией и подписан другой операцией (алгоритму подписания это указывает хэш не вычислять, поэтому о хэше алгоритм "ничего не знает", но понятно что для гост-2012 подписи других вариантов кроме хэша гост-2012 все равно нет).
Подпись гост включает в себя случайное значение, так что меняется каждый раз даже если данные и ключ не изменились. Более того, для гост не описан алгоритм как по значению подписи восстановить хэш (проверка подписи основана на другом принципе), поэтому и промежуточный хэш от SignedInfo заранее нам неизвестен.
SignatureMethod со значением urn:ietf:params:xml:ns:cpxmlsec:algorithms:gostr34102012-gostr34112012-256 это как раз, "что происходит" в RawSignature при указании ключа гост-2012. Внутри считается хэш гост-2012 256(часть gostr34112012-256 идентификатора) и подписывается ключом гост-2012 (часть gostr34102012). Основное отличие в том, что порядок байт может оказаться разный и значение надо будет перевернуть.
Что происходит у них - неизвестно, так как Java класс могли переопределить и там может произойти все что угодно. Как я понимаю, обычно это все в одной строке:
Код:signature.initSign(privateKey)
signature.update(signInfoXml.toByteArray())
val sign = signature.sign()
Судите сами по краткому пересказу стандарта (подпись собирается сразу в документе без формирования кусками и и вставки в каркас):
В нужное место файла добавляется тег Signature, в него добавляются SignedInfo, SignatureValue, KeyInfo, под SignedInfo добавляется CanonicalizationMethod со значением алгоритма, SignatureMethod со значением алгоритма. Это начальный каркас подписи. Добавляется нужное количество Reference (пропущу их обработку раз их победили), референсы блокируются (просто флаг в ПО подписания запрещающий добавлять новые референсы). К этому моменту сформировано в документе полное содержание SignedInfo, оно выбирается из документа в отдельный фрагмент (как правило это приводит к дублированию объявления пространства имен из Signature в SignedInfo, другие пространства имен из вышестоящих тегов также могут дублироваться во фрагмент). Далее текст SignedInfo идет на алгоритм, указанный в CanonicalizationMethod, все C14N алгоритмы каноникализации гарантировано удаляют неиспользуемые пространства имен. Результат CanonicalizationMethod (канонический текст SignedInfo) идет на алгоритм, указанный в SignatureMethod вместе с аргументом-закрытым ключом*, результат сокращается с учетом кодирования криптографических типов** и кодируется в Base64 и заносится в SignatureValue, а соответствующий открытый ключ указывают в KeyInfo (форм для указания достаточно много, но самое распространенное для гост - указание всего сертификата либо в KeyInfo либо ссылка на отдельный тег с сертификатом).
* В случае гост с дескриптором закрытого ключа - сам закрытый ключ не покидает КриптоПро CSP, наружу идет дескриптор. Как в раздевалке дают номерок. Именно дескриптор содержится в PrivateKey как будто это закрытый ключ. Этот "номерок" действителен до закрытия контейнера или перезапуска криптопровайдера, сохранять его куда либо бесполезно.
** Сокращение: если в начале получается байт 00, то он отбрасывается по аналогии с десятичной записью числа когда не указываются нули впереди числа и пишут 10 вместо 0010. Обычное значение подписи гост-256 после кодирования имеет 88 или 90 символов. Разница 88 или 90 - зависит от того, вставился ли перевод строки (историческая практика идущая от мониторов ДОС - делать перевод когда строка превышает ширину монитора в символах - сохранилась в кодировщиках Base64). Сокращения менее 88 не случается почти никогда, но нужно иметь ввиду, что в теории значение может быть короче.
Все вот эти формирования кусков текста отдельно от документа - не по стандарту. Допустимы пока результат тот же, но все же зачем изобретать велосипед, парсеры XML есть практически для любого языка программирования. Конечно, сначала придется разобраться чем атрибут отличается от объявления пространства имен, но потом легче.