Формат подписи для загрузки подписанных xml файлов должен быть base64

Обновлено: 04.07.2024

Можно использовать классы в пространстве имен System.Security.Cryptography.Xml для подписания XML-документа или его части при помощи цифровой подписи. Цифровые подписи XML (XMLDSIG) позволяют убедиться, что данные не были изменены после подписания. Дополнительные сведения о стандарте XMLDSIG см. в разделе рекомендации по синтаксису XML-подписиконсорциум W3C (W3C) и обработке.

Код в этой статье относится к Windows.

В примере кода в этой процедуре показано, как подписать весь XML-документ с помощью цифровой подписи и присоединить подпись к документу в Signature элементе <>. Пример создает ключ подписывания RSA, добавляет его в безопасный контейнер ключей и затем использует этот ключ для создания цифровой подписи XML-документа. Впоследствии ключ можно извлечь для проверки цифровой подписи XML либо использовать для подписывания другого XML-документа.

Сведения о том, как проверить цифровую подпись XML, созданную с помощью этой процедуры, см. в разделе как проверить цифровые подписи XML-документов.

Создание цифровой подписи XML-документа

Создайте объект CspParameters и укажите имя контейнера ключей.

Создайте асимметричный ключ, используя класс RSACryptoServiceProvider. Этот ключ автоматически сохраняется в контейнер ключей при передаче объекта CspParameters в конструктор класса RSACryptoServiceProvider. Этот ключ будет использоваться для подписывания XML-документа.

Создайте объект XmlDocument, загрузив XML-файл с диска. Объект XmlDocument содержит XML-элемент для шифрования.

Создайте новый объект SignedXml и передайте в него объект XmlDocument.

Добавьте ключ подписывания RSA в объект SignedXml.

Создайте объект Reference, определяющий, что именно требуется подписать. Чтобы подписать весь документ целиком, установите для свойства Uri значение "" .

Добавьте объект XmlDsigEnvelopedSignatureTransform в объект Reference. Преобразование позволяет проверяющему представить XML-данные в той же самой форме, которую использовал подписавший. XML-данные могут быть представлены различными способами, поэтому этот шаг крайне важен для выполнения проверки.

Добавьте объект Reference в объект SignedXml.

Вычислите подпись, вызвав метод ComputeSignature.

Извлеките XML-представление сигнатуры ( Signature элемент> <) и сохраните его в новый XmlElement объект.

Добавьте элемент в объект XmlDocument.

Пример

В этом примере предполагается, что файл с именем test.xml существует в том же каталоге, что и скомпилированная программа. Можно поместить следующий XML-код в файл с именем test.xml и использовать его вместе с данным примером.

Компиляция кода

Не следует хранить или передавать закрытый ключ из пары асимметричных ключей в виде обычного текста. Дополнительные сведения о симметричных и асимметричных криптографических ключах см. в разделе Создание ключей для шифрования и расшифровки.

На хабре уже была обзорная статья о механизмах создания ЭЦП в браузере, где было рассказано о связке Крипто-Про CSP +их же плагин к браузерам. Как там было сказано, предварительные требования для работы — это наличие CryptoPro CSP на компьютере и установка сертификата, которым собираемся подписывать. Вариант вполне рабочий, к тому же в версии 1.05.1418 плагина добавлена работа с подписью XMLDsig. Если есть возможность гонять файлы на клиент и обратно, то для того, чтобы подписать документ на клиенте, достаточно почитать КриптоПрошную справку. Все делается на JavaScript вызовом пары методов.
Однако, что если файлы лежат на сервере и хочется минимизировать трафик и подписывать их, не гоняя на клиент целиком?
Интересно?
Итак, клиент/серверный алгоритм формирования цифровой подписи XMLDSig.
Информацию об спецификации по XMLDsig можно найти по адресу тут.
Я буду рассматривать формирование enveloping signature (обворачивающей подписи) для xml-документа.
Простой пример подписанного xml:

Чтобы лучше понять, что из себя представляет enveloping signature, предлагаю краткий перевод описания тэгов из спецификации:

  • Signature -содержит данные подписи, включая саму подпись и сертификат.
  • SignedInfo -содержит информация о подписываемых данных и алгоритмах, которые будут применяться при формировании подписи.
  • CanonicalizationMethod -указывает канокализирующий алгоритм, который будет применен к SignedInfo перед вычислением подписи.
  • SignatureMethod -указывает алгоритм, используемый для генерации и валидации подписи. На вход алгоритму приходит канокализированный тэг SignedInfo.
  • Reference -может встречаться 1 или больше раз. Содержит информацию о подписываемых данных, включая местоположение данных в документе, алгоритм вычисления хэша от данных, преобразования, и сам хэш.
  • Transforms и Transform -перобразования над данными. На вход первого transform приходит результат разыменовании(dereferencing ) атрибута URI тэга Reference. На вход каждому последующему transform приходит результат предыдущего, результат последнего transform приходит на вход алгоритма, указанного в DigestMethod. Обычно в transform указывают XPath, определяющий защищаемые части документа.
  • DigestMethod -алгоритм вычисления хэша от результатов Transforms.
  • DigestValue -значение хэша от результатов Transforms.Часто это хэш от данных, на которые указывает Reference URI.
  • SignatureValue -собственно сама подпись, ради формирования которой все и затевается.
  • KeyInfo — информация о ключе, на тут интересует тэг X509Certificate, который содержит base64encoded сертификат из ключа, которым подписаны данные.

