Class: HexaPDF::DigitalSignature::CMSHandler
- Defined in:
- lib/hexapdf/digital_signature/cms_handler.rb
Overview
The signature handler for PKCS#7 a.k.a. CMS signatures. Those include, for example, the adbe.pkcs7.detached and ETSI.CAdES.detached sub-filters.
See: PDF2.0 s12.8.3.3
Instance Attribute Summary
Attributes inherited from Handler
Instance Method Summary collapse
-
#certificate_chain ⇒ Object
Returns the certificate chain.
-
#initialize(signature_dict) ⇒ CMSHandler
constructor
Creates a new signature handler for the given signature dictionary.
-
#signer_certificate ⇒ Object
Returns the signer certificate (an instance of OpenSSL::X509::Certificate).
-
#signer_info ⇒ Object
Returns the signer information object (an instance of OpenSSL::PKCS7::SignerInfo).
-
#signer_name ⇒ Object
Returns the common name of the signer.
-
#signing_time ⇒ Object
Returns the time of signing.
-
#verify(store, allow_self_signed: false) ⇒ Object
Verifies the signature using the provided OpenSSL::X509::Store object.
Constructor Details
#initialize(signature_dict) ⇒ CMSHandler
Creates a new signature handler for the given signature dictionary.
50 51 52 53 |
# File 'lib/hexapdf/digital_signature/cms_handler.rb', line 50 def initialize(signature_dict) super @pkcs7 = OpenSSL::PKCS7.new(signature_dict.contents) end |
Instance Method Details
#certificate_chain ⇒ Object
Returns the certificate chain.
66 67 68 |
# File 'lib/hexapdf/digital_signature/cms_handler.rb', line 66 def certificate_chain @pkcs7.certificates end |
#signer_certificate ⇒ Object
Returns the signer certificate (an instance of OpenSSL::X509::Certificate).
71 72 73 74 |
# File 'lib/hexapdf/digital_signature/cms_handler.rb', line 71 def signer_certificate info = signer_info certificate_chain.find {|cert| cert.issuer == info.issuer && cert.serial == info.serial } end |
#signer_info ⇒ Object
Returns the signer information object (an instance of OpenSSL::PKCS7::SignerInfo).
77 78 79 |
# File 'lib/hexapdf/digital_signature/cms_handler.rb', line 77 def signer_info @pkcs7.signers.first end |
#signer_name ⇒ Object
Returns the common name of the signer.
56 57 58 |
# File 'lib/hexapdf/digital_signature/cms_handler.rb', line 56 def signer_name signer_certificate.subject.to_a.assoc("CN")&.[](1) || super end |
#signing_time ⇒ Object
Returns the time of signing.
61 62 63 |
# File 'lib/hexapdf/digital_signature/cms_handler.rb', line 61 def signing_time signer_info.signed_time rescue super end |
#verify(store, allow_self_signed: false) ⇒ Object
Verifies the signature using the provided OpenSSL::X509::Store object.
82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 |
# File 'lib/hexapdf/digital_signature/cms_handler.rb', line 82 def verify(store, allow_self_signed: false) result = super signer_info = self.signer_info signer_certificate = self.signer_certificate certificate_chain = self.certificate_chain if certificate_chain.empty? result.log(:error, "No certificates found in signature") return result end if @pkcs7.signers.size != 1 result.log(:error, "Exactly one signer needed, found #{@pkcs7.signers.size}") end unless signer_certificate result.log(:error, "Signer serial=#{signer_info.serial} issuer=#{signer_info.issuer} " \ "not found in certificates stored in PKCS7 object") return result end key_usage = signer_certificate.extensions.find {|ext| ext.oid == 'keyUsage' } unless key_usage && key_usage.value.split(', ').include?("Digital Signature") result.log(:error, "Certificate key usage is missing 'Digital Signature'") end if signature_dict.signature_type == 'ETSI.RFC3161' # Getting the needed values is not directly supported by Ruby OpenSSL p7 = OpenSSL::ASN1.decode(signature_dict.contents.sub(/\x00*\z/, '')) signed_data = p7.value[1].value[0] content_info = signed_data.value[2] content = OpenSSL::ASN1.decode(content_info.value[1].value[0].value) digest_algorithm = content.value[2].value[0].value[0].value original_hash = content.value[2].value[1].value recomputed_hash = OpenSSL::Digest.digest(digest_algorithm, signature_dict.signed_data) hash_valid = (original_hash == recomputed_hash) else data = signature_dict.signed_data hash_valid = true # hash will be checked by @pkcs7.verify end if hash_valid && @pkcs7.verify(certificate_chain, store, data, OpenSSL::PKCS7::DETACHED | OpenSSL::PKCS7::BINARY) result.log(:info, "Signature valid") else result.log(:error, "Signature verification failed") end result end |