2
votes

Summarized and clarified:

Using origami, extracting a certificate from a signed pdf (signed within e.g. Adobe Reader) I cannot verify the signature:

origami = Origami::PDF.read(File.open('/path/to/file.pdf', 'r'))
pdf_signature = origami.signature[:Contents]
cert = OpenSSL::PKCS7.new(pdf_signature).certificates.first

origami.verify(trusted_certs: [cert]) #=> false

As far as I can tell, this should always be true. So maybe Adobe uses a different byte range that it takes a SHA of when signing the PDF? How do I get that verify to work?

If it's any help, after tiptoing through the changes on origami master, I was able to get the exact OpenSSL error from the storecontext: V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY - I presume it means from the X509::Store it sets up.


Full background

I'm trying to verify the digital signature of a PDF. Here's what I've got:

A PDF that I've signed using Adobe Acrobat (tried both Pro 10 and Reader DC)

The key was generated in Acrobat Pro, I have access to the .p12, or exporting as FDF, PKCS#7 or just "Certificate File". Have also tried loading this "Certificate File" via Apple's "Keychain Access" and exporting that as a .pem This gives the same result as OpenSSL::PKCS7.new(File.read('/path/to/exported.p7c')).certificates.first.to_pem which gives the same result as:

openssl pkcs7 -print_certs -inform der -in pkcs7file.p7c -out certificate.cer

So, I'm pretty sure I've extracted the certificate correctly.

Also, I can verify that exact same certificate is embedded in the PDF -

pdf_signature = origami.signature[:Contents]
OpenSSL::PKCS7.new(pdf_signature).certificates.first.to_pem #=> Same as above

With the Origami gem, I've tried loading the certificate and attempting verification:

cert = OpenSSL::X509::Certificate.new(File.read('/path/to/pem.cer'))
Origami::PDF.read(File.open('/path/to/file.pdf', 'r')).verify(trusted_certs: [cert])

Origami's output confirms the document has been signed, but the verify(..) method returns false.

Note that working through the code from this excellent answer works fine, but it only seems to work if you generate the X.509 keypair using openssl (e.g. the ruby-land bindings as per that code). Unfortunately I'm required to use the pre-existing Adobe-blessed signatures from the user's machines.

That said, aside from this I have very few restraints; I can ask the users to export their certificate in any other way that is useful to us (I can even run some simple code on their machines if necessary), though I mustn't transmit the private key in the procedure. I don't have to use Origami for the verification, but it does have to be a command accessible from ruby on an ubuntu server. The users are all running on Macs with reasonably up-to-date software.

2

2 Answers

2
votes

I've gotten a little further from my original issue, but not by much:

Certificates require the correct extensions

In the original code from Harry Fairbanks' very useful answer, the extensions are paramount:


extension_factory = OpenSSL::X509::ExtensionFactory.new
extension_factory.issuer_certificate = cert
extension_factory.subject_certificate = cert

cert.add_extension extension_factory.create_extension('basicConstraints', 'CA:TRUE', true)
cert.add_extension extension_factory.create_extension('keyUsage', 'digitalSignature,keyCertSign')
cert.add_extension extension_factory.create_extension('subjectKeyIdentifier', 'hash')

So... following the rest of that answer, if you save the certificate to a pemfile, the extensions do not also get saved.

I was able to create a PDF, sign it with Origami using the key I exported from Acrobat reader, and then do the following:

cert = OpenSSL::PKCS7.new(pdf_signature).certificates.first

origami.verify(trusted_certs: [cert]) #=> false

## ... then run the extension factory snippet above

origami.verify(trusted_certs: [cert]) #=> true

Success! In fact, even Adobe Acrobat Reader was happy - which I couldn't get it to do with the self-signed certificate that Origami generated.

... however, when I sign the document using Adobe Acrobat Reader, with the same key, perform the same magical incantation on the cert, I still get false from the verify call.


Note: I have been told that this actually works for some people. Not sure why it failed for me - when I have a chance to play, will give it another go. Will mark this as answered for now!

0
votes

It's possible the ciphers are different. It could be that the Adobe cipher is not the same one that the openssl is using and would then fail the verification check. Take a look at this. Details on ciphers

This might be useful too openssl commands