Class: Sepa::DanskeResponse

Inherits:
Response show all
Defined in:
lib/sepa/banks/danske/danske_response.rb

Overview

Handles Danske Bank specific Response functionality. Mainly decryption and certificate specific stuff.

Constant Summary

Constants included from ErrorMessages

ErrorMessages::CONTENT_ERROR_MESSAGE, ErrorMessages::CUSTOMER_ID_ERROR_MESSAGE, ErrorMessages::DECRYPTION_ERROR_MESSAGE, ErrorMessages::ENCRYPTION_CERT_ERROR_MESSAGE, ErrorMessages::ENCRYPTION_CERT_REQUEST_ERROR_MESSAGE, ErrorMessages::ENCRYPTION_PRIVATE_KEY_ERROR_MESSAGE, ErrorMessages::ENVIRONMENT_ERROR_MESSAGE, ErrorMessages::FILE_REFERENCE_ERROR_MESSAGE, ErrorMessages::FILE_TYPE_ERROR_MESSAGE, ErrorMessages::HASH_ERROR_MESSAGE, ErrorMessages::NOT_OK_RESPONSE_CODE_ERROR_MESSAGE, ErrorMessages::PIN_ERROR_MESSAGE, ErrorMessages::SIGNATURE_ERROR_MESSAGE, ErrorMessages::SIGNING_CERT_REQUEST_ERROR_MESSAGE, ErrorMessages::STATUS_ERROR_MESSAGE, ErrorMessages::TARGET_ID_ERROR_MESSAGE

Instance Attribute Summary

Attributes inherited from Response

#command, #environment, #error, #soap

Instance Method Summary collapse

Methods inherited from Response

#client_errors, #content, #doc, #document_must_validate_against_schema, #error_doc, #extract_application_response, #file_references, #find_digest_values, #find_nodes_to_verify, #hashes_match?, #initialize, #response_code_is_ok?, #signature_is_valid?, #to_s, #validate_hashes, #validate_response_code, #verify_certificate, #verify_signature

Methods included from Utilities

#calculate_digest, #canonicalize_exclusively, #canonicalized_node, #cert_request_valid?, #check_validity_against_schema, #csr_to_binary, #decode, #encode, #extract_cert, #format_cert, #format_cert_request, #hmac, #iso_time, #load_body_template, #process_cert_value, #rsa_key, #set_node_id, #validate_signature, #verify_certificate_against_root_certificate, #x509_certificate, #xml_doc

Constructor Details

This class inherits a constructor from Sepa::Response

Instance Method Details

#application_responseString

Returns:

  • (String)

See Also:



10
11
12
# File 'lib/sepa/banks/danske/danske_response.rb', line 10

def application_response
  @application_response ||= decrypt_application_response
end

#bank_encryption_certificateOpenSSL::X509::Certificate?

Returns the bank's encryption certificate which is used to encrypt messages sent to the bank. The certificate is only present in :get_bank_certificate responess.

Returns:

  • (OpenSSL::X509::Certificate)

    if Response#command is :get_bank_certificate

  • (nil)

    if command is any other



19
20
21
22
23
# File 'lib/sepa/banks/danske/danske_response.rb', line 19

def bank_encryption_certificate
  return unless @command == :get_bank_certificate

  @bank_encryption_certificate ||= extract_cert(doc, 'BankEncryptionCert', DANSKE_PKI)
end

#bank_root_certificateOpenSSL::X509::Certificate?

Returns the bank's root certificate which is the certificate that is used to sign bank's other certificates. Only present in :get_bank_certificate responses.

Returns:



41
42
43
44
45
# File 'lib/sepa/banks/danske/danske_response.rb', line 41

def bank_root_certificate
  return unless @command == :get_bank_certificate

  @bank_root_certificate ||= extract_cert(doc, 'BankRootCert', DANSKE_PKI)
end

#bank_signing_certificateOpenSSL::X509::Certificate?

Returns the bank's signing certificate which is used by the bank to sign the responses. The certificate is only present in :get_bank_certificate responses

Returns:



30
31
32
33
34
# File 'lib/sepa/banks/danske/danske_response.rb', line 30

def bank_signing_certificate
  return unless @command == :get_bank_certificate

  @bank_signing_certificate ||= extract_cert(doc, 'BankSigningCert', DANSKE_PKI)
end

#ca_certificateOpenSSL::X509::Certificate?

Returns the CA certificate that has been used to sign own signing and encryption certificates. Only present in :create_certificate & :renew_certificate responses

