XMLDSig: формат подписи XML

17 8

На одном из идущих в настоящее время проектов решалась задача подписания (наложения ЭП - электронной подписи) XML документов, а именно SOAP-пакетов. Рекомендованным форматом был OASIS Standard 200401 с профилем X.509 Certificate Token Profile. Эти документы описывают применение созданного www-консорциумом (W3C) формата электронных подписей XML (XMLDSig - XML Digital Signature) в SOAP-сообщениях. XML-подписи, как и другие виды ЭП, поддерживают аутентификацию, целостность данных и неотрекаемость от подписания данных.

Отмечу несколько особенностей формата XMLDSig:

1. Объектом подписания может служить не весь XML-документ, а только его часть, т.е. определённый узел. Согласно OASIS Standard 200401 подписываемым объектом является тело (узел Body) SOAP-сообщения.

2. Различные части XML-документа могут быть подписаны несколькими исполнителями.

3. XML-подпись может находиться на разных уровнях по отношению к подписываемому объекту:

  • в структуре подписи может находиться URI (унифицированный идентификатор ресурса);
  • XML-подпись может находиться на одном уровне с подписываемым узлом;
  • XML-подпись может находиться внутри подписываемого узла;
  • подписываемый узел может находиться внутри структуры XML-подписи.

4. Для проверки действительности ЭП необходим доступ к объекту подписания.

Структура SOAP-коверта

В общем случае сообщение состоит из заголовка и тела: Header и Body. Header содержит метаданные, а Body данные. XML-подпись помещается в узел Header.

Криптографические алгоритмы и каноникализация.

Для решения задачи были использованы ГОСТ Р 34.11-94 — российский криптографический стандарт вычисления хеш-функции и ГОСТ Р 34.10-2001 - стандарт электронной подписи.

В силу гибкости правил составления XML, одна и та же структура документа и одна и та же часть информации могут быть представлены различными XML-документами. Рассмотрим два документа:

 

С логической точки зрения они равнозначны, то есть имеют одинаковую XML-схему. Но XML-файлы этих листингов не содержат одну и ту же последовательность символов, что повлечёт разные результаты, например, при получении значения хэша.

Во избежание подобных разночтений были приняты строгие правила форматирования и требования к содержанию XML-сообщений. Процесс приведения XML-документов к унифицированному (каноническому) виду называют каноникализацией (англ. Canonicalization). Примерами правил может быть применение определённой схемы кодирования (UTF-8), нормализация значений атрибутов, использование двойдых кавычек для значений атрибутов, определённый порядок атрибутов и объявлений пространств имён, и др. Каноникализация XML бывает нескольких видов, которые отличаются составом правил. Побробнее о процессе каноникализации можно прочитать в официальной спецификации W3C (русскоязычные статьи на эту тему можно найти здесь и здесь)

Библиотека SIRCrypt

Для реализации подписания XML в DIRECTUM была написана COM-библиотека, внутри которорй описаны 3 класса: Hasher, Signer и XMLCanonicalizer для получения хэша, значения ЭП и каноникализации XML-документов соответственно.

Для функционирования библиотеки требуется Crypto PRO CSP (тестировалась на версии Crypto PRO CSP 3.6.6497 KC2) и .NET (минимально 2.0).

Регистрация библиотеки выполняется выполнением следующей команды:

> regasm.exe "путь к dll" /codebase /tlb

Объект Hasher для вычисления хэша по ГОСТ

Содержит поля Content (тип 'строка') и HashValueAsBase64 (тип 'строка'), а также метод для вычисления значения хэш-функции Hash(). Для вычисления необходимо означить Content, вызвать метод Hash(), в результате которого в поле HashValueAsBase64 запишется значение хэш-функции в Base64.

Объект Signer для получения значения ЭП по ГОСТ

Содержит поля Content (тип 'строка'), ContainerName (тип 'строка'), CertificateAsPEM (тип 'строка'), BESignatureValueAsBase64 (тип 'строка'), метод Sign(). После инициализации объекта необходимо означить Content (данные для подписания), ContainerName (имя контейнера закрытого ключа сертификата), вызвать метод Sign(). После чего в поле CertificateAsPEM попадёт соответствующий закрытому ключу сертификат в Base64, а в поле BESignatureValueAsBase64 значение подписи в виде Base64-строки.

Объект XMLCanonicalizer для каноникализации XML

Содержит поля XMLContent (тип 'строка'), CanonicalXML (тип 'строка'), метод C14NExc(). Для получения канонической формы XML нужно означить XMLContent, вызвать C14NExc(), получить результат из поля CanonicalXML.

Структура XML-подписи

Создание подписи выглядит следующим образом: сначала формируется основа soap-пакета, узлы Header и Body. Body заполняется данными и добавляется атрибут wsu:ID="Body" - идентификатор подписываемых данных.

Далее нужно подготовить структуру Security и включить её в Header.

