1

I'm trying to create an open source library for Digital Signing of PDF files.

Bad parameter

I got most of it done, but I have a problem that the signature shows the following error:

Error during signature verification.

Adobe Acrobat error.
Bad parameter.

I tried to find the problem, but until now have not found it. I have created 2 pdf files that are striped of almost all other data, except the needed info.

Does anyone know where this error might originate from? I have already tried different online and offline validators, but non of them pointed me in the right direction. Does anyone know if this error might originate from the certificate and not the pdf struture itself?

Invalid byte range

While creating this post I also tested it on other pdf file too, but got the error:

Error during signature verification.

Unexpected byte range values defining scope of signed data.
Details: The signature byte range is invalid

Note a slice of the pdf describes it as:

...
/SubFilter/adbe.pkcs7.detached
/ByteRange[0 4197 22193 30080       ]
/Contents<30820...

I have multiple times recalculated the ByteRange attribute and even tried changing it by one byte in each direction, but that will always result in Signature processing error.. I don't know what else can be incorrect about the ByteRange. (the added spaces are the same as how Acrobat pads the byterange.)

If anyone might have an idea on what the problem might is, let me know.

Files

Here are my resulting files:

A signature file (same as the Contents field in a pdf, excepts directly in separate file):

The content of the signature.der is printed here: https://pastebin.com/W4EGJ2fX (using openssl cms -inform DER -in signature.der -cmsout -print command)

(I know the signature is self signed and does not contains a lot of info, but this should not matter for this, I think, this was just to create these examples)

Edit: New links after solving some problems and added some extra files:

Ralph Bisschops
  • 1,888
  • 1
  • 22
  • 34
  • What program/command is generating those error messages? When I open the first two files in Acrobat the UI reports there are "problems" and lists: "Signer's identity is unknown..." and "Signing time is not available". – Zach Young Jun 02 '22 at 19:21
  • In Acrobat when you open the file and click on the signature (on the page). https://i.imgur.com/eD1TUMc.png a valid signature should show something like this: https://i.imgur.com/xuxEEcI.png – Ralph Bisschops Jun 02 '22 at 20:10
  • 2
    The byte ranges for the last 2 files are indeed incorrect: for the first one it should be [0 4011 22008 164] and for the second one it should be [0 4197 22193 30054] – iPDFdev Jun 03 '22 at 12:51
  • @iPDFdev, thanks for taking a look at this, but are you sure? I changed the `[0 4012 22008 164]` to `[0 4011 22008 164]` (so `12` to `11`) And this results in an incorrect signature. https://drive.google.com/file/d/199Hd-ohtUwVgi0CDzO00lF5oVK900Nj5/view?usp=sharing But that might also be because of something else. (the spec said the `<` and `>` should be included in the hash, but this will always result in `SigDict /Contents illegal data`) The `[0 4197 22193 30054]` goes out of range of the pdf, `22193+30054=52247` and the file is only `52246` bytes long. – Ralph Bisschops Jun 03 '22 at 13:58
  • The < > are not included in the hash as they are part of the /Contents entry. Updating the /ByteRange fixes the Byte range error only. The other problems with your signature are detailed in @mkl answer. – iPDFdev Jun 06 '22 at 08:23
  • 2
    Concerning your new examples: The claimed signing time is May 30th but the signing certificate is not valid before June 7th. Furthermore, the signed attribute still are not DER encoded. But in my opinion the "Bad parameter" is caused by the uncommon signature field structure. – mkl Jun 13 '22 at 15:12

1 Answers1

2

There are some errors in your signature and an uncommon structure which in the context of digital signatures may result in rejection by a validator.

Incorrect Signed Hash Value Inside Signature Container

Signing in CMS signature containers with signed attributes makes use of two hash values:

  • the hash value of the signed byte ranges of the PDF; that value is correct in your example files;
  • the hash value of the signed attributes in the SignerInfo of the signature container; that value is not correct in your example files.

PS: Looking into the mismatch once again, it turns out that your signed attributes are not DER encoded: The DER encoding in particular sorts the elements of a SET in a specific order, and in your case the attribute order is not the DER order. The specification requires the signed attributes to be DER encoded, though.

