Ключевое слово в защите информации
КЛЮЧЕВОЕ СЛОВО
в защите информации
Получить ГОСТ TLS-сертификат для домена (SSL-сертификат)
Добро пожаловать, Гость! Чтобы использовать все возможности Вход или Регистрация.

Уведомление

Icon
Error

2 Страницы12>
Опции
К последнему сообщению К первому непрочитанному
Offline Setix  
#1 Оставлено : 20 июля 2022 г. 12:03:57(UTC)
Setix

Статус: Участник

Группы: Участники
Зарегистрирован: 19.09.2019(UTC)
Сообщений: 28
Российская Федерация
Откуда: МСК

Задача: Подписать документ PDF цифровой подписью

1. Установка необходимого ПО (последовательность): Adobe Acrobat DC x32, КриптоПро 5.0.12500, КриптоПро PDF 2.0.2055 x32.
2. Настройка ПО:
2.1. Установка сертификата в хранилище (устанавливаю в папку личные)
2.2. Adobe Acrobat DC.
2.2.1. "Редактирование" --> "установки...":
2.2.1.1. JavaScript --> Активировать Acrobat JavaScript.
2.2.1.2. Подписи -----> Цифровые подписи -- Создание и оформление --> Подробнее...
Установки создания и оформления -- Создание -- Метод подписания по умолчанию --> КриптоПро PDF.

В результате установки и настройки получаю готовое к применению ПО.
Рекомендую проверить папки на наличие файла: CPPDFDSig.js - в нем описан механизм подписания документа на JS.
1. C:\Program Files (x86)\Adobe\...\Acrobat\Javascripts\
2. C:\Program Files (x86)\Crypto Pro\PDF\

Примечание: (На текущий момент написания) версия ПО Adobe Acrobat DC должна быть x32. x64 НЕ ПОДДЕРЖИВАЕТ КриптоПро PDF 2.0.2055 x32.3
Примечание 2: Adobe Acrobat DC должна быть не демонстрационной, в противном случае выдает ошибку подписания.
Выполнив действия можно проверить подписывается ли документ у вас вручную средствами Adobe Acrobat.

Перехожу к вопросу: почитав форум так и не получилось сформировать подпись для PDF документа. Далее буду выкладывать исходный код и свои вопросы.

Отредактировано пользователем 6 сентября 2022 г. 14:20:39(UTC)  | Причина: Не указана

Offline Setix  
#2 Оставлено : 20 июля 2022 г. 12:21:26(UTC)
Setix

Статус: Участник

Группы: Участники
Зарегистрирован: 19.09.2019(UTC)
Сообщений: 28
Российская Федерация
Откуда: МСК

Код взят с поста на форуме и переписан.

Функция создания подписи для документа:
Код:

Uses Soap.Win.CertHelper, {описание структур для ЭЦП}
     Acrobat_TLB;         {для подписи PDF, появляется в списке Type Library после установки Adobe Acrobat DC}

Function CreateDigitalSignature(const FileNameIn,FileNameOut:string; pCert:PCERT_CONTEXT):WideString;
Var
  I: Integer;
  AVDoc: CAcroAVDoc;
  PDDoc: CAcroPDDoc;
  jso:   Variant{IDispatch};
  CertificateHEX,pin:string;
  pdfLockMode:Integer;
  signReason,signLocation,signContactInfo,timeStamp: string;
  bShowCertificatesDialog,bShowSignDialogs: Boolean;
//  gApp: CAcroApp;
Begin
  Result:=''; 
  if Assigned(pCert) then
    if FileExists(FileNameIn) then
      Begin
{
//Не понятно зачем эта часть кода так как она дальше не используется: 
//единственное что в голову пришло чтобы посмотреть настройки программы Acrobat Reader. 
        gApp := CoAcroApp.Create;
        gApp.Show;
}
        AVDoc:= CoAcroAVDoc.Create;
        if AVDoc.Open(FileNameIn,FileNameOut) then
          begin
            PDDoc:=AVDoc.GetPDDoc as CAcroPDDoc;
            jso:=PDDoc.GetJSObject;  
