Экспорт/импорт отсоединённой электронной подписи (ЭП)

19 9

Постановка задачи

На входе мы имеем два или более файлов (тело электронного документа и файлы отсоединённых ЭП).

Необходимо выполнить загрузку документа с ЭП в систему DIRECTUM и обеспечить удобный доступ пользователя к информации об ЭП.

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

Импорт ЭД с ЭП

В системе DIRECTUM «из коробки» есть возможность импортировать документы с ЭП в формате ESD (см. EDocuments.CreateNewFromFile). Посмотрим, получится ли сгенерировать ESD из имеющихся данных (файлы тела документа и ЭП). Для этого создадим в системе тестовый документ, подпишем его и экспортируем в ESD.

ESD представляет собой XML следующего содержания:

<!--?xml version="1.0" encoding="UTF-8" standalone="yes"?-->
<structuredelectronicobject metadataid="" metadataversion="" version="2.0">
 <header author="Test" createdinsendersystem="true" date="" extension="DOC" globalid="{991B7778-B7D9-4AC1-A5A5-B88CD590216F}" modified="2015-12-09T17:35:55+04:00" name="Пример ESD" note="Test" number="" organization="Test" sourceedocumentid="150010485" sourceedocumentversionnumber="1" type="Document">
 <extattributes>
  <id isnull="false" name="ID" type="Integer" value="150010485">
  <organization isnull="true" metadataid="" name="Organization" reference="Организации" type="Reference" value="">
  <versionnumber isnull="false" name="VersionNumber" type="Integer" value="1">
 </versionnumber></organization></id></extattributes>
 <accessrights>
 <links>
 <contents>
  <!--[CDATA[0M8R4KGxGuEAAAAAAAAAAAAAAA....]]-->
 </contents>
 <MetadataScheme>
  <!--[CDATA[]]-->
 
 <digitalsignatures>
  <digitalsignature certificateissuedto="Test" cryptoprovider="CryptoPro Encryption" signaturetype="Approving" signed="2015-12-09T17:35:53+04:00" version="2.0">
   <attributes>
    <comment isnull="false" name="Comment" type="String" value="">
    <signedbyusername isnull="false" name="SignedByUserName" type="String" value="Test">
    <inthenameofusername isnull="false" name="InTheNameOfUserName" type="String" value="Test">
   </inthenameofusername></signedbyusername></comment></attributes>
   <data>
    <!--[CDATA[MIIH5gYJKoZIhvcNAQcCoII...]]-->
   </data>
  </digitalsignature>
 </digitalsignatures>
</links></accessrights></header></structuredelectronicobject>

Как видим, ESD, кроме самого тела документа и ЭП, содержит ещё много различных реквизитов (ИД, Дата изменения, криптопровайдер, имя пользователя-подписанта и т.п.).

В нашем случае, на этапе импорта мы имеем только файлы тела документа и ЭП. Некоторую информацию о сертификате и его владельце можно достать из ЭП. Но сначала проверим, можно ли без этого обойтись и импортировать в систему ESD, указав только тело документа и ЭП (в CDATA).

<!--?xml version="1.0" encoding="UTF-8" standalone="yes"?-->
<structuredelectronicobject metadataid="" metadataversion="" version="2.0"><links><links>
 <header extension="" modified="" name="" type="Document">
 <accessrights>
 <links>
 <contents>
  <!--[CDATA[0M8R4KGxGuEAAAAAAAAAAAAAAA....]]-->
 </contents>
 <MetadataScheme>
  <!--[CDATA[]]-->
 
 <digitalsignatures>
  <digitalsignature certificateissuedto="" cryptoprovider="" signaturetype="" signed="" version="">
   <attributes>
    <comment isnull="false" name="Comment" type="String" value="">
    <signedbyusername isnull="false" name="SignedByUserName" type="String" value="">
    <inthenameofusername isnull="false" name="InTheNameOfUserName" type="String" value="">
   </inthenameofusername></signedbyusername></comment></attributes>
   <data>
    <!--[CDATA[MIIH5gYJKoZIhvcNAQcCoII...]]-->
   </data>
  </digitalsignature>
 </digitalsignatures>
</links></accessrights></header></links></links></structuredelectronicobject>