PPS: In a comment you argued

I just checked the order of the SET and I can not find anything that is wrong with it. Here is my reasoning, let me know what part is incorrect. The items should be order according to the 'key' which in my case is an Oid.

First of all, this reasoning is flawed: The type in question is a set type (more exactly an ASN.1 SET OF), not some map type; and the DER encoding rule set only knows the ASN.1 base types. Thus, that OID (which just is an arbitrary part of the attribute structure) cannot be the generic ordering key.

And indeed, a quick glance at the specification shows:

11.6 Set-of components

The encodings of the component values of a set-of value shall appear in ascending order, the encodings being compared as octet strings with the shorter components being padded at their trailing end with 0-octets.

NOTE – The padding octets are for comparison purposes only and do not appear in the encodings.

(ISO/IEC 8825-1 / ITU-T Rec. X.690, section 11 "Restrictions on BER employed by both CER and DER")

Thus, in case of the signed attributes essentially you first DER encode the attribute elements and then sort the resulting byte arrays as described above.

As an aside, this issue of your signature merely causes problems in probably half the validators around. Some validators do not check or DER re-encode the signed attributes, so they get the same hash as you get. Others either check the encoding up front (and, therefore, throw an error because of the issue) or simply re-encode the attributes in DER (and, therefore, get a different hash than you get).

Problematic Extended Key Usage of Signer Certificate

