class HexaPDF::DigitalSignature::Signing::DefaultHandler
arguments in its initialize method to be compatible with the Signatures#handler method.
‘signature.signing_handler’ configuration option for easy use. It has to take keyword
Once a custom signing handler has been created, it can be registered under the
See their descriptions for details.
#signature_size, #finalize_objects and #sign are used by the digital signature algorithm.
This class also serves as an example on how to create a custom handler: The public methods
== Implementing a Signing Handler
document.sign(“output.pdf”, signature_size: 10_000, external_signing: signing_proc)
end
OpenSSL::PKCS7::DETACHED | OpenSSL::PKCS7::BINARY).to_der
OpenSSL::PKCS7.sign(certificate, key, data, certificate_chain,
data << io.read(byte_range)
io.pos = byte_range[2]
data = io.read(byte_range)
io.pos = byte_range[0]
signing_proc = lambda do |io, byte_range|
# Signing with DSA or ECDSA certificate/keys
external_signing: signing_proc)
document.sign(“output.pdf”, certificate: my_cert, certificate_chain: my_chain,
end
signing_service.sign_raw(digest_method, hash)
signing_proc = lambda do |digest_method, hash|
# Signing using external mechanism with certificate set
document.sign(“output.pdf”, signature_size: 10_000, external_signing: signing_proc)
end
signing_service.pkcs7_sign(data).to_der
data << io.read(byte_range)
io.pos = byte_range[2]
data = io.read(byte_range)
io.pos = byte_range[0]
signing_proc = lambda do |io, byte_range|
# Signing using an external mechanism without certificate set
certificate_chain: my_chain)
document.sign(“output.pdf”, certificate: my_cert, key: my_key,
# Signing using certificate + key
== Examples
a DoCMDP permission
* Making the signature a certification signature by applying the DocMDP transform method and
* Reason, location and contact information
Besides the required data, some optional attributes can also be specified:
== Optional Data
Signatures.embed_signature to embed the actual signature.
appropriately, return an empty string during signing and later use
If the signing process needs to be asynchronous, make sure to set the #signature_size
returned.
already digested data which needs to be signed (but not digested) and the signature
callable #external_signing object is called with the used digest algorithm and the
* If #certificate is set, the CMS signed data object is created by HexaPDF. The
signed data object.
accept the same arguments as #sign and needs to return a complete, DER-serialized CMS
* If #certificate is not set, the callable object is used instead of #sign, so it needs to
Depending on whether #certificate is set the signing happens differently:
an interface to it (e.g. when dealing with a HSM).
asynchronously. This is needed in case the signing key is not directly available but only
Here the actual signing happens “outside” of HexaPDF, for example, in custom code or even
* By using an *external signing mechanism*, a callable object assigned to #external_signing.
Assign the respective data to the #certificate, #key and #certificate_chain attributes.
needed information is available.
chain, HexaPDF itself does the signing internally. It is the preferred way if all the
* By providing the signing certificate together with the signing key and the certificate
Signatures#add:
This handler provides two ways to create the CMS signed-data structure required by
== Signing Modes - Internal, External, External/Asynchronous
B-T compatibility.
B-B and B-T. The difference between those two is that a timestamp handler was defined for
When creating PAdES signatures the following two PAdES baseline signatures are supported:
changed by setting #signature_type to :pades.
signatures specified in PDF 2.0. By default, CMS signatures are created but this can be
The handler supports the older standard of CMS signatures as well as the newer PAdES
== CMS and PAdES Signatures
how to handle them using external signing.
Note: Currently only RSA is supported, DSA and ECDSA are not. See the examples below for
it is usually only necessary to provide the actual attribute values.
The signing handler is used by default by all methods that need a signing handler. Therefore
== Usage
name.
adbe.pkcs7.detached or ETSI.CAdES.detached algorithms. It is registered under the :default
This is the default signing handler which provides the ability to sign a document with the
def doc_mdp_permissions=(permissions)
Only filling in forms, signing and annotation creation/deletion/modification are
+:form_filling_and_annotations+ or 3::
Only filling in forms and signing are allowed.
+:form_filling+ or 2::
No changes whatsoever are allowed.
+:no_changes+ or 1::
Don't set any DocMDP permissions (default).
+nil+::
Valid values for +permissions+ are:
Sets the DocMDP permissions that should be applied to the document.
def doc_mdp_permissions=(permissions) case permissions when :no_changes, 1 then @doc_mdp_permissions = 1 when :form_filling, 2 then @doc_mdp_permissions = 2 when :form_filling_and_annotations, 3 then @doc_mdp_permissions = 3 when nil then @doc_mdp_permissions = nil else raise ArgumentError, "Invalid permissions value '#{permissions.inspect}'" end end
def finalize_objects(_signature_field, signature)
def finalize_objects(_signature_field, signature) signature[:Filter] = :'Adobe.PPKLite' signature[:SubFilter] = (signature_type == :pades ? :'ETSI.CAdES.detached' : :'adbe.pkcs7.detached') signature[:M] = Time.now signature[:Reason] = reason if reason signature[:Location] = location if location signature[:ContactInfo] = contact_info if contact_info signature[:Prop_Build] = {App: {Name: :HexaPDF, REx: HexaPDF::VERSION}} if doc_mdp_permissions 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: doc_mdp_permissions}) sigref = doc.add({Type: :SigRef, TransformMethod: :DocMDP, DigestMethod: :SHA256, TransformParams: params}) signature[:Reference] = [sigref] (doc.catalog[:Perms] ||= {})[:DocMDP] = signature end end
def initialize(**arguments)
def initialize(**arguments) @signature_size = nil @signature_type = :cms arguments.each {|name, value| send("#{name}=", value) } end
def sign(io, byte_range)
length2]. The offset numbers are byte positions in the +io+ argument and the to-be-signed
The +byte_range+ argument is an array containing four numbers [offset1, length1, offset2,
IO byte ranges.
Returns the DER serialized CMS signed data object containing the signature for the given
def sign(io, byte_range) if certificate io.pos = byte_range[0] data = io.read(byte_range[1]) io.pos = byte_range[2] data << io.read(byte_range[3]) SignedDataCreator.create(data, type: signature_type, certificate: certificate, key: key, digest_algorithm: digest_algorithm, timestamp_handler: timestamp_handler, certificates: certificate_chain, &external_signing).to_der else external_signing.call(io, byte_range) end end
def signature_size
If a custom size is set using #signature_size=, it used. Otherwise the size is determined
Returns the size of the serialized signature that should be reserved.
def signature_size @signature_size || sign(StringIO.new, [0, 0, 0, 0]).size end