//          1. certificateHEX String. Обязательный параметр. Массив байт сертификата, используемого для создания ЭП, в
//             виде строки шестнадцатеричных цифр. Сертификат должен быть установлен в хранилище «Личные»
//             текущего пользователя и иметь ссылку на закрытый ключ.
            CertificateHEX:=CertificateToHEX(pCert);
//          2. pin String. PIN-код для доступа к закрытому ключу.
            pin:='...';
//          3. pdfLockMode Numeric. Число, характеризующее тип создаваемой
//             подписи и действия, разрешенные с документом после подписания:
//             0 - обычная подпись, изменения разрешены;
//             1 - обычная подпись, документ заблокирован;
//             2 - сертифицирующая подпись, изменения запрещены;
//             3 - сертифицирующая подпись, разрешается заполнение полей форм и создание ЭП;
//             4 - сертифицирующая подпись, разрешается добавление комментариев,
//             заполнение полей форм и создание ЭП.
            pdfLockMode:=1;
//          4. signReason String. Причина подписи.
            signReason:='signReason:причина подписи';
//          5. signLocation String Местоположение.
            signLocation:='signLocation:Москва';
//          6. signContactInfo String. Контактная информация.
            signContactInfo:='signContactInfo:Контактная информация';
//          7. timeStamp String. URL службы штампов времени. Если данный
//             параметр задан, то будет сделана усовершенствованная подпись.
            timeStamp:='http://qs.cryptopro.ru/tsp/tsp.srf';
//          8. bShowCertificatesDialog Boolean. Флаг, указывающий будут ли отображены
//             диалог со списком сертификатов и диалог для ввода ПИН-кода.
            bShowCertificatesDialog:=False;
//          9. bShowSignDialogs Boolean Флаг, указывающий будут ли отображены
//             диалог с параметрами подписи и диалог для сохранения файла.
            bShowSignDialogs:=False;
//          10. appearance Object. Объект, определяющий параметры поля
//              подписи и его содержимое. 
//              Если параметр не задан, то будет создана невидимая подпись.
//              Как корректно описать данный объект я не понял вопросы по нему будут ниже.
//              НА текущий момент считаем что appearance=''.  
            Result:=jso.CPSign(certificateHEX,
                               pin,
                               pdfLockMode,
                               signReason,
                               signLocation,
                               signContactInfo,
                               timeStamp,
                               bShowCertificatesDialog,
                               bShowSignDialogs,
                               ''
                               );
          end;
      End
    else Result:=Format('Файл не найден: [%s]',[FileNameIn])
  else Result:=Format('Адрес сертификата не найден: [%p]',[pCert]);

Отредактировано пользователем 20 июля 2022 г. 18:05:58(UTC)  | Причина: Не указана

Offline Setix  
#3 Оставлено : 20 июля 2022 г. 12:29:50(UTC)
Setix

Статус: Участник

Группы: Участники
Зарегистрирован: 19.09.2019(UTC)
Сообщений: 28
Российская Федерация
Откуда: МСК

Теперь рассматриваю по пунктам:
1. certificateHEX String.
Обязательный параметр. Массив байт сертификата, используемого для создания ЭП, приведенный к HEX строке.
Сертификат должен быть установлен в хранилище «Личные» текущего пользователя и иметь ссылку на закрытый ключ.
Функция для получения сертификата в виде строки шестнадцатеричных цифр:
Код:

Uses Soap.Win.CertHelper; {описание структур для ЭЦП}

Function BytesToHex(Value: PAnsiChar; siz: Integer):string;
var
  i: Integer;
begin
  Result := '';
  if (Assigned(Value)) and (siz>0) then
    for i := 0 to siz-1 do
      Result := Result + IntToHex(Byte(Value[i]), 2);
End;

Function CertificateToHEX(pCert:PCERT_CONTEXT):string;
var
  certValue: string;
