Do you own a Debenu Quick PDF Library version 7, 8, 9, 10, 11, 12, 13 or iSEDQuickPDF license? Upgrade to Debenu Quick PDF Library 14 today!

Debenu Quick PDF Library - PDF SDK Community Forum Homepage
Forum Home Forum Home > For Users of the Library > I need help - I can help
  New Posts New Posts RSS Feed - Digital Signature
  FAQ FAQ  Forum Search   Register Register  Login Login

Digital Signature

 Post Reply Post Reply
Author
Message
MauricioPt View Drop Down
Beginner
Beginner


Joined: 31 Oct 22
Location: PORTUGAL
Status: Offline
Points: 3
Post Options Post Options   Thanks (0) Thanks(0)   Quote MauricioPt Quote  Post ReplyReply Direct Link To This Post Topic: Digital Signature
    Posted: 31 Oct 22 at 2:50PM
Hi,

i have translated the sample source code from C# and VB from here :
https://www.debenu.com/kb/advanced-options-signing-pdf-files/
into Delphi language.

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, DebenuPDFLibrary,
  cyStrUtils, cyDebenuPDFLibrary, idGlobal, IdCoderMIME,

  ssl_const, ssl_types, ssl_init, ssl_err, ssl_evp, ssl_bio, ssl_pem, ssl_engine, ssl_x509, ssl_pkcs7, ssl_pkcs12, ssl_rsa;

procedure TFrmPrinDemoPdf.SignDataWithPfxFile(const AFileName, Password: String; const DataToSign: AnsiString; var Result: ansistring);
var
  LM: TMemoryStream;
  AnsiPassword: AnsiString;
  PAnsiPassword: PAnsiChar;

  B: PBIO;
  P12: PPKCS12;

  // Pfx extaction :
  pCertificate: PX509;
  pkey: PEVP_PKEY;
  ca: PSTACK_OF_X509;
  md: PEVP_MD;

  // Hash :
  mdctx: EVP_MD_CTX;
  AnsiDataToHash: AnsiString;
  LengthAnsiDataToHash: integer;
  HashBuffer: Array of AnsiChar;    //  Array [0..4095] of AnsiChar;
  HashBufferF: Array [0..4095] of AnsiChar;
  c, HashSize: integer;

  // Sign :
  AnsiDataToSign: AnsiString;
  LengthAnsiDataToSign: integer;
  ppKeyCtx: PPEVP_PKEY_CTX;
  pEngin: PENGINE;
  SignBuffer: Array of AnsiChar;    //  Array [0..4095] of AnsiChar;
  SignSize: cardinal;

  // Test
  bp, b64, mbp: PBIO;
  signFile: string;
  _RSA: PRSA;

  // PKCS7 :
  _pPKCS7: PPKCS7;
  _flags: integer;
  _certs: PSTACK_OF;
  InputBioFile, OutputBioFile: PBIO;

  aIdBytes: TIdBytes;
  pmdctx: PEVP_MD_CTX;
  // f: file;