Сначала получаем ошибку «Недопустимое имя типа подписи ""». Хорошо, означим атрибут в ESD «SignatureType="Approving"».

После этого импорт проходит, но, при просмотре ЭП документа, получаем ошибку «Подпись недостоверна».

Методом проб и ошибок можно установить минимальный набор реквизитов ESD, которые необходимо обязательно заполнить:

  1. Extension – расширение (DOC, DOCX…).
  2. CryptoProvider – плагин для подписания (CryptoPro, Capicom...).
  3. Version – версия плагина для подписания.
  4. SignatureType – тип ЭП (Approving/Authenticating).

Бинго! Расширение документа мы знаем, тип ЭП можно выбрать «по-умолчанию», например, утверждающая (Approving), а вот с плагином оказалось не всё так просто. В системе DIRECTUM есть три плагина для взаимодействия с криптопровайдерами: Bicrypt signing, Capicom Encryption и CryptoPro Encryption. Bicrypt и CryptoPro "заточены" для одноименных CSP. Универсальным же из всех перечисленных является Capicom. На входе у нас только файлы документа и ЭП. Как и с помощью чего было выполнено подписание, мы не знаем. Из ЭП сведений об используемом криптопровайдере достать, к сожалению, не получилось.

Таким образом, в атрибуте ESD «CryptoProvider» необходимо указывать «Capicom Encryption», а «Version=”2.0”». (Подробностей описания механизмов работы с ЭП здесь касаться не буду, т.к. это тема уже отдельной статьи и даже не одной.)

Примечание: «Version=”2.0”» актуально для версии IS-Builder 7.16.0.1706 и выше. В ранних версиях IS-Builder были проблемы с плагином Capicom Encryption в части работы с ЭП, которые были сгенерированы по алгоритмам ГОСТ. Таким образом, для IS-Builder старше чем 7.16.0.1706 версию плагина указывать не нужно, но при просмотре ЭП по ГОСТ будет ошибка «Подпись недостоверна». Ещё один повод обновиться на новую версию!

У Capicom есть ещё одна особенность. Как правило, отсоединённая ЭП кодируется в BASE64 и передаётся в файле с расширением «.p7s». В ESD для плагина Capicom ЭП должна быть закодирована в BASE64 два раза (а, например, для CryptoPro хватит и одного раза). Поэтому, при формировании ESD, необходимо проверить, закодирована ли ЭП в BASE64, если нет, то закодировать 2 раза, если да, то закодировать в BASE64 ещё один раз.

После того как мы сгенерировали XML в формате ESD, можно смело импортировать структурированный документ в DIRECTUM.

При просмотре подписей в системе, пользователи смогут увидеть всю необходимую информацию: ФИО владельца сертификата и дату подписания, эти данные подтянутся автоматом, так что отдельно указывать их в ESD смысла нет.

Код можно посмотреть в функции «CreateEDocWithSigFromFile». Пакет разработки прилагается.

Экспорт ЭП

С экспортом ЭП в файлы всё обстоит гораздо проще. Каких-то подводных камней и хитростей здесь нет.

Выгрузить ЭП можно двумя способами.

Первый способ: через ESD (по аналогии с импортом, но в обратном порядке).

  1. Выгрузить подписанный документ в ESD.
  2. Пробежаться по XML и выгрузить ЭП в отдельные файлы.

Плюсы:

  • Кроме самих ЭП в ESD можно найти много полезной информации, которую тоже можно использовать при выгрузке.

Минусы:

  • Такой способ обработки достаточно медленный, т.к. нужно разбирать XML.

Второй способ: получить ЭП напрямую из базы с помощью SQL-запроса. Как можно подсмотреть в справке, нас интересует таблица «SBEDocSignature», где в поле «Sign» типа «image» хранится не что иное, как Электронная подпись документа.

С помощью нехитрого SQL-запроса можно быстро достать электронные подписи документа

     select
        cast(cast(S.Sign as varbinary(max)) as varchar(max))
      from
        SBEDocSignature S (nolock)
      where
        S.EDocID = «ИД_Документа»

Плюсы:

  • Высокая скорость
  • В БД информации не меньше, чем в ESD.

Минусы: -

Пример реализации функции экспорта ЭП «ExportSignaturesFromEDocVer». Пакет разработки прилагается.

Заключение