begin
  SetLength(certValue,pCert.cbCertEncoded);
  system.Move(pCert.pbCertEncoded^, pointer(certValue)^, pCert.cbCertEncoded);
  Result:=BytesToHex(PAnsiChar(certValue),pCert.cbCertEncoded);
end; 


Для проверки корректности результата можно сравнить с сертификатом открытом в HEX редакторе.
Замечание: сертификат для сравнения экспортировал в формат X.509 (.CER) в кодировке DER.

Отредактировано пользователем 20 июля 2022 г. 18:02:53(UTC)  | Причина: Не указана

Online Андрей *  
#4 Оставлено : 20 июля 2022 г. 12:33:45(UTC)
Андрей *

Статус: Сотрудник

Группы: Участники
Зарегистрирован: 26.07.2011(UTC)
Сообщений: 13,322
Мужчина
Российская Федерация

Сказал «Спасибо»: 549 раз
Поблагодарили: 2208 раз в 1723 постах
Здравствуйте.

1. Странно, что не написано главное - наименование среды разработки, но мне понятно.
2. Панель управления\КриптоПРО CSP\Сервис\Протестировать\По сертификату - должно быть без ошибок
3. Что мешает вызвать функции с разной реализацией и сверить результаты, между собой и независимым способом (hex редактор и сертификат)?
Техническую поддержку оказываем тут
Наша база знаний
Offline Setix  
#5 Оставлено : 20 июля 2022 г. 12:38:22(UTC)
Setix

Статус: Участник

Группы: Участники
Зарегистрирован: 19.09.2019(UTC)
Сообщений: 28
Российская Федерация
Откуда: МСК

Автор: Андрей * Перейти к цитате

1. Странно, что не написано главное - наименование среды разработки, но мне понятно.

среда XE3. -пропустил так как в теме указана.

Автор: Андрей * Перейти к цитате
Здравствуйте.
2. Панель управления\КриптоПРО CSP\Сервис\Протестировать\По сертификату - должно быть без ошибок

Это пригодится для проверки сертификата. Там ошибок не обнаружено.

Автор: Андрей * Перейти к цитате

3. Что мешает вызвать функции с разной реализацией и сверить результаты, между собой и независимым способом (hex редактор и сертификат)?

Я почти не работал с сертификатами, поэтому я только разбираюсь ...
Мешает то, что я не понимаю какой я результат должен увидеть. С чем сравнивать?
Если сравнивать сертификат и то что получаю в результате Function CertificateToHEX(Cert:PCERT_CONTEXT):string; оба способа выдают данные не похожие на сертификат в HEX.
Я должен получить, в результате работы функции, строку представляющую собой вид сертификата в HEX редакторе?

Отредактировано пользователем 20 июля 2022 г. 12:53:07(UTC)  | Причина: Не указана

Offline Setix  
#6 Оставлено : 20 июля 2022 г. 12:56:40(UTC)
Setix

Статус: Участник

Группы: Участники
Зарегистрирован: 19.09.2019(UTC)
Сообщений: 28
Российская Федерация
Откуда: МСК

Для получения сертификата использую его отпечаток:
Код:

Uses Soap.Win.CertHelper, {описание структур для ЭЦП}
     System.Classes,
     Winapi.Windows;  

type
  CRYPT_HASH_BLOB = record
    cbData: DWORD;
    pbData: PBYTE;
  end;

Function CertificateByThumbprint(Thumbprint:String):PCERT_CONTEXT;
//Thumbprint - отпечаток.
var
  sz: Integer;
  buf:array of byte;
  certHash: CRYPT_HASH_BLOB;
  hStore:HCERTSTORE;
