Click here to Skip to main content
13,865,687 members
Click here to Skip to main content
Add your own
alternative version


Posted 5 Oct 2018
Licenced CPOL

The *AdES Collection, Part 3: PAdES for Windows in C++

, 15 Oct 2018
Rate this:
Please Sign up or sign in to vote.
PDF signing with AdES


The third part of my collection discusses PDF signing with Advanced Electronic Signatures. Unlike CAdES or XAdES, PAdES does not define any new protocol for encryption, but it describes meta-information on how to sign a PDF file. In the PDF file, you can include either a CAdES format or a XAdES one inside the PDF as a detached signature. The levels of signing are similar to what we have seen so far (B, T, etc.) with a few exceptions, so at the moment, our library will be able to create B-B, B-T and B-LT signatures.


PDF is made of some items:

  • %%EOF to indicate end-of-current-revision. PDF can have many revisions.
  • An object index table (xref) which tells the parser where the objects are placed.
  • Objects

Each object contains either data to be seen, or metadata, describing the document.

Here follows a simple Hello World, PDF file:


1 0 obj  % entry point
  /Type /Catalog
  /Pages 2 0 R

2 0 obj
  /Type /Pages
  /MediaBox [ 0 0 200 200 ]
  /Count 1
  /Kids [ 3 0 R ]

3 0 obj
  /Type /Page
  /Parent 2 0 R
  /Resources <<
    /Font <<
      /F1 4 0 R 
  /Contents 5 0 R

4 0 obj
  /Type /Font
  /Subtype /Type1
  /BaseFont /Times-Roman

5 0 obj  % page content
  /Length 44
70 50 TD
/F1 12 Tf
(Hello, world!) Tj

0 6
0000000000 65535 f 
0000000010 00000 n 
0000000079 00000 n 
0000000173 00000 n 
0000000301 00000 n 
0000000380 00000 n 
  /Size 6
  /Root 1 0 R

My library includes a small and very experimental PDF parser which supports many simple PDF files. Much of the code has been compared with the results of jSignPDF. If you can't load a specific PDF file, let me know.

PDF Signatures

A PDF signature has the following properties:

  • It is always detached.
  • It is put inside a special object in the PDF file. The PDF file is first created with enough space to hold the signature, initially filled with zeroes.
22 0 obj
<</Contents <000000 .... 00>
/ByteRange [0 64944 124946 1312]/Filter/Adobe.PPKLite>>

The byterange parameter specifies the portion of the PDF file that is signed. Theoretically, you can sign any portion, but Adobe Reader rejects any signature unless the entire PDF file is signed. Therefore, the byte range is from the start to the '<' character before the 00s, and from the '>' character after the 00s to the end of file. This is the part that will be hashed.

If you put a standard CMS. then the marking is adbe.pkcs7.detached. If you put a CAdES level signature, the marking is ETSI.CAdES.detached.

The main difference between ordinary CAdES signatures and those that are put to the PDF file is that the signature must not contain a timestamp from the current clock, as this information is already put in the PDF file with the /M parameter. Therefore, the OID szOID_RSA_signingTime is not added to the signature when SIGNPARAMS.PAdES = true.

PDF Reconstruction

To add a signature, a new revision must be added to the PDF. Therefore my library:

  • Creates a new root, a pointer to the signature created and a new xref table, mentioning the old revision
  • Replaces info, contents, pages and kids objects with the  the necessary structures to hold the PDF signature while still pointing to the old data.
  • Signs with CAdES.

The following is the added revision after using my library to the above hello world PDF file:

42 0 obj
<</F 132/Type/Annot/Subtype/Widget/Rect[0 0 0 0]/FT/Sig/DR<<>>/T(Signature1)/V 40 0 R/P 7 0 R/AP<</N 41 0 R>>>>
40 0 obj
... signature
43 0 obj
44 0 obj
41 0 obj
<</Type/XObject/Resources<</ProcSet [/PDF /Text /ImageB /ImageC /ImageI]>>/Subtype/Form/BBox[0 0 0 0]/Matrix [1 0 0 1 0 0]/Length 8/FormType 1/Filter/FlateDecode>>stream
xœ     endstream
7 0 obj
<</Type/Page/MediaBox[0 0 595 842]/Rotate 0/Parent 3 0 R/Resources<</ProcSet[/PDF/Text]/ExtGState 22 0 R/Font 23 0 R>>/Contents 8 0 R/Annots[42 0 R]>>
3 0 obj
<</Type/Pages/Kids[7 0 R 

24 0 R
28 0 R
32 0 R]/Count 4/Rotate 0>>
1 0 obj
<</Type/Catalog/AcroForm<</Fields[ 42 0 R]/DR<</Font<</Helv 43 0 R/ZaDb 44 0 R>>>>/DA(/Helv 0 Tf 0 g )/SigFlags 3>>/Pages 3 0 R>>
2 0 obj
<</Producer(AdES Tools'00')>>
0 4
0000000000 65535 f 
0000166315 00000 n 
0000166460 00000 n 
0000166230 00000 n 
7 1
0000166062 00000 n 
40 5
0000105528 00000 n 
0000165857 00000 n 
0000105400 00000 n 
0000165681 00000 n 
0000165780 00000 n 
<</Root 1 0 R/Prev 104519/Info 2 0 R>>

XRef Streams

I won't go too far at this moment, since this article is about PAdES and not about PDF. However, some PDF files have their XRef, not as a plain-text entry but as an object which contains the xref table compressed with zlib. In that case, the XRef generated is an object:

465 0 obj
<</Type/XRef/Index [0 1 358 2 362 1 364 1 460 6 ]/W[1 4 2]/Root 364 0 R/Prev 116/Info 362 0 R/Size 472/ID[<C570CC80F0638E5337E581345C7449FB><17DEE6C01B14632A778C3FC1D5297D97>]/Length 77/Filter/FlateDecode>>stream

The format of this stream is beyond the scope of this article, but it contains the same data as the above text-only XRef.

LT type

A PDF file cannot have parts that are unsigned. Therefore, putting the XL information (certificates and crls) to the CMS as unsigned attributes will not make our signature automatically XL compatible. We have to create a special dictionary, called DSS, which contains indirect references to the certificates and crls, and this is put before signing so it is also signed.  

Using the Code

HRESULT PDFSign(LEVEL lev,const char* data,DWORD sz,const std::vector<CERT>
& Certificates, SIGNPARAMETERS& Params,std::vector<char>& Signature);

The parameters in this function call are identical to the Sign() function in the CAdES article, except that you must pass PAdES = true to the SIGNPARAMETERS structure, and you must pass only one certificate. If you pass more, PAdES will work successfully but Adobe Reader is not able to read multiple certificates in one element (you must re-sign the PDF). Currently, the library works up to the XL level:

  • Pass LEVEL::CMS -> Sign normally (old method)
  • Pass LEVEL::B -> Sign PAdES B-B
  • Pass LEVEL::T -> Sign PAdES B-T
  • Pass LEVEL::XL -> Sign PAdES B-LT

The output is fully compliant with the ETSI verification tools.

My library can sign most PDF files. Password protected PDF files cannot be signed. Signing a PDF file which is already signed might cause incompatibilities. If you have issues, let me know,


Multiple Signatures

Since it is CAdES, it supports multiple certificates. However, Adobe Reader will only show information about the last certificate found in the collection. While the ETSI tools will successfully validate such a PDF, Acrobat only supports a recursive signature: You sign the first PDF file, creating a new signed PDF, then you sign this new PDF to another new PDF. This means that the new signature will also sign the entire previous signature.




  • 15th October, 2018: Better PDF parser, PAdES B-T, PAdES B-LT
  • 9th October, 2018: Enhanced PDF parser, 100% ETSI compliance.
  • 6th October, 2018: First release


This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


About the Author

Michael Chourdakis
Greece Greece
I'm working in C++, PHP , Java, Windows, iOS and Android.

I 've a PhD in Digital Signal Processing and Artificial Intelligence and I specialize in Pro Audio and AI applications.

My home page:

You may also be interested in...

Comments and Discussions

-- There are no messages in this forum --
Permalink | Advertise | Privacy | Cookies | Terms of Use | Mobile
Web04 | 2.8.190214.1 | Last Updated 15 Oct 2018
Article Copyright 2018 by Michael Chourdakis
Everything else Copyright © CodeProject, 1999-2019
Layout: fixed | fluid