begin
  Result := '';

  B := nil;
  P12 := nil;
  pkey := Nil;
  pCertificate := Nil;
  ca := Nil;

  LM := nil;
  AnsiPassword := Copy(Password, 1, Length(Password));
  PAnsiPassword := PAnsichar(AnsiPassword);
  //ShowMessage( intToStr(Length(AnsiPassword)) );

  // raise exception SSL_InitLib;
  SSL_InitPEM;
  SSL_InitERR;
  SSL_InitBIO;
  SSL_InitEVP;

  // Ajouté !
  //SSL_InitRSA;
  SSL_InitPKCS7;
  //SSL_InitX509;
  //SSL_InitPKCS12;

  // Exception SSL_InitENGINE;
  SSL_InitX509;
  SSL_InitPKCS12;

  // Exception ENGINE_load_builtin_engines;
  OpenSSL_add_all_ciphers;
  OpenSSL_add_all_digests;

  //  OpenSSL_add_all_algorithms;
  ERR_load_crypto_strings;

  try
    LM := TMemoryStream.Create;
    LM.LoadFromFile(AFileName);
  except
    LM.Free;
    Exit;
  end;

  try
    B := BIO_new_mem_buf(LM.Memory, LM.Size);
    if not Assigned(B) then
    begin
      Exit;
    end;

    // * Créer le PPKCS12 * //
    try
      P12 := ssl_pkcs12.d2i_PKCS12_bio(B, nil);
      if Assigned(P12) then
      begin
        try
          // * Vérifier le mot de passe (!!! Ne fonctionne que si nous avons appelé tous les SSL_Init + OpenSSL_add !!!) * //
          if PKCS12_verify_mac(P12, PAnsiChar(AnsiPassword), Length(AnsiPassword)) = 1 then   // 0 s'il y a un pb ...      PAnsiChar(AnsiPassword) ou PAnsiPassword ...
          begin
            // * Récupérer le certificat X509 * //
            PKCS12_parse(p12, PAnsiChar(AnsiPassword), @pkey, @pCertificate, @ca);       // PAnsiChar(AnsiPassword) ou PAnsiPassword ...

            if Assigned(pCertificate) then
            begin
              // INFO :              ShowMessage(pCertificate^.name);
            end;

            // * Sign * //
            if Assigned(pkey) and CBSignProcessPassthroughSHA1.Checked then
              try
                // Create an SHA-1 hash of the file data //
                md := EVP_get_digestbyname('SHA1');         //  EVP_sha256;
                // Pas besoin ... New(mdctx);

                if EVP_SignInit(@mdctx, md) = 1 then      // Ou directement EVP_SignInit(@md_ctx, EVP_SHA1);
                  try
                    // SSL_CheckError;
                    AnsiDataToHash := DataToSign;
                    LengthAnsiDataToHash := Length(AnsiDataToHash);

                    // Tableau dynamique :
                    if EVP_SignUpdate(@mdctx, PAnsiChar(AnsiDataToHash), LengthAnsiDataToHash) = 1 then
                    begin
                      SetLength(HashBuffer, EVP_PKEY_size(pKey));

                      if EVP_SignFinal(@mdctx, PAnsiChar(HashBuffer), HashSize, pkey) = 1 then     // HashSize = 256 cars
                      begin
                        Result := '';
                        c := 0;
                        while c < HashSize do
                        begin
                          Result := Result + HashBuffer[c];
                          inc(c);
                        end;

                        MemLogs.Lines.Add('Data to SHA1 : "' + Copy(AnsiDataToHash, 1, 20) + '..."');
                        MemLogs.Lines.Add('HASH result : "' + Result + '"');
                        string_Savetofile(Result, 'D:\_DELPHI XE10.2\_TEST SIGN\_DEBUG\QuickPDF\Certificat\SetSignProcessPassthrough 04 hash result.txt', TEncoding.ANSI);
                      end;
                    end;
                  finally
                  end;
              finally
              end;

            // PKCS 7 //
            if Assigned(pkey) and CBSignProcessPassthroughPkcs7.Checked then
              try
                MemLogs.Lines.Add('HASH to sign (PKCS7) : "' + string_LoadFromfile('D:\_DELPHI XE10.2\_TEST SIGN\_DEBUG\QuickPDF\Certificat\SetSignProcessPassthrough 04 hash result.txt', TEncoding.ANSI) + '"');

                // Range du pdf ! InputBioFile := BIO_new_file('D:\_DELPHI XE10.2\_TEST SIGN\_DEBUG\QuickPDF\Certificat\SetSignProcessPassthrough.data_to_sign.txt','rb');
                InputBioFile := BIO_new_file('D:\_DELPHI XE10.2\_TEST SIGN\_DEBUG\QuickPDF\Certificat\SetSignProcessPassthrough 04 hash result.txt','rb');      // TODO: charger via la variable Result //
                OutputBioFile := BIO_new_file('D:\_DELPHI XE10.2\_TEST SIGN\_DEBUG\QuickPDF\Certificat\SetSignProcessPassthrough 05 pkcs7 signature.p7m','wb');

                _certs := nil; // := ca fait augmenter la taille du pdf
                _flags := ssl_const.PKCS7_NOATTR;// or ssl_const.PKCS7_STREAM;     // _flags info: If "PKCS7_NOCERTS" is set, the signer's certificate will not be included in the PKCS7 structure
                _pPKCS7 := ssl_pkcs7.PKCS7_sign(pCertificate, pkey, _certs, InputBioFile, _flags);     // il faut appeler SSL_InitPKCS7 avant !
                ssl_pkcs7.i2d_PKCS7_bio_stream(OutputBioFile, _pPKCS7, InputBioFile, _flags);      //  SMIME_write_PKCS7( OutputBioFile, p7, InputBioFile, flags );

                PKCS7_free(_pPKCS7);
                BIO_Free(InputBioFile);
                BIO_Free(OutputBioFile);   // Le fichier est écrit une fois le free effectué !

                Result := string_LoadFromfile('D:\_DELPHI XE10.2\_TEST SIGN\_DEBUG\QuickPDF\Certificat\SetSignProcessPassthrough 05 pkcs7 signature.p7m', TEncoding.ANSI);    // TODO: Sauvegarder dans la variable Result //
                MemLogs.Lines.Add('Sign (PKCS7) result : "' + Result + '"');
              finally
              end;

          end
          else begin
            // ERR_get_error;
            // ERR_print_errors(B);
          end;
        finally
          PKCS12_free(P12);
        end;
      end;
    finally
      BIO_free(B);
    end;
  finally
    FreeAndNil(LM);
  end;