Данный функционал может применяться в интеграционных решениях, где необходима передача электронных документов с отсоединённой электронной подписью, а интегрируемая система не умеет генерировать ESD.

Пакет разработки: ImportAndExportSignedEDoc.zip

Алексей Семакин

Андрей, а что думают о полученной экспортом отсоединенной ЭП внешние приложения для работы с подписями, например, Крипто АРМ?

И, кстати, о недостатках экспорта ЭП прямым запросом. Теоретически может потребоваться самостоятельно реализовывать в запросе системную логику — проверку прав доступа, например.

Андрей Шестаков
а что думают о полученной экспортом отсоединенной ЭП внешние приложения для работы с подписями, например, Крипто АРМ?

Проверка экспортированных ЭП выполнялась на ресурсе https://www.gosuslugi.ru/pgu/eds/. Думаю, в Крипто АРМ тоже всё будет хорошо.

Руслан Бапин
а что думают о полученной экспортом отсоединенной ЭП внешние приложения для работы с подписями, например, Крипто АРМ?

 

Проверка экспортированных ЭП выполнялась на ресурсе https://www.gosuslugi.ru/pgu/eds/. Думаю, в Крипто АРМ тоже всё будет хорошо.

А мы загружали документ и экспортированную sql-запросом ЭП в сервис Диадок - тоже нормально подхватилось.

Только есть нюанс - в веб-интерфейсе Диадок отображается, что документ подписал тот пользователь, под которым загрузили документ, а не тот, кому принадлежит сертификат. А если открыть окошечко свойств ЭП, то там уже в строке "ФИО подписанта" отображается владелец сертификата.

Мне кажется, такое поведение сервиса Диадок можно объяснить следующим образом: ЭП считается валидной и юридически значимой, если ей сопутствует доверенный штамп времени, а в отсоединенной ЭП, насколько я понимаю, этого штампа нет (или он не всегда есть). А вот в момент загрузки документа и подписи в сервис Диадок штамп времени генерируется должным образом (т.е. с помощью специального серверного ПО, удовлетворяющего требованиям и аттестованного). Поэтому сервис пишет, что подписал тот пользователь, который в данный момент взаимодействует с сервисом, т.е. под чьей учеткой загрузили документ и ЭП. И это далеко не обязательно тот же самый пользователь, которому принадлежит сертификат.

Андрей Манаков
//Получаю подпись методом приведенным в данной статье:
//sql: cast(cast(S.Sign as varbinary(max)) as varchar(max))
//Signatures = SQL(Format(GetSignaturesQueryTemplate; ArrayOf(EDocID; EDocVersion.Number)))
//SignatureBase64 = SubString(Signature; ';'; 2)

//Записываю в файл
SignData64 = MimeEncodeString(SignatureBase64)
ФайлЗаписать(SignFullFileName;;SignData64)

Но на некоторых документах MimeEncodeString() возвращает некорректную подпись (обрезает последние 4 символа). Причем эти документы ничем особо не отличаются (ни размером, ни форматом, ни содержанием) от всех остальных (по которым подпись конвертируется корректно).

Если эти документы выгружать в .esd формате, то там подпись верная.

Как добиться того, чтобы MimeEncodeString() работал корректно во всех случаях?

Сталкивался ли кто-то с подобным поведением этой функции?

Андрей Морозов

Коллеги, выгружаю подпись методами которые описаны в данной статье, сам документ экспортирую вручную, при попытке пройти проверку на подлинность на сайте госуслуг  (электронного документа. ЭП — отсоединенная, в формате PKCS#7) выдает что Электронная подпись недействительна, Подлинность документа НЕ ПОДТВЕРЖДЕНА.

При проверке на том же ресурсе ЭП, все ОК. В чем загвоздка?

Дмитрий Сайфулин

Добрый день!
Подскажите, у вас ГОСТ подпись создается нормально? у меня пустой файл p7s (70 байт)

Как можно выявить проблему?

 

Дмитрий Сайфулин

Уточнение, эта подпись не конвертируется

Никита Захаров

Андрей, большое спасибо за функцию !!!
Очень выручил !

Анатолий Придыбайло
Экспорт и импорт отсоединенной электронной подписи: опыт применения развитие и дополнение текущей статьи
Анатолий Придыбайло: обновлено 30.04.2021 в 14:00

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