Class: HexaPDF::DigitalSignature::Signing::TimestampHandler

Inherits:
Object
  • Object
show all
Defined in:
lib/hexapdf/digital_signature/signing/timestamp_handler.rb

Overview

This is a signing handler for adding a timestamp signature (a PDF2.0 feature) to a PDF document. It is registered under the :timestamp name.

The timestamp is provided by a timestamp authority and establishes the document contents at the time indicated in the timestamp. Timestamping a PDF document is usually done in context of long term validation but can also be done standalone.

Usage

It is necessary to provide at least the URL of the timestamp authority server (TSA) via #tsa_url, everything else is optional and uses default values. The TSA server must not use authentication to be usable.

Example:

document.sign("output.pdf", handler: :timestamp, tsa_url: 'https://freetsa.org/tsr')

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(**arguments) ⇒ TimestampHandler

Creates a new TimestampHandler with the given attributes.



94
95
96
97
# File 'lib/hexapdf/digital_signature/signing/timestamp_handler.rb', line 94

def initialize(**arguments)
  @signature_size = nil
  arguments.each {|name, value| send("#{name}=", value) }
end

Instance Attribute Details

#contact_infoObject

The contact information. If used, will be set on the signature dictionary.



91
92
93
# File 'lib/hexapdf/digital_signature/signing/timestamp_handler.rb', line 91

def contact_info
  @contact_info
end

#locationObject

The timestamping location. If used, will be set on the signature dictionary.



88
89
90
# File 'lib/hexapdf/digital_signature/signing/timestamp_handler.rb', line 88

def location
  @location
end

#reasonObject

The reason for timestamping. If used, will be set on the signature dictionary.



85
86
87
# File 'lib/hexapdf/digital_signature/signing/timestamp_handler.rb', line 85

def reason
  @reason
end

#signature_sizeObject

Returns the size of the serialized signature that should be reserved.



100
101
102
# File 'lib/hexapdf/digital_signature/signing/timestamp_handler.rb', line 100

def signature_size
  @signature_size || (sign(StringIO.new, [0, 0, 0, 0]).size * 1.5).to_i
end

#tsa_hash_algorithmObject

The hash algorithm to use for timestamping. Defaults to SHA512.



70
71
72
# File 'lib/hexapdf/digital_signature/signing/timestamp_handler.rb', line 70

def tsa_hash_algorithm
  @tsa_hash_algorithm
end

#tsa_policy_idObject

The policy OID to use for timestamping. Defaults to nil.



73
74
75
# File 'lib/hexapdf/digital_signature/signing/timestamp_handler.rb', line 73

def tsa_policy_id
  @tsa_policy_id
end

#tsa_urlObject

The URL of the timestamp authority server.

This value is required.



67
68
69
# File 'lib/hexapdf/digital_signature/signing/timestamp_handler.rb', line 67

def tsa_url
  @tsa_url
end

Instance Method Details

#finalize_objects(_signature_field, signature) ⇒ Object

Finalizes the signature field as well as the signature dictionary before writing.



105
106
107
108
109
110
111
112
113
# File 'lib/hexapdf/digital_signature/signing/timestamp_handler.rb', line 105

def finalize_objects(_signature_field, signature)
  signature.document.version = '2.0'
  signature[:Type] = :DocTimeStamp
  signature[:Filter] = :'Adobe.PPKLite'
  signature[:SubFilter] = :'ETSI.RFC3161'
  signature[:Reason] = reason if reason
  signature[:Location] = location if location
  signature[:ContactInfo] = contact_info if contact_info
end

#sign(io, byte_range) ⇒ Object

Returns the DER serialized OpenSSL::PKCS7 structure containing the timestamp token for the given IO byte ranges.



117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
# File 'lib/hexapdf/digital_signature/signing/timestamp_handler.rb', line 117

def sign(io, byte_range)
  hash_algorithm = tsa_hash_algorithm || 'SHA512'
  digest = OpenSSL::Digest.new(hash_algorithm)
  io.pos = byte_range[0]
  digest << io.read(byte_range[1])
  io.pos = byte_range[2]
  digest << io.read(byte_range[3])

  req = OpenSSL::Timestamp::Request.new
  req.algorithm = hash_algorithm
  req.message_imprint = digest.digest
  req.policy_id = tsa_policy_id if tsa_policy_id

  http_response = Net::HTTP.post(URI(tsa_url), req.to_der,
                                 'content-type' => 'application/timestamp-query')
  if http_response.kind_of?(Net::HTTPOK)
    response = OpenSSL::Timestamp::Response.new(http_response.body)
    if response.status == 0
      response.token.to_der
    else
      raise HexaPDF::Error, "Timestamp token could not be created: #{response.failure_info}"
    end
  else
    raise HexaPDF::Error, "Invalid TSA server response: #{http_response.body}"
  end
end