Returns:

  • (OpenSSL::X509::Certificate)

    if Response#command is :create_certificate or :renew_certificate

  • (nil)

    if command is any other



74
75
76
77
78
# File 'lib/sepa/banks/danske/danske_response.rb', line 74

def ca_certificate
  return unless [:create_certificate, :renew_certificate].include?(@command)

  @ca_certificate ||= extract_cert(doc, 'CACert', DANSKE_PKI)
end

#can_be_decrypted_with_given_keyObject (private)

Validates that the encrypted key in the response can be decrypted with the private key given to the response in the parameters. Response is invalid if this cannot be done.



205
206
207
208
209
210
211
# File 'lib/sepa/banks/danske/danske_response.rb', line 205

def can_be_decrypted_with_given_key
  return if [:get_bank_certificate, :create_certificate, :renew_certificate].include? @command
  return unless encrypted_application_response.css('CipherValue', 'xmlns' => XMLENC)[0]
  return if decrypt_embedded_key

  errors.add(:encryption_private_key, DECRYPTION_ERROR_MESSAGE)
end

#certificateOpenSSL::X509::Certificate

Extract certificate that has been used to sign the response. This overrides Response#certificate method with specific functionality for :get_bank_certificate, :create_certificate & :renew_certificate commands. Otherwise just calls Response#certificate

Returns:

  • (OpenSSL::X509::Certificate)

Raises:

  • (OpenSSL::X509::CertificateError)

    if certificate cannot be processed



86
87
88
89
90
# File 'lib/sepa/banks/danske/danske_response.rb', line 86

def certificate
  return super unless [:get_bank_certificate, :create_certificate, :renew_certificate].include? @command

  @certificate ||= extract_cert(doc, 'X509Certificate', DSIG)
end

#certificate_is_trusted?true, false

Checks whether certificate embedded in the response has been signed with the bank's root certificate. Always returns true when Response#command is :get_bank_certificate, because the certificate is not present with that command.

Returns:

  • (true)

    if certificate is trusted

  • (false)

    if certificate is not trusted



130
131
132
133
134
# File 'lib/sepa/banks/danske/danske_response.rb', line 130

def certificate_is_trusted?
  return true if @command == :get_bank_certificate

  verify_certificate_against_root_certificate(certificate, DANSKE_ROOT_CERTIFICATE)
end

#decrypt_application_responseString (private)

Decrypts the application response in the response. Starts by calling #decrypt_embedded_key method to get the key used in encrypting the application response. After this the encrypted data is retrieved from the document and base64 decoded. After this the iv (initialization vector) is extracted from the encrypted data and a decipher with the 'DES-EDE3-CBC' algorithm is initialized (This is used by banks as encryption algorithm) and its key and iv set accordingly and mode changes to decrypt. After this the data is decrypted and returned as string.

Returns:

  • (String)

    the decrypted application response as raw xml



163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
# File 'lib/sepa/banks/danske/danske_response.rb', line 163

def decrypt_application_response
  key = decrypt_embedded_key

  encypted_data = encrypted_application_response
                  .css('CipherValue', 'xmlns' => XMLENC)[1]
                  .content

  encypted_data = decode encypted_data
  iv            = encypted_data[0, 8]
  encypted_data = encypted_data[8, encypted_data.length]

  decipher = OpenSSL::Cipher.new('DES-EDE3-CBC')
  decipher.decrypt
  decipher.key = key
  decipher.iv = iv

  decipher.update(encypted_data) + decipher.final
end

#decrypt_embedded_keyString? (private)

Decrypts (assymetrically) the symmetric encryption key embedded in the response with the private key given to the response in the parameters. The key is later used to decrypt the application response.

Returns:

  • (String)

    the encryption key as a string

  • (nil)

    if the key cannot be decrypted with the given key



219
220
221
222
223
224
225
226
# File 'lib/sepa/banks/danske/danske_response.rb', line 219

def decrypt_embedded_key
  enc_key = encrypted_application_response.css('CipherValue', 'xmlns' => XMLENC)[0].content
  enc_key = decode enc_key
  @encryption_private_key.private_decrypt(enc_key)

rescue OpenSSL::PKey::RSAError
  nil
end

#encrypted_application_responseNokogiri::XML? (private)

Extracts the encrypted application response from the response and returns it as a nokogiri document

Returns:

  • (Nokogiri::XML)

    the encrypted application response if it is found

  • (nil)

    if the application response cannot be found



196
197
198
199
200
201
# File 'lib/sepa/banks/danske/danske_response.rb', line 196

def encrypted_application_response
  @encrypted_application_response ||= begin
    encrypted_application_response = extract_application_response(BXD)
    xml_doc(encrypted_application_response)
  end
end

#find_node_by_uri(uri) ⇒ Nokogiri::XML::Node (private)

Finds a node by its reference URI from Danske Bank's certificate responses. If Response#command is other than :get_bank_certificate, :create_certificate or :renew_certificate returns super. This method is needed because Danske Bank uses a different way to reference nodes in their certificate responses.

Parameters:

  • uri (String)

    reference URI of the node to find

Returns:

  • (Nokogiri::XML::Node)

    node with signature removed from its document since signature has to be removed for canonicalization and hash calculation



146
147
148
149
150
151
152
# File 'lib/sepa/banks/danske/danske_response.rb', line 146

def find_node_by_uri(uri)
  return super unless [:get_bank_certificate, :create_certificate, :renew_certificate].include? @command

  doc_without_signature = doc.dup
  doc_without_signature.at('xmlns|Signature', xmlns: DSIG).remove
  doc_without_signature.at("[xml|id='#{uri}']")
end

#own_encryption_certificateOpenSSL::X509::Certificate?

Returns own encryption certificate which has been signed by the bank. Only present in :create_certificate & :renew_certificate responses

Returns:

  • (OpenSSL::X509::Certificate)

    if Response#command is :create_certificate or :renew_certificate

  • (nil)

    if command is any other



52
53
54
55
56
# File 'lib/sepa/banks/danske/danske_response.rb', line 52

def own_encryption_certificate
  return unless [:create_certificate, :renew_certificate].include?(@command)

  @own_encryption_certificate ||= extract_cert(doc, 'EncryptionCert', DANSKE_PKI)
end

#own_signing_certificateOpenSSL::X509::Certificate?

Returns own signing certificate which has been signed by the bank. Is used to sign requests sent to the bank. Is only present in :create_certificate & :renew_certificate responses.

Returns:

  • (OpenSSL::X509::Certificate)

    if Response#command is :create_certificate or :renew_certificate

  • (nil)

    if command is any other



63
64
65
66
67
# File 'lib/sepa/banks/danske/danske_response.rb', line 63

def own_signing_certificate
  return unless [:create_certificate, :renew_certificate].include?(@command)

  @own_signing_certificate ||= extract_cert(doc, 'SigningCert', DANSKE_PKI)
end

#response_codeString?

Extract response code from the response. Overrides super method when Response#command is :get_bank_certificate, :create_certificate or :renew_certificate because response code node is named differently in those responses.

Returns:

  • (String)

    if response code is found

  • (nil)

    if response code cannot be found

See Also:



99
100
101
102
103
104
105
106
# File 'lib/sepa/banks/danske/danske_response.rb', line 99

def response_code
  return super unless [:get_bank_certificate, :create_certificate, :renew_certificate].include? @command

  node = doc.at('xmlns|ReturnCode', xmlns: DANSKE_PKI)
  node = doc.at('xmlns|ReturnCode', xmlns: DANSKE_PKIF) unless node

  node.content if node
end

#response_textString?

Extract response text from the response. Overrides super method when Response#command is :get_bank_certificate, :create_certificate or :renew_certificate because response text node is named differently in those responses.

Returns:

  • (String)

    if response text is found

  • (nil)

    if response text cannot be found

See Also:



115
116
117
118
119
120
121
122
# File 'lib/sepa/banks/danske/danske_response.rb', line 115

def response_text
  return super unless [:get_bank_certificate, :create_certificate, :renew_certificate].include? @command

  node = doc.at('xmlns|ReturnText', xmlns: DANSKE_PKI)
  node = doc.at('xmlns|ReturnText', xmlns: DANSKE_PKIF) unless node

  node.content if node
end

#valid_get_bank_certificate_responseObject (private)

Validates get bank certificate response. Response is valid if service fault is not returned from the bank.



184
185
186
187
188
189
# File 'lib/sepa/banks/danske/danske_response.rb', line 184

def valid_get_bank_certificate_response
  return unless @command == :get_bank_certificate
  return unless doc.at('xmlns|PKIFactoryServiceFault', xmlns: DANSKE_PKIF)

  errors.add(:base, "Did not get a proper response when trying to get bank's certificates")
end