Class: Sepa::Response

Inherits:
Object
  • Object
show all
Defined in:
lib/sepa/response.rb

Instance Method Summary collapse

Constructor Details

#initialize(response) ⇒ Response

Returns a new instance of Response.



3
4
5
6
7
8
9
10
11
12
13
# File 'lib/sepa/response.rb', line 3

def initialize(response)
  @response = response

  if !@response.respond_to?(:canonicalize)
    fail ArgumentError,
      "The response you provided is not a valid Nokogiri::XML file."
  elsif !valid_against_schema?(@response)
    fail ArgumentError,
      "The response you provided doesn't validate against soap schema."
  end
end

Instance Method Details

#application_responseObject

Gets the application response from the response as an Nokogiri::XML document



97
98
99
100
101
# File 'lib/sepa/response.rb', line 97

def application_response
  ar = @response.at_css('mod|ApplicationResponse').content
  ar = Base64.decode64(ar)
  Nokogiri::XML(ar)
end

#cert_is_trusted?(root_cert) ⇒ Boolean

Verifies that the soap’s certificate is trusted.

Returns:

  • (Boolean)


36
37
38
39
40
41
42
43
44
# File 'lib/sepa/response.rb', line 36

def cert_is_trusted?(root_cert)
  if root_cert.subject == certificate.issuer
    certificate.verify(root_cert.public_key)
  else
    fail SecurityError,
      "The issuer of the certificate doesn't match the subject of the roo" \
      "t certificate."
  end
end

#certificateObject

Returns the x509 certificate embedded in the soap as an OpenSSL::X509::Certificate



17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# File 'lib/sepa/response.rb', line 17

def certificate
  cert_value = @response.at_css(
    'wsse|BinarySecurityToken',
    'wsse' => 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-ws' \
    'security-secext-1.0.xsd'
  ).content.gsub(/\s+/, "")

  cert = process_cert_value(cert_value)

  begin
    cert = OpenSSL::X509::Certificate.new(cert)
  rescue => e
    fail OpenSSL::X509::CertificateError,
      "The certificate embedded to the soap response could not be process" \
      "ed. It's most likely corrupted. OpenSSL had this to say: #{e}."
      end
end

#hashes_match?(options = {}) ⇒ Boolean

Verifies that all digest values in the response match the actual ones. Takes an optional verbose parameter to show which digests didn’t match i.e. verbose: true

Returns:

  • (Boolean)


49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
# File 'lib/sepa/response.rb', line 49

def hashes_match?(options = {})
  digests = find_digest_values(@response)
  nodes = find_nodes_to_verify(@response, digests)

  verified_digests = digests.select do |uri, digest|
    uri = uri.sub(/^#/, '')
    digest == nodes[uri]
  end

  if digests == verified_digests
    true
  else
    unverified_digests = digests.select do |uri, digest|
      uri = uri.sub(/^#/, '')
      digest != nodes[uri]
    end

    if options[:verbose]
      puts "These digests failed to verify: #{unverified_digests}."
    end

    false
  end
end

#signature_is_valid?Boolean

Verifies the signature by extracting the public key from the certificate embedded in the soap header and verifying the signature value with that.

Returns:

  • (Boolean)


76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
# File 'lib/sepa/response.rb', line 76

def signature_is_valid?
  node = @response.at_css('xmlns|SignedInfo',
                          'xmlns' => 'http://www.w3.org/2000/09/xmldsig#')

  node = node.canonicalize(
    mode=Nokogiri::XML::XML_C14N_EXCLUSIVE_1_0,
    inclusive_namespaces=nil,with_comments=false
  )

  signature = @response.at_css(
    'xmlns|SignatureValue',
    'xmlns' => 'http://www.w3.org/2000/09/xmldsig#'
  ).content

  signature = Base64.decode64(signature)

  certificate.public_key.verify(OpenSSL::Digest::SHA1.new, signature, node)
end