end;

procedure TFrmPrinDemoPdf.BtnSetSignProcessPassthroughClick(Sender: TObject);
var
  FichierEntreePdf, FichierSortiePdf: string;
  DataToSign, ResultatSignature, LoadSignature: AnsiString;
  FichierImage, FichierCert, SignatureFieldName, Reason, Location, ContactInfo: string;
  PfxPassword: AnsiString;

  PDFLibrary: TDebenuPDFLibrary;
  UnlockResult, LoadResult, SignProcessID, TestByteSignature, SignatureByteLength, X, Rslt: Integer;
  hashRange: array[0..7] of integer;
  LM: TMemoryStream;
  filedata: TBytes;
  filedataLength: Integer;
  BytesRead: Integer;
  Hex: string;
begin
  MemLogs.Lines.Clear;
  FichierCert := 'c:\certificate.pdf';
  PfxPassword := '';

  FichierEntreePdf := 'c:\input.pdf';
  FichierSortiePdf := 'c:\signed.pdf';

  // Estimate how much space we need for the signature by first signing some random data
  DataToSign := 'calc need bytes';
  SignDataWithPfxFile(FichierCert, PfxPassword, DataToSign, ResultatSignature);

  TestByteSignature := Length(ResultatSignature);
  MemLogs.Lines.Add('Bytes de la signature : ' + intToStr(TestByteSignature) );

  PDFLibrary := TDebenuPDFLibrary.Create;
  UnlockResult := PDFLibrary.UnlockKey('my licence key');

  if UnlockResult = 0 then
  begin
    PDFLibrary.Free;
    Exit;
  end;

  try
    Rslt := 0;   // Signé ?
    SignatureFieldName := 'FIELD_SIGN';
    FichierImage := 'c:\image.png';
    Reason := 'DEBUGGING ...';
    Location := 'PORTUGAL';
    ContactInfo := 'Mauricio';

    // SignatureByteLength := TestByteSignature * 2 + 512;
    // Number of bytes to reserve for the signature placeholder. We need double the bytes because they will will be stored in hex format, and we add 512 bytes extra margin :
    // Non car le fonction SetSignProcessPassthrough duplique déjà les bytes comme indiqué ici:
    SignatureByteLength := TestByteSignature + 256;    // SetSignProcessPassthrough already duplicates bytes !

    SignProcessID := PDFLibrary.NewSignProcessFromFile(FichierEntreePdf, '');

    if SignProcessID <> 0 then
      try
        // In this mode, the PDF is prepared using a placeholder for the signature data. The user can then replace this placeholder with the signature data of their choice
        // using the GetSignProcessByteRange function to determine the byte range that the signature hashing should be calculated over.
        // The length in bytes of the raw binary signature data. This value will be doubled when allocating the space in the PDF as the signature data should be written using hex encoding (two characters per raw byte).
        Rslt := PDFLibrary.SetSignProcessPassthrough(SignProcessID, SignatureByteLength);

        if Rslt = 1 then
        begin
          PDFLibrary.SetSignProcessInfo(SignProcessID, Reason, Location, ContactInfo);
          PDFLibrary.SetSignProcessField(SignProcessID, SignatureFieldName);
          PDFLibrary.SetSignProcessFieldBounds(SignProcessID, 100, 600, 200, 100);

          if FichierImage <> '' then
            PDFLibrary.SetSignProcessFieldImageFromFile(SignProcessID, FichierImage, 0);

          PDFLibrary.EndSignProcessToFile(SignProcessID, FichierSortiePdf);     // Sauvegarde le fichier sur le disque

          Rslt := PDFLibrary.GetSignProcessResult(SignProcessID);

          if Rslt = 1 then
            if FileExists(FichierSortiePdf) then
            begin
              // Byte ranges //
              for X := 0 to 7 do
              begin
                hashRange[X] := PDFLibrary.GetSignProcessByteRange(SignProcessID, X + 1);
                MemLogs.Lines.Add('hashRange[' + intToStr(X) + '] : ' + intToStr(hashRange[X]) );
                // hashRange[4] hashRange[5] hashRange[6] et hashRange[7] à 0
              end;

              // Read the file data into a byte array, missing out the placeholder for the signature data //
              filedataLength := hashRange[1] + (hashRange[3]);  // Visual Basic sample do -1 but not C# sample ... := hashRange[1] + (hashRange[3] - 1);
              SetLength(filedata, filedataLength);

              try
                LM := TMemoryStream.Create;
                LM.LoadFromFile(FichierSortiePdf);

                LM.Seek(hashRange[0], TSeekOrigin.soBeginning);   // Seek() Déplace la position en cours dans le flux de Offset octets par rapport à la position de départ spécifiée par Origin.
                BytesRead := LM.Read(filedata, 0, hashRange[1]);  // La méthode Read permet de lire le contenu du flux mémoire dans un tampon en partant de la position en cours
                LM.Seek(hashRange[2], TSeekOrigin.soBeginning);
                BytesRead := LM.Read(filedata, hashRange[1], hashRange[3]);

                // Sign the file data (generates an SHA-1 hash and signs that hash) /
                SetString(DataToSign, PAnsiChar(filedata), filedataLength);

                SignDataWithPfxFile(FichierCert, PfxPassword, DataToSign, ResultatSignature);

                // Convert the signature to hex format //
                Hex := AnsistringToHex(ResultatSignature);       // Length(Hex) = 2 x Length(ResultatSignature) = 512 ...

                // Write the signature into the placeholder //
                LM.Seek(hashRange[1] + 1, TSeekOrigin.soBeginning);
                filedata := BytesOf(Hex);
                LM.Write(filedata, length(filedata));

                LM.SaveToFile(FichierSortiePdf);
              finally
                LM.Free;
              end;
            end;
        end;
      finally
        PDFLibrary.ReleaseSignProcess(SignProcessID); // Releases a signature process from memory
      end;

    if Rslt = 1 then
      ShowMessage('Signé');
  finally
    PDFLibrary.Free;
  end;

  MemLogs.SelStart := 0;
  MemLogs.SelLength := 0;
