Module: HexaPDF::DigitalSignature::Signing

Defined in:
lib/hexapdf/digital_signature/signing.rb,
lib/hexapdf/digital_signature/signing/default_handler.rb,
lib/hexapdf/digital_signature/signing/timestamp_handler.rb,
lib/hexapdf/digital_signature/signing/signed_data_creator.rb

Overview

This module contains everything related to the signing of a PDF document, i.e. signing handlers and the actual code for signing.

  • The DefaultHandler is the standard signing handler and should be sufficient for most cases.

  • The TimestampHandler is used for timestamping purposes.

  • The SignedDataCreator provides the functionality to create custom CMS signed data objects.

Defined Under Namespace

Classes: DefaultHandler, SignedDataCreator, TimestampHandler

Class Method Summary collapse

Class Method Details

.embed_signature(io, signature) ⇒ Object

Embeds the given signature into the /Contents value of the newest signature dictionary of the PDF document given by the io argument.

This functionality can be used together with the support for external signing (see DefaultHandler and DefaultHandler#external_signing) to implement asynchronous signing.

Note: This will, most probably, only work on documents prepared for external signing by HexaPDF and not by other libraries.



62
63
64
65
66
67
68
69
70
71
72
73
74
75
# File 'lib/hexapdf/digital_signature/signing.rb', line 62

def self.embed_signature(io, signature)
  doc = HexaPDF::Document.new(io: io)
  signature_dict = doc.signatures.find {|sig| doc.revisions.current.object(sig) == sig }
  signature_dict_offset, signature_dict_length = locate_signature_dict(
    doc.revisions.current.xref_section,
    doc.revisions.parser.startxref_offset,
    signature_dict.oid
  )
  io.pos = signature_dict_offset
  signature_data = io.read(signature_dict_length)
  replace_signature_contents(signature_data, signature)
  io.pos = signature_dict_offset
  io.write(signature_data)
end

.locate_signature_dict(xref_section, start_xref_position, signature_oid) ⇒ Object

Uses the information in the given cross-reference section as well as the byte offset of the cross-reference section to calculate the offset and length of the signature dictionary with the given object id.



80
81
82
83
84
85
# File 'lib/hexapdf/digital_signature/signing.rb', line 80

def self.locate_signature_dict(xref_section, start_xref_position, signature_oid)
  data = xref_section.map {|oid, _gen, entry| [entry.pos, oid] if entry.in_use? }.compact.sort <<
    [start_xref_position, nil]
  index = data.index {|_pos, oid| oid == signature_oid }
  [data[index][0], data[index + 1][0] - data[index][0]]
end

.replace_signature_contents(signature_data, contents) ⇒ Object

Replaces the value of the /Contents key in the serialized signature_data with the value of contents.



89
90
91
92
93
94
95
96
97
98
99
100
# File 'lib/hexapdf/digital_signature/signing.rb', line 89

def self.replace_signature_contents(signature_data, contents)
  signature_data.sub!(/Contents(?:\(.*?\)|<.*?>)/) do |match|
    length = match.size
    result = "Contents<#{contents.unpack1('H*')}"
    if length < result.size
      raise HexaPDF::Error, "The reserved space for the signature was too small " \
        "(#{(length - 10) / 2} vs #{(result.size - 10) / 2}) - use the handlers " \
        "#signature_size method to increase the reserved space"
    end
    "#{result.ljust(length - 1, '0')}>"
  end
end