Skip to content

Instantly share code, notes, and snippets.

@elico
Created October 2, 2017 08:41
Show Gist options
  • Save elico/d203aaf4667d592cb45b669d878ffe99 to your computer and use it in GitHub Desktop.
Save elico/d203aaf4667d592cb45b669d878ffe99 to your computer and use it in GitHub Desktop.
Siginging pdf with origami and OpenSSL on ruby 2.x+
#!/usr/bin/env ruby
require 'openssl'
require 'time'
begin
require 'origami'
rescue LoadError
abort 'origami not installed: gem install origami'
end
include Origami
input_files = ARGV
abort 'Missing file names to run' if ARGV.nil?
abort 'Usage: sign-pdf input.pdf [...]' if input_files.empty?
key = OpenSSL::PKey::RSA.new(2048)
cipher = OpenSSL::Cipher::Cipher.new 'AES-128-CBC'
pass_phrase = 'Origami rocks'
key_secure = key.export cipher, pass_phrase
# Create the certificate
name = OpenSSL::X509::Name.parse 'emailAddress=eliezer@ngtech.co.il/C=IL/ST=Shomron/L=Karney Shomron/CN=Mr Eliezer Croitoru (NgTech LTD)/O=ngtech.co.il'
issuername = OpenSSL::X509::Name.parse 'emailAddress=eliezer@ngtech.co.il/C=IL/ST=Shomron/L=Karney Shomron/CN=Mr Eliezer Croitoru (NgTech LTD)/O=MyCA'
cert = OpenSSL::X509::Certificate.new
cert.version = 2
cert.serial = 0
cert.not_before = Time.now
cert.not_after = Time.now + (2 * 365 * 24 * 60 * 60) # 2 years
cert.public_key = key.public_key
cert.subject = name
cert.issuer = issuername
ef = OpenSSL::X509::ExtensionFactory.new
ef.subject_certificate = cert
cert.add_extension(ef.create_extension('basicConstraints', 'CA:TRUE', true))
cert.add_extension(ef.create_extension('keyUsage', 'nonRepudiation,digitalSignature', true))
cert.add_extension(ef.create_extension('extendedKeyUsage', 'emailProtection,clientAuth', true))
cert.add_extension(ef.create_extension('subjectKeyIdentifier', 'hash', false))
cert.sign(key, OpenSSL::Digest::SHA256.new)
puts cert.to_text
input_files.each do |file|
output_filename = file.dup.insert(file.rindex('.'), '_signed')
pdf = PDF.read(file)
page = pdf.get_page(1)
width = 200.0
height = 50.0
y = page.MediaBox[2].to_f - width - height
x = height
size = 8
signedby = 'Eliezer Croitoru'
location = 'Karney Shomron, Israel'
contact = 'eliezer@ngtech.co.il' # pdf.signature[pdf.signature.keys[6]]
reason = 'IRS' # pdf.signature[pdf.signature.keys[7]]
date = Time.now
# caption = "Digitally Signed By: #{signedby}\nContact: #{contact}\nLocation: #{location}\nReason: #{reason}\nDate: #{date} "
caption = "Digitally Signed By: #{signedby}\nContact: #{contact}\nLocation: #{location}\nReason: #{reason}\nDate: #{date.iso8601}"
text_annotation = Annotation::AppearanceStream.new
text_annotation.Type = Origami::Name.new('XObject')
text_annotation.Resources = Resources.new
text_annotation.Resources.ProcSet = [Origami::Name.new('Text')]
text_annotation.set_indirect(true)
text_annotation.Matrix = [1, 0, 0, 1, 0, 0]
text_annotation.BBox = [0, 0, width, height]
text_annotation.write(caption, x: size, y: (height / 2) - (size / 2), size: size)
# Add signature annotation (so it becomes visibles in PDF document)
# signature_annotation = Annotation::Widget::Signature.new
signature_annotation = Annotation::Widget::Signature.new.set_indirect(true)
signature_annotation.Rect = Rectangle[llx: x, lly: y + height, urx: x + width, ury: y]
# signature_annotation.set_normal_appearance(text_annotation)
signature_annotation.F = Annotation::Flags::PRINT
page.add_annotation(signature_annotation)
def cert_to_sha1(cert)
OpenSSL::Digest::SHA1.new(cert.to_der).to_s.upcase
end
puts "Certificate SHA1: #{cert_to_sha1(cert)}"
puts "Certificate SHA256: #{OpenSSL::Digest::SHA256.new(cert.to_der).to_s.upcase}"
puts "Certificate SHA512: #{OpenSSL::Digest::SHA512.new(cert.to_der).to_s.upcase}"
# Sign the PDF with the specified keys
pdf.sign(cert, key,
method: 'adbe.pkcs7.detached',
annotation: signature_annotation,
location: location,
contact: contact,
issuer: 'MyCA',
reason: reason)
# Save the resulting file
pdf.save(output_filename)
puts "Signed with Certificate SHA1: #{cert_to_sha1(OpenSSL::PKCS7.new(pdf.signature.Contents).certificates[0])}"
puts output_filename
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment