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 collapse

CERTIFICATE_COMMANDS =
[:get_bank_certificate, :create_certificate, :renew_certificate].freeze

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:



12
13
14
# File 'lib/sepa/banks/danske/danske_response.rb', line 12

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



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

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:



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

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:



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

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



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

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.



189
190
191
192
193
194
195
# File 'lib/sepa/banks/danske/danske_response.rb', line 189

def can_be_decrypted_with_given_key
  return if CERTIFICATE_COMMANDS.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



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

def certificate
  return super unless CERTIFICATE_COMMANDS.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



114
115
116
117
118
# File 'lib/sepa/banks/danske/danske_response.rb', line 114

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



147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
# File 'lib/sepa/banks/danske/danske_response.rb', line 147

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



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

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



180
181
182
183
184
185
# File 'lib/sepa/banks/danske/danske_response.rb', line 180

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



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

def find_node_by_uri(uri)
  return super unless CERTIFICATE_COMMANDS.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



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

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



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

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

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

#response_codeObject



95
96
97
98
99
# File 'lib/sepa/banks/danske/danske_response.rb', line 95

def response_code
  return super unless CERTIFICATE_COMMANDS.include? @command

  super(namespace: DANSKE_PKI, node_name: 'ReturnCode') || super(namespace: DANSKE_PKIF, node_name: 'ReturnCode')
end

#response_textObject



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

def response_text
  return super unless CERTIFICATE_COMMANDS.include? @command

  super(namespace: DANSKE_PKI, node_name: 'ReturnText') || super(namespace: DANSKE_PKIF, node_name: 'ReturnText')
end

#valid_get_bank_certificate_responseObject (private)

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



168
169
170
171
172
173
# File 'lib/sepa/banks/danske/danske_response.rb', line 168

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