begin
  Result:=NIL;
  Thumbprint:=AnsiUpperCase(Trim(Thumbprint)); //убираем пробелы с начала и конца приводим к верхнему регистру.
  sz:=length(Thumbprint);                      //размер строки отпечатка.
  if sz>0 then
    begin
      sz:=sz div 2;        //каждые 2 символа в строке отпечатка представляют 1 байт данных.
      SetLength(buf,sz);   //выделяем размер под buf.
      System.Classes.HexToBin(PChar(Thumbprint),PChar(buf),sz);  //переводим 16 представление в двоичное.
      //для поиска сертификата.
      certHash.cbData:=sz;
      certHash.pbData:=PByte(buf);
      hStore:=CertOpenSystemStore(0,'MY'); //адрес хранилища сертификатов.
      Result:=CertFindCertificateInStore(hStore,                                 //адрес хранилища сертификатов.
                                       PKCS_7_ASN_ENCODING or X509_ASN_ENCODING, //тип кодировки
                                       0,                                        //Задает модификатор критериев поиска при использовании
                                                                                 //с определенными значениями. Для большинства значений этот параметр
                                                                                 //не используется и должен быть равен нулю. Для получения подробной информации
                                                                                 //см. раздел «Примечания» https://docs.microsoft.com/en-us/previous-versions/windows/embedded/ms883817(v=msdn.10).
                                       CERT_FIND_SHA1_HASH,                      //Ищет сертификат с хешем SHA1, совпадающим с хешем в certHash.
                                       @certHash,                                //Указывает на элемент данных или структуру, используемую для поиска.
                                       Nil                                       //Указатель на последнюю структуру CERT_CONTEXT, возвращаемую этой функцией.
                                                                                 //Чтобы найти последовательные сертификаты, соответствующие критериям поиска,
                                                                                 //задайте указатель, возвращенный предыдущим вызовом функции.
                                                                                 //Эта функция освобождает CERT_CONTEXT, на который ссылаются значения этого параметра,
                                                                                 //отличные от NIL
                                       );
      CertCloseStore(hStore,CERT_CLOSE_STORE_CHECK_FLAG);                        //Проверяет контексты неосвобожденного сертификата, CRL и CTL.
                                                                                 //Возвращаемый код ошибки указывает на то, что один или несколько элементов
                                                                                 //хранилища все еще используются. Этот флаг следует использовать только
                                                                                 //в качестве диагностического инструмента при разработке приложений.
    end;
end;

Отредактировано пользователем 20 июля 2022 г. 13:14:36(UTC)  | Причина: Не указана

Online Андрей *  
#7 Оставлено : 20 июля 2022 г. 13:05:52(UTC)
Андрей *

Статус: Сотрудник

Группы: Участники
Зарегистрирован: 26.07.2011(UTC)
Сообщений: 13,322
Мужчина
Российская Федерация

Сказал «Спасибо»: 549 раз
Поблагодарили: 2208 раз в 1723 постах
Автор: Setix Перейти к цитате
Автор: Андрей * Перейти к цитате

1. Странно, что не написано главное - наименование среды разработки, но мне понятно.

среда XE3. -пропустил так как в теме указана.


Печально, это версия... ну да ладно.
Техническую поддержку оказываем тут
Наша база знаний
Offline Setix  
#8 Оставлено : 20 июля 2022 г. 13:06:30(UTC)
Setix

Статус: Участник

Группы: Участники
Зарегистрирован: 19.09.2019(UTC)
Сообщений: 28
Российская Федерация
Откуда: МСК

10. appearance Object. Объект, определяющий параметры поля подписи и его содержимое.
Если параметр не задан, то будет создана невидимая подпись.
Вопрос каким образом описать данный Object? Как его подавать в процедуру?
В моем понимании Object это экземпляр класса. Т.е.

Код:

type
  Tappearance = Class(TObject)
    pageNumber: Integer;
    coords: array[0..3] of integer;
    appearanceFilter: string;
  end;

Тем самым, чтобы подать его в процедуру jso.CPSign необходимо:
Код:

type
  Tappearance = Class(TObject)
    pageNumber: Integer;
    coords: array[0..3] of integer;
    appearanceFilter: string;
  end;