Заполнение структуры Security происходит в следующем порядке:

  1. Берётся значение хэш-функции от узла Body в каноническом виде и помещается в узел DigestValue.
  2. Узел SignedInfo приводится к каноническому виду, подписывается ЭП. Результат в формате Base64-строки попадает в узел SignatureValue.
  3. Открытый ключ сертификата, которым было выполнено подписание помещается в узел BinarySecurityToken в формате строки Base64.

Для того, чтобы проверить сформированную таким образом ЭП необходимо проделать все действия в обратном порядке, а именно:

  1. получить каноническую форму элемента SignedInfo.
  2. С использованием резльтата предыдущего шага проверить, действительно ли значение ЭП из узла SignatureValue с помощью открытого ключа сертификата. На данном этапе проверяется только корректность ЭП, что не гарантирует неизменность данных.
  3. Если проверка действительности ЭП пройдена успешно, сравнивается хэш из узла DigestValue и хэш от узла с данными. Если они неравны, то подписанные данные изменены и вся ЭП недействительна.

Пример использования

XML-документ до подписания: SOAPEnvelope.xml (266,00 байт)

Подписанный XML-документ: SignedSOAPEnvelope.xml (3,32 Кб)

Пакет разработки и библиотека

Примеры подписания XML на ISBL (сценарий): dev.zip (5,95 Кб)

Для постоянного использования код, выполняющий типовое подписание готового SOAP-конверта, вынесен в функцию SignSOAP().

Для подписания используется сертификат из личного хранилища сертификатов текущего пользователя.

Cертификат для тесподписания можно получить на тестовом центре сертификации КриптоПРО.

Библиотека SIRCrypt: SIRCrypt.zip (7,42 Кб)

Алексей Пестерев

Дима, это невероятно круто! Плюсую за данную разработку!

Вадим Грозов

Монструозно!

Анатолий Васюрин

Где можно ознакомится подробно с библиотеками(описание функций и т.д.)?

 

Игорь Поляков

Здравствуйте!Как использовать данную библиотеку в Delphi?

Игорь Поляков

Разобрался...Только почему то 

CanonicalXML возвращает результат - байтовый а не строка

 

Игорь Поляков

   function SirCryptSigner(const Content: WideString; const Container: WideString;const enc :boolean; out CertPem: WideString): WideString;
   Var
    p,s:Pointer;
    v,c:Variant;
    obj:OleVariant;
  Pair: TStringStream;
    begin
      //https://club.directum.ru/post/546
      {Ñîäåðæèò ïîëÿ Content (òèï 'ñòðîêà'), ContainerName (òèï 'ñòðîêà'), CertificateAsPEM (òèï 'ñòðîêà'), BESignatureValueAsBase64 (òèï 'ñòðîêà'), ìåòîä Sign().
       Ïîñëå èíèöèàëèçàöèè îáúåêòà íåîáõîäèìî îçíà÷èòü Content (äàííûå äëÿ ïîäïèñàíèÿ), ContainerName (èìÿ êîíòåéíåðà çàêðûòîãî êëþ÷à ñåðòèôèêàòà), âûçâàòü ìåòîä Sign().
      Ïîñëå ÷åãî â ïîëå CertificateAsPEM ïîïàä¸ò ñîîòâåòñòâóþùèé çàêðûòîìó êëþ÷ó ñåðòèôèêàò â Base64, à â ïîëå BESignatureValueAsBase64 çíà÷åíèå ïîäïèñè â âèäå Base64-ñòðîêè.}
  try
  Pair:=TStringStream.Create('');

   obj:=CreateOleObject('SirCrypt.Signer');
    obj.Content:=Content;
    obj.ContainerName:=Container  ;
 //   obj.CertificateAsPEM:=CertPem;
 obj.Sign;
      CertPem:=Base64EnCodeStr(obj.CertificateAsPEM) ;
      //ShowMessage(By);
   v:={Base64EnCodeStr}(obj.BESignatureValueAsBase64);
    c:= obj.CertificateAsPEM   ;
   s:=@c ;
   p:=@v;
   Pair.WriteBuffer(p,(Length(v)+SizeOf(v))*16);

   if enc then
   Result:=Base64EncodeStr(Pair.DataString)//WideString((obj.BESignatureValueAsBase64))  ;
     else
   Result:=Trim(WideString(Pair.DataString));
  //Pair.WriteBuffer(p,(Length(v)+SizeOf(v))*16);

  Pair.WriteBuffer(s,(Length(v)+SizeOf(c))*16);
      CertPem:=Base64EncodeStr(Pair.DataString);

  // ShowMessage(Base64DecodeStr(String(v)) )  ;
//Èñòî÷íèê: http://ezp20.ru/ecp-xml
   // obj.Free;
      Pair.Free;
       except
      Result:='';
     ShowMessage('Class Signer  не зарегистрирован!');
      end;

      end;

 

Павел Андреев

Хорошая библиотека, пока не пришел ГОСТ 2012. Жаль, что она не переделана под сегодняшние нужды

Владимир Клейн

переделай ..)) Родина тебя не забудет

Авторизуйтесь, чтобы написать комментарий