Your signer certificate has an extended key usage value 1.3.6.1.4.1.311.80.1 (Microsoft's OID for Document Encryption) and only that. Adobe validation used to only support certificates with either no extended key usage or one or more of the following:

  • emailProtection
  • codeSigning
  • anyExtendedKeyUsage
  • 1.2.840.113583.1.1.5 (Adobe Authentic Documents Trust)

See Enterprise Toolkit » Digital Signatures Guide for IT » A: Changes Across Releases.

Incorrect Incremental Updates

You sign in an incremental update to the original PDF. This in general is a good idea as it allows to extract the unsigned original document.

But one needs to add the incremental update correctly, and in case of result2_invalid_byte_range_no_image.pdf and result2_invalid_byte_range_with_image.pdf it is done incorrectly: The original revision there is created using cross reference tables but your incremental updates use pure cross reference streams. This is incorrect, you have to continue with the same kind of cross references.

When opening documents with a mix of cross reference tables and pure cross reference streams, Adobe Acrobat internally repairs this which in particular relocates signatures and so makes byte ranges incorrect.

Uncommon Signature Field Structure

You use an uncommon signature field structure in your example PDFs, you separate the widget from the field and only update the field, not the widget, in signing.

While this strictly speaking is ok, I would implement the common structures while making the code work at all, and deviate only thereafter.

PS: In a comment you asked whether I could elaborate on this.

Your signing implementation in a first step adds an incremental update with an empty signature field and a widget as indirectly referenced kid, e.g.:

16 0 obj
<<
  /Type /Annot
  /F 4
  /Subtype /Widget
  /BS << /Type /Border  /S /S  /W 0 >>
  /Parent 17 0 R
  /P 2 0 R
  /Rect [141.75 664.89 276.75 702.39]
  /AP << /N 18 0 R >>
  /MK << /BC [.1882353 .1882353 .1882353]  /BG [1.00 1.00 1.00]  /R 0 >>
  /DA (/TiRo 0 Tf 0 0 0 rg\r
)
>>
endobj
17 0 obj
<<
  /Kids [16 0 R]
  /FT /Sig
  /T (eyJ1c2VySWQiOiIyNzIifQ==)
>>

In another incremental update you then sign the field with a direct signature value but don't change the widget, e.g.:

17 0 obj
<<
  /Kids [16 0 R]
  /FT /Sig
  /T (eyJ1c2VySWQiOiIyNzIifQ==)
  /V << /Type/Sig  ... >>
>> 

This is uncommon in some ways:

  • Usually for signatures the option to merge field and widget is used.
  • Usually for signatures (except for usage right signatures) the signature dictionary is not a direct but an indirect value of the key V.
  • Usually the appearance of a signature field is updated together with the signature dictionary if there is an appearance at all.

Also, unless other form fill-ins shall happen between adding an empty signature field and signing it, fields usually are added and filled in the same document update.

Thus, more common would be a single incremental update (or even full re-save) containing something like this:

92 0 obj
<<
  /AP << /N 94 0 R >>
  /DA (/MyriadPro-Regular 0 Tf 0 Tz 0 g)
  /F 132
  /FT /Sig
  /MK <<>>
  /P 1 0 R
  /Rect [117.575 499.561 515.968 520.938]
  /Subtype /Widget
  /T (Signature3)
  /Type /Annot
  /V 93 0 R
>>
endobj
93 0 obj
<<
  /ByteRange [ 0 3227714 5751810 2789]
  ...
>>

As said above, though, your structure strictly speaking is ok, too. But the "Bad parameter" only occurs when validating from the widget in the document or from the signature panel, but it does not occur when validating your signature using the "Validate Signature" button of the "Signature Properties" dialog. Because of that I think it's possible that Adobe is iritated by an uncommon structure.

mkl
  • 90,588
  • 15
  • 125
  • 265
  • 1
    Thanks a lot for the reply. I have been working on fixing all the points you listed above. But I have some questions: About "Incorrect Signed Hash Value", By this you mean `signerInfos -> signature` I assume? This is a signed `OCTET STRING` of the `signerInfos -> signedAttrs` [I currently have this](https://pastebin.com/H6Zs4gWr) as a DER encoded ASN1. And this looks correct (maybe first `SET` is incorrect?). The DER value is then signed using the key. I checked using `openssl` and using code, the signatures match exactly. "Extended Key Usage" and "Incorrect Incremental Updates" I fixed. :D – Ralph Bisschops Jun 11 '22 at 18:42
  • 1
    Yes, that octet string should contain the signature value of the signed attributes but as far as i remember it contains the signature value of something else. I can try and take another look next week. Maybe i can deduce what it signs. – mkl Jun 11 '22 at 19:36
  • 1
    Ok, the problem is that your signed attributes are not DER encoded (the order of elements in the SET is wrong). Thus, you should fix this order. – mkl Jun 13 '22 at 08:47
  • I just checked the order of the `SET` and I can not find anything that is wrong with it. Here is my reasoning, let me know what part is incorrect. The items should be order according to the 'key' which in my case is an Oid. So `1.2.840.113549.1.9.3` for `contentType`, `messageDigest (1.2.840.113549.1.9.4)` and `signingTime (1.2.840.113549.1.9.5)` in that order. As seen here: https://pastebin.com/W1KFudZ1 I also removed `signingTime` and tested with the 2 other items in all the orders. But that did not change the `Bad parameter` error, nor anything else. I also edited the Q to add some files – Ralph Bisschops Jun 13 '22 at 12:46
  • 2
    *"The items should be order according to the 'key'"* - No, that's entirely wrong: There is no ASN.1 MAP type, so there is no concept of a "key" where the DER ordering is specified. Please read the specs, e.g. ITU-T Rec. X.690 section 11.6 "Set-of components": ***The encodings of the component values of a set-of value shall appear in ascending order, the encodings being compared as octet strings with the shorter components being padded at their trailing end with 0-octets.*** Thus, you first encode the elements of the SET OF, then you order these byte arrays. – mkl Jun 13 '22 at 13:39
  • Thanks, I think I solved the ordering problem. The library I used already did some kind of ordering (as I described above, and it was documented, so assumed it was correct). But I now do the encoding and compared on that, like you described. The order is now slightly different. Now the last part, I was trying to solve the "Uncommon Signature Field Structure" but it is not exactly clear to me what you mean. Could you maybe elaborate on you answer? (and sorry for all the additional questions, but I really appreciate your help!) – Ralph Bisschops Jun 14 '22 at 22:07
  • 2
    *" I was trying to solve the "Uncommon Signature Field Structure" but it is not exactly clear to me what you mean. Could you maybe elaborate on you answer?"* - see the edit to my answer. – mkl Jun 15 '22 at 18:20