end;

I have written SignDataWithPfxFile() function with open_ssl library.

I can see the certificate but not Acrobat says that the document has been modified.  
Any help will be appreciate.
Back to Top
MauricioPt View Drop Down
Beginner
Beginner


Joined: 31 Oct 22
Location: PORTUGAL
Status: Offline
Points: 3
Post Options Post Options   Thanks (0) Thanks(0)   Quote MauricioPt Quote  Post ReplyReply Direct Link To This Post Posted: 31 Oct 22 at 2:52PM
I can see the certificate but Acrobat says that the document has been modified.  
Any help will be appreciate.
Back to Top
samukinha View Drop Down
Beginner
Beginner
Avatar

Joined: 23 Nov 22
Location: Portugal
Status: Offline
Points: 2
Post Options Post Options   Thanks (0) Thanks(0)   Quote samukinha Quote  Post ReplyReply Direct Link To This Post Posted: 23 Nov 22 at 6:08PM
Hi Mauricio
What is your Debenu version? I have 13.12 version.
I don't have binaries for Delphi 11 to test, if you can send me...
Regards
Back to Top
samukinha View Drop Down
Beginner
Beginner
Avatar

Joined: 23 Nov 22
Location: Portugal
Status: Offline
Points: 2
Post Options Post Options   Thanks (0) Thanks(0)   Quote samukinha Quote  Post ReplyReply Direct Link To This Post Posted: 25 Nov 22 at 2:22PM
Hi Mauricio
What is your Debenu version? I have 13.12 version.
I don't have binaries for Delphi 11 to test, if you can send me...
Regards
Back to Top
 Post Reply Post Reply
  Share Topic   

Forum Jump Forum Permissions View Drop Down

Forum Software by Web Wiz Forums® version 11.01
Copyright ©2001-2014 Web Wiz Ltd.

Copyright © 2017 Debenu. Debenu Quick PDF Library is a PDF SDK. All rights reserved. AboutContactBlogSupportOnline Store