var appearance:Tappearance;
begin
  Appearance:=Tappearance.Create;
  Appearance.pageNumber := PDDoc.GetNumPages - 1;  //PDDoc как создается написан во 2 посте.
  Appearance.coords[0] := 1;
  Appearance.coords[1] := 5;
  Appearance.coords[2] := 400;
  Appearance.coords[3] := 400;
  Appearance.appearanceFilter := 'appearanceFilter';
  jso.CPSign(...,...,Appearance);  
  appearance.Free;
end;

При компиляции происходит ошибка [dcc32 Error] Digital_Signature.pas(251): E2281 Type not allowed in Variant Dispatch call. на строке jso.CPSign(...,...,Appearance);
Т.е. Appearance - я подаю не корректно. В некоторых постах в качестве Appearance подают строку Пример Appearance='{"pageNumber": 0, "coords": [10, 10, 50, 50], "appearanceFilter": "Fancy"}'.
Как я понял есть 2 варианта указания этого параметра из кода JS в CPPDFDSig.js. Можно привести примеры...

Отредактировано пользователем 20 июля 2022 г. 13:52:51(UTC)  | Причина: Не указана

Offline Setix  
#9 Оставлено : 20 июля 2022 г. 13:09:46(UTC)
Setix

Статус: Участник

Группы: Участники
Зарегистрирован: 19.09.2019(UTC)
Сообщений: 28
Российская Федерация
Откуда: МСК

Автор: Андрей * Перейти к цитате
Автор: Setix Перейти к цитате
Автор: Андрей * Перейти к цитате

1. Странно, что не написано главное - наименование среды разработки, но мне понятно.

среда XE3. -пропустил так как в теме указана.


Печально, это версия... ну да ладно.

На работе XE3, дома RAD 11. Это не должно особо повлиять на результаты работы функции.
Offline two_oceans  
#10 Оставлено : 21 июля 2022 г. 8:08:19(UTC)
two_oceans

Статус: Эксперт

Группы: Участники
Зарегистрирован: 05.03.2015(UTC)
Сообщений: 1,602
Российская Федерация
Откуда: Иркутская область

Сказал(а) «Спасибо»: 110 раз
Поблагодарили: 395 раз в 366 постах
Цитата:
CertificateToHEX

Добрый день.
Честно, я не рекомендую обращаться по индексу к символам в типе PAnsiChar. Причина в том, что этот тип "прыгает" в памяти если что-то делать с символами, еще и преобразования string в PAnsiChar потом еще и char в byte, выйдет мешанина из случайного заполнения памяти.
Необходимости копировать из контекста сертификата нет. Еще кажется не разыменовали pCert в коде выше (так как PCERT_CONTEXT суть указатель и прежде чем работать с полями контекста сертификата надо ставить крышечку). Я бы сделал как-то так:
Код:
Uses Soap.Win.CertHelper; {описание структур для ЭЦП}
type bytearr=array[0..262143] of byte;
pbytearr=^bytearr;

Function BytesToHex(Value: pbytearr; siz: Integer):string;
var
  i: Integer;
begin
  Result := '';
  if (not IsBadReadPtr(pbytearr,siz)) and (siz>0) then
    for i := 0 to siz-1 do
      Result := Result + IntToHex(Value[i], 2);
End;

Function CertificateToHEX(pCert:PCERT_CONTEXT):string;
begin
  Result:=BytesToHex(pointer(pCert^.pbCertEncoded),pCert^.cbCertEncoded);
  {преобразование в pointer на всякий случай, так как не знаю как поле описано в Soap.Win.CertHelper, иногда среда сопротивляется присвоению типизированных указателей, но pointer влезает везде}
end;

Отредактировано пользователем 21 июля 2022 г. 8:14:52(UTC)  | Причина: Не указана

RSS Лента  Atom Лента
Пользователи, просматривающие эту тему
Guest (3)
2 Страницы12>
Быстрый переход  
Вы не можете создавать новые темы в этом форуме.
Вы не можете отвечать в этом форуме.
Вы не можете удалять Ваши сообщения в этом форуме.
Вы не можете редактировать Ваши сообщения в этом форуме.
Вы не можете создавать опросы в этом форуме.
Вы не можете голосовать в этом форуме.