Итак, исходные данные:

  • устанавливаем CryptoPro CSP 3.6
  • устанавливаем КриптоПро ЭЦП Browser Plugin
  • устанавливаем сертификат с флешки в локальное хранилище (см. инструкцию
    пункт «2.5.2.2. Установка личного сертификата, хранящегося в контейнере закрытого ключа»)

Посмотреть хэш можно с помощью hashObject.Value
3.2)Считаем хэш на сервере и отправляем на клиент. Этот вариант у меня так и не заработал, но честно сказать я особо и не пытался.

Возможно хэш надо преобразовывать в base64.

Отправляем на клиент, там используем

Именно на методе hashObject.SetHashValue у меня падала ошибка. Разбираться я не стал, но криптопрошном форуме говорят, что можно как-то заставить ее работать.

Если соберетесь реализовывать серверный алгоритм генерации хэша, то вот пара полезных советов:
1) Посчитайте хэш на клиенте и на сервере от пустой строки. он должен совпадать, это значит ваши алгоритмы одинаковые.
Для GOST3411 это следующие значения:
base64: mB5fPKMMhBSHgw+E+0M+E6wRAVabnBNYSsSDI0zWVsA=
hex: 98 1e 5f 3c a3 0c 84 14 87 83 0f 84 fb 43 3e 13 ac 11 01 56 9b 9c 13 58 4a c4 83 23 4c d6 56 c0
2) Добейтесь, чтобы у вас совпадали хэши для произвольных данных, генерируемые на клиенте и на сервере.
После этого можно пересылать клиенту только хэш от SignedInfo вместо всего SignedInfo.

Шаг №4.(клиент)
Генерируем SignatureValue и отсылаем на сервер SignatureValue и информацию о сертификате


Возвращем на сервер binReversedSignatureString и certValue.

Код функций из utils не выкладываю. Мне его подсказали на форуме криптоПро и его можно посмотреть в этой теме

Шаг №5. (сервер)
Заменяем в сгенерированном на шаге №1 тэге Signature значения тэгов SignatureValue и X509Certificate значениями, полученными с клиента

Шаг №6. (сервер)
Верифицируем карточку.
Если верификация прошла успешно, то все хорошо. В результате мы получаем на сервере документ, подписанный клиентским ключом, не гоняя туда-обратно сам файл.

Примечание: если работа ведется с документом, уже содержащим подписи, то их надо отсоединить от документа до шага №1 и присоединить к документу обратно после шага №6

В заключение хочется сказать большое спасибо за помощь в нахождении алгоритма участникам форума КриптоПро dmishin и Fomich.
Без их советов я бы плюхался с этим в разы дольше.

Как с помощью КриптоПро выгрузить подпись для методов PostMessagePatch и GenerateUniversalTransferDocumentXmlForBuyer . Подпись в формате X509 Base64 не подходит для метода PostMessagePatch, ошибка "Invalid signature". Файл подписи в формате PKCS7 (.p7b) кодированный в Base64 дает ошибку "The server encountered an internal error or misconfiguration and was unable to complete your request"

The text was updated successfully, but these errors were encountered:

@elvirazakharova Что вы и как пытаетесь подписать? У вас есть бинарное представление подписи?

@ethaniel Операция подписания происходит на клиентской стороне, поэтому нам в PostMessagePatch не нужно передавать сертификат, нужно передавать подпись.

ethaniel commented Jan 24, 2020

@atytsky у нас есть "-----BEGIN CERTIFICATE-----" и "-----BEGIN ENCRYPTED PRIVATE KEY-----". Я так понимаю, что сам PRIVATE KEY мы передавать не должны.
Как должна выглядеть подпись?

@ethaniel конечно нет, Private Key — это ваш секрет, он как раз нужен для подписания/шифрования различного контента. Про подписи можно почитать например тут, или вот еще хорошая спека

Вообще, уже есть сертифицированные криптопровайдеры, которые умеют делать хорошие подписи, самый популярный — Крипто ПРо

@ethaniel конечно нет, Private Key — это ваш секрет, он как раз нужен для подписания/шифрования различного контента. Про подписи можно почитать например тут, или вот еще хорошая спека

Да вот только непонятно, что именно мы подписываем. Нужно заново скачать обратно XML по 820 приказу через GetMessage и заново его подписать как в SignedContent?
Мы делаем все на PHP и JSON к примеру, поэтому нехватка документации и примеров смущает.

atytsky commented Jan 24, 2020

Весь workflow описан здесь. Вам надо подписать XML-файл 1-го, либо 2-го титула (в зависимости от того, получаете или отправляете документ).

Если вы отправляете — подписывается 1-й титул УПД по №820, если получаете 2-й титул.

elvirazakharova commented Jan 24, 2020

Частично разобралась. Надеюсь это поможет многим людям за неимением какого-либо мануала в доступе. Для подписания титула (PostMessagePatch) покупателем понадобятся:

  1. Непосредственно сам титул титул (использовать GenerateUniversalTransferDocumentXmlForBuyer)
  2. Файл открепленной подписи к этому файлу.
    То что подпись идет к файлу нигде не написано, ровно как и то, что она открепленная. Но это не точно.
    Создать такую подпись возможно например, программой КриптоАРМ или ViPNet CryptoFile. Если ваша среда разработки не особо позволяет пользоваться какими-либо библиотеками, то в коде этот файл открепленной подписи можно сгенерировать с помощью объекта (он же COM-компонент, он же ActiveXObject, он же AutoObject) с названием "CAdESCOM". Предварительно нужно будет:
  3. Установить криптопровайдер(=ПО, что понимает, что такое подпись и как на нее посмотреть) И именно тот, на котором вам подпись дали, скорее всего это КриптоПРо.
  4. Установить плагин КриптоПро ЭЦП Browser plug-in
  5. Установить сертификат на компьютер в хранилище “Личные”.
    У объекта "CAdESCOM" есть весьма гуглимый набор методов, который позволит получить подпись к файлу (который предварительно нужно кодировать в Base64 и подавать на вход строкой). А подойдет она или нет я узнаю только в понедельник. Открепленная подпись, созданная КриптоАРМом подошла.
    На выходе подпись уже в Base64, не нужно ее еще раз кодировать, это текстовый файл, можно блокнотом открыть, посмотреть что к чему там.

atytsky commented Jan 27, 2020

@elvirazakharova в целом описано все верно, но есть следующие моменты:

  • Мануала по подписнию нет, так как наше API ничего не подписывает, и все криптографические операции происходит на стороне клиента/интеграции/etc. Мы не можем написать инструкцию, так как она отличается в зависимости от того, какой стек/ОС использует интегратор
  • То, что подписывается сам файл, написано в приказах ФНС. Подпись мы умеем принимать как прикрепленную, так и открепленную
  • GenerateUniversalTransferDocumentXmlForBuyer - используется для приказа №155, который уже не действует. Нужно использовать новый формат приказа №820

Если кому-то нужно пример на php (linux), то вот наша функция для подписания документов:

подписываем документы формата sig

Office

Данную статья я решил написать, после того как мне на почту от читателей поступил вопрос:

У меня возникла проблема с подписанием документа электронной подписью. Подскажите, пожалуйста, что можно сделать. Ситуация такая. Мне необходимо подписать pdf документ (в данном случае копию приказа) электронной подписью руководителя. Подписанный документ должен быть обязательно в формате .sig Сейчас же мы рассмотрим подпись документа не просто c использованием ЭЦП, а что бы на выходе у нас получился файл формата SIG, как это сделать сейчас вы узнаете!

Что такое *.SIG файл?

Если вы задались вопрос, а что же за такой формат SIG? который нас гос службы обязывают сдать документы, то ответ на этот вопрос очень простой:

Особенностью файлы формата sig является то, что он состоит из двух частей:

Какие бывают подписи формата SIG

Главное отличие этих подписей состоит в том:

Отсоединенная подпись ЭЦП в формате sig

на рисунке видно документ Документ Microsoft Word.docx и рядом с ним идет документ Документ Microsoft Word.docx.sig который считается его подписью

Поэтому отсоединенная копия очень удобный вариант подписи для второй стороны.

Присоединенная подпись ЭЦП в формате sig

Присоединенная подпись ЭЦП в формате sig

на рисунке видно документ Документ Microsoft Word.docx с размеров в 13кб, а под ним документ Документ Microsoft Word.docx.sig который и является уже документом с присоединенной подписью, что свидетельствует размер документа в 20кб

Для просмотра этого документа на другой стороне должна стоять программа которая может открыть этот документ и просмотреть его содержимое, т.е. тот же КриптоАРМ

Читайте также: