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.
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
/Pages 2 0 R
2 0 obj
/MediaBox [ 0 0 200 200 ]
/Kids [ 3 0 R ]
3 0 obj
/Parent 2 0 R
/F1 4 0 R
/Contents 5 0 R
4 0 obj
5 0 obj % page content
70 50 TD
/F1 12 Tf
(Hello, world!) Tj
0000000000 65535 f
0000000010 00000 n
0000000079 00000 n
0000000173 00000 n
0000000301 00000 n
0000000380 00000 n
/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.
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>>
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
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
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
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
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 https://www.turboirc.com)/ModDate(D:20181009160112+00'00')>>
0000000000 65535 f
0000166315 00000 n
0000166460 00000 n
0000166230 00000 n
0000166062 00000 n
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>>
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.
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,
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