Class: HexaPDF::Document::Signatures::DefaultHandler
- Inherits:
-
Object
- Object
- HexaPDF::Document::Signatures::DefaultHandler
- Defined in:
- lib/hexapdf/document/signatures.rb
Overview
This is the default signing handler which provides the ability to sign a document with the adbe.pkcs7.detached or ETSI.CAdES.detached algorithms. It is registered under the :default name.
Usage
The signing handler is used by default by all methods that need a signing handler. Therefore it is usually only necessary to provide the actual attribute values.
This handler provides two ways to create the PKCS#7 signed-data structure required by Signatures#add:
-
By providing the signing certificate together with the signing key and the certificate chain. This way HexaPDF itself does the signing. It is the preferred way if all the needed information is available.
Assign the respective data to the #certificate, #key and #certificate_chain attributes.
-
By using an external signing mechanism. Here the actual signing happens “outside” of HexaPDF, for example, in custom code or even asynchronously. This is needed in case the signing certificate plus key are not directly available but only an interface to them (e.g. when dealing with a HSM).
Assign a callable object to #external_signing. If the signing process needs to be asynchronous, make sure to set the #signature_size appropriately, return an empty string during signing and later use Signatures.embed_signature to embed the actual signature.
Additional functionality:
-
Optionally setting the reason, location and contact information.
-
Making the signature a certification signature by applying the DocMDP transform method.
Example:
# Signing using certificate + key
document.sign("output.pdf", certificate: my_cert, key: my_key,
certificate_chain: my_chain)
# Signing using an external mechanism:
signing_proc = lambda do |io, byte_range|
io.pos = byte_range[0]
data = io.read(byte_range[1])
io.pos = byte_range[2]
data << io.read(byte_range[3])
signing_service.pkcs7_sign(data)
end
document.sign("output.pdf", signature_size: 10_000, external_signing: signing_proc)
Implementing a Signing Handler
This class also serves as an example on how to create a custom handler: The public methods #signature_size, #finalize_objects and #sign are used by the digital signature algorithm. See their descriptions for details.
Once a custom signing handler has been created, it can be registered under the 'signature.signing_handler' configuration option for easy use. It has to take keyword arguments in its initialize method to be compatible with the Signatures#handler method.
Instance Attribute Summary collapse
-
#certificate ⇒ Object
The certificate with which to sign the PDF.
-
#certificate_chain ⇒ Object
The certificate chain that should be embedded in the PDF; normally contains all certificates up to the root certificate.
-
#contact_info ⇒ Object
The contact information.
-
#doc_mdp_permissions ⇒ Object
The DocMDP permissions that should be set on the document.
-
#external_signing ⇒ Object
A callable object fulfilling the same role as the #sign method that is used instead of the default mechanism for signing.
-
#key ⇒ Object
The private key for the #certificate.
-
#location ⇒ Object
The signing location.
-
#reason ⇒ Object
The reason for signing.
-
#signature_size ⇒ Object
Returns the size of the serialized signature that should be reserved.
-
#signature_type ⇒ Object
The type of signature to be written (i.e. the value of the /SubFilter key).
Instance Method Summary collapse
-
#finalize_objects(_signature_field, signature) ⇒ Object
Finalizes the signature field as well as the signature dictionary before writing.
-
#initialize(**arguments) ⇒ DefaultHandler
constructor
Creates a new DefaultHandler with the given attributes.
-
#sign(io, byte_range) ⇒ Object
Returns the DER serialized OpenSSL::PKCS7 structure containing the signature for the given IO byte ranges.
Constructor Details
#initialize(**arguments) ⇒ DefaultHandler
Creates a new DefaultHandler with the given attributes.
154 155 156 157 |
# File 'lib/hexapdf/document/signatures.rb', line 154 def initialize(**arguments) @signature_size = nil arguments.each {|name, value| send("#{name}=", value) } end |
Instance Attribute Details
#certificate ⇒ Object
The certificate with which to sign the PDF.
108 109 110 |
# File 'lib/hexapdf/document/signatures.rb', line 108 def certificate @certificate end |
#certificate_chain ⇒ Object
The certificate chain that should be embedded in the PDF; normally contains all certificates up to the root certificate.
115 116 117 |
# File 'lib/hexapdf/document/signatures.rb', line 115 def certificate_chain @certificate_chain end |
#contact_info ⇒ Object
The contact information. If used, will be set on the signature object.
131 132 133 |
# File 'lib/hexapdf/document/signatures.rb', line 131 def contact_info @contact_info end |
#doc_mdp_permissions ⇒ Object
The DocMDP permissions that should be set on the document.
See #doc_mdp_permissions=
151 152 153 |
# File 'lib/hexapdf/document/signatures.rb', line 151 def @doc_mdp_permissions end |
#external_signing ⇒ Object
A callable object fulfilling the same role as the #sign method that is used instead of the default mechanism for signing.
If this attribute is set, the attributes #certificate, #key and #certificate_chain are not used.
122 123 124 |
# File 'lib/hexapdf/document/signatures.rb', line 122 def external_signing @external_signing end |
#key ⇒ Object
The private key for the #certificate.
111 112 113 |
# File 'lib/hexapdf/document/signatures.rb', line 111 def key @key end |
#location ⇒ Object
The signing location. If used, will be set on the signature object.
128 129 130 |
# File 'lib/hexapdf/document/signatures.rb', line 128 def location @location end |
#reason ⇒ Object
The reason for signing. If used, will be set on the signature object.
125 126 127 |
# File 'lib/hexapdf/document/signatures.rb', line 125 def reason @reason end |
#signature_size ⇒ Object
Returns the size of the serialized signature that should be reserved.
If a custom size is set using #signature_size=, it used. Otherwise the size is determined by using #sign to sign an empty string.
190 191 192 |
# File 'lib/hexapdf/document/signatures.rb', line 190 def signature_size @signature_size || sign(StringIO.new, [0, 0, 0, 0]).size end |
#signature_type ⇒ Object
The type of signature to be written (i.e. the value of the /SubFilter key).
The value can either be :adobe (the default; uses a detached PKCS7 signature) or :etsi (uses an ETSI CAdES compatible signature).
146 147 148 |
# File 'lib/hexapdf/document/signatures.rb', line 146 def signature_type @signature_type end |
Instance Method Details
#finalize_objects(_signature_field, signature) ⇒ Object
Finalizes the signature field as well as the signature dictionary before writing.
195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 |
# File 'lib/hexapdf/document/signatures.rb', line 195 def finalize_objects(_signature_field, signature) signature[:SubFilter] = :'ETSI.CAdES.detached' if signature_type == :etsi signature[:Reason] = reason if reason signature[:Location] = location if location signature[:ContactInfo] = contact_info if contact_info if doc = signature.document if doc.signatures.count > 1 raise HexaPDF::Error, "Can set DocMDP access permissions only on first signature" end params = doc.add({Type: :TransformParams, V: :'1.2', P: }) sigref = doc.add({Type: :SigRef, TransformMethod: :DocMDP, DigestMethod: :SHA1, TransformParams: params}) signature[:Reference] = [sigref] (doc.catalog[:Perms] ||= {})[:DocMDP] = signature end end |
#sign(io, byte_range) ⇒ Object
Returns the DER serialized OpenSSL::PKCS7 structure containing the signature for the given IO byte ranges.
The byte_range argument is an array containing four numbers [offset1, length1, offset2, length2]. The offset numbers are byte positions in the io argument and the to-be-signed data can be determined by reading length bytes at the offsets.
220 221 222 223 224 225 226 227 228 229 230 231 |
# File 'lib/hexapdf/document/signatures.rb', line 220 def sign(io, byte_range) if external_signing external_signing.call(io, byte_range) else io.pos = byte_range[0] data = io.read(byte_range[1]) io.pos = byte_range[2] data << io.read(byte_range[3]) OpenSSL::PKCS7.sign(@certificate, @key, data, @certificate_chain, OpenSSL::PKCS7::DETACHED | OpenSSL::PKCS7::BINARY).to_der end end |