Class: HexaPDF::DigitalSignature::CMSHandler

Inherits:
Handler
  • Object
show all
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

#signature_dict

Instance Method Summary collapse

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_chainObject

Returns the certificate chain.



66
67
68
# File 'lib/hexapdf/digital_signature/cms_handler.rb', line 66

def certificate_chain
  @pkcs7.certificates
end

#signer_certificateObject

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_infoObject

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_nameObject

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_timeObject

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]
     = signed_data.value[2]
    content = OpenSSL::ASN1.decode(.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