Insert digital signature into existing pdf file

2019-01-11 05:33发布

问题:

I need to insert a digital signature into already existing pdf files, using a rails application server. (Basically, clients upload pdf files and the server signs them with a local certificate)

I've been using JSignpdf to insert digital signatures into pdf files, and started probing for gems for ruby...

I've found another portable file to do this job on rubypdf site http://soft.rubypdf.com/software/pdf-digital-signe, but cannot find any gem or even example code to do this in ruby.

I've looked also at Digital signature verification with OpenSSL, but couldn't understand how to actually sign an already existing document, with a local certificate file.

I also took a peak at http://code.google.com/p/origami-pdf/ , but this seems a bit harsh for a supposingly "simple" (at least in concept) task.

Any ideas/suggestions?

Thank you

回答1:

After some research, recurring to the OpenSSL documentation and exploring the Origami solution, i built the code below, and managed to insert a locally generated signature/certificate into a pdf document. Now I just need to figure out how to use this with an external generated certificate (check version 2 below, where i solved it). I've opened a new question where you can find some details on a difficulty i had with OpenSSL and DER encoded certificates.

To develop version 2, i also spent some time wondering how to add an annotation - so the signature becomes visible in Adobe reader - without adding a new page to the document. From origami documentation, i found the get_page method, which solved my last problem on this. I'm using Adobe Reader X, for the record.

Hope you find this useful as I will ;-).

VERSION 1 - Generate certificate and key file, and insert them directly into the document

require 'openssl'

begin
  require 'origami'
rescue LoadError
  ORIGAMIDIR = "C:\RailsInstaller\Ruby1.9.3\lib\ruby\gems\1.9.1\gems\origami-1.2.4\lib"
  $: << ORIGAMIDIR
  require 'origami'
end
include Origami

# Code below is based on documentation available on
# http://www.ruby-doc.org/stdlib-1.9.3/libdoc/openssl/rdoc/OpenSSL.html
key = OpenSSL::PKey::RSA.new 2048

open 'private_key.pem', 'w' do |io| io.write key.to_pem end
open 'public_key.pem', 'w' do |io| io.write key.public_key.to_pem end

cipher = OpenSSL::Cipher::Cipher.new 'AES-128-CBC'
pass_phrase = 'Origami rocks'

key_secure = key.export cipher, pass_phrase

open 'private_key.pem', 'w' do |io|
  io.write key_secure
end

#Create the certificate

name = OpenSSL::X509::Name.parse 'CN=nobody/DC=example'

cert = OpenSSL::X509::Certificate.new
cert.version = 2
cert.serial = 0
cert.not_before = Time.now
cert.not_after = Time.now + 3600

cert.public_key = key.public_key
cert.subject = name


OUTPUTFILE = "test.pdf"

contents = ContentStream.new.setFilter(:FlateDecode)
contents.write OUTPUTFILE,
  :x => 350, :y => 750, :rendering => Text::Rendering::STROKE, :size => 30

pdf = PDF.read('Sample.pdf')


# Open certificate files

#sigannot = Annotation::Widget::Signature.new
#sigannot.Rect = Rectangle[:llx => 89.0, :lly => 386.0, :urx => 190.0, :ury => 353.0]

#page.add_annot(sigannot)

# Sign the PDF with the specified keys
pdf.sign(cert, key, 
  :method => 'adbe.pkcs7.sha1',
  #:annotation => sigannot, 
  :location => "Portugal", 
  :contact => "myemail@email.tt", 
  :reason => "Proof of Concept"
)

# Save the resulting file
pdf.save(OUTPUTFILE)

VERSION 2 - Use existing certificates to sign a pdf document

require 'openssl'

begin
  require 'origami'
rescue LoadError
  ORIGAMIDIR = "C:\RailsInstaller\Ruby1.9.3\lib\ruby\gems\1.9.1\gems\origami-1.2.4\lib"
  $: << ORIGAMIDIR
  require 'origami'
end
include Origami

INPUTFILE = "Sample.pdf"
@inputfile = String.new(INPUTFILE)
OUTPUTFILE = @inputfile.insert(INPUTFILE.rindex("."),"_signed")
CERTFILE = "certificate.pem"
RSAKEYFILE = "private_key.pem"
passphrase = "your passphrase"

key4pem=File.read RSAKEYFILE

key = OpenSSL::PKey::RSA.new key4pem, passphrase
cert = OpenSSL::X509::Certificate.new(File.read CERTFILE)

pdf = PDF.read(INPUTFILE)
page = pdf.get_page(1)

# Add signature annotation (so it becomes visibles in pdf document)

sigannot = Annotation::Widget::Signature.new
sigannot.Rect = Rectangle[:llx => 89.0, :lly => 386.0, :urx => 190.0, :ury => 353.0]

page.add_annot(sigannot)

# Sign the PDF with the specified keys
pdf.sign(cert, key, 
  :method => 'adbe.pkcs7.sha1',
  :annotation => sigannot, 
  :location => "Portugal", 
  :contact => "myemail@email.tt", 
  :reason => "Proof of Concept"
)

# Save the resulting file
pdf.save(OUTPUTFILE)


回答2:

If you're working on a project for pay, you might want to consider jPDFSecure, a commercial Java library built for developers to digitally sign PDF documents and change security settings on PDF Documents. With jPDFSecure, your application or java applet can encrypt PDF documents, set permissions and passwords, and create and apply digital signatures. jPDFSecure is optimized for performance and is built on top of Qoppa's proprietary PDF technology so there is no need for any third party software or drivers.

jPDFSecure has a simple interface to load PDF documents from files, network drives,URLs and even input streams, which can be generated runtime or come directly from a database. After changing security settings, jPDFSecure can save the document to a file, a java.io.OutputStream or a javax.servlet.ServletOutputStream when running in a Java EE application server to output the file directly to a browser.

jPDFSecure is platform independent and can be used in any environment that supports Java, including Windows, Mac OSX and Linux.