sepafm 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +35 -0
- data/.ruby-version +1 -0
- data/Gemfile +4 -0
- data/LICENSE +8 -0
- data/README.md +236 -0
- data/Rakefile +10 -0
- data/lib/danske_get_bank_certificate_test.rb +15 -0
- data/lib/sepa/application_request.rb +182 -0
- data/lib/sepa/application_response.rb +123 -0
- data/lib/sepa/client.rb +79 -0
- data/lib/sepa/danske_testing/keys/danske_encryption.crt +24 -0
- data/lib/sepa/filedescriptor.rb +7 -0
- data/lib/sepa/filetypeservice.rb +6 -0
- data/lib/sepa/nordea_testing/keys/CSR.csr +0 -0
- data/lib/sepa/nordea_testing/keys/nordea.crt +27 -0
- data/lib/sepa/nordea_testing/keys/nordea.key +19 -0
- data/lib/sepa/nordea_testing/response/content_053.xml +998 -0
- data/lib/sepa/nordea_testing/response/content_054.xml +1 -0
- data/lib/sepa/nordea_testing/response/download_file_response.xml +14 -0
- data/lib/sepa/nordea_testing/response/download_filelist_response.xml +14 -0
- data/lib/sepa/nordea_testing/response/get_user_info_response.xml +14 -0
- data/lib/sepa/nordea_testing/response/upload_file_response.xml +14 -0
- data/lib/sepa/response.rb +177 -0
- data/lib/sepa/sender_verifier.rb +15 -0
- data/lib/sepa/signature.rb +7 -0
- data/lib/sepa/soap_builder.rb +395 -0
- data/lib/sepa/soap_danske.rb +47 -0
- data/lib/sepa/soap_nordea.rb +68 -0
- data/lib/sepa/userfiletype.rb +16 -0
- data/lib/sepa/version.rb +3 -0
- data/lib/sepa/wsdl/wsdl_danske.xml +234 -0
- data/lib/sepa/wsdl/wsdl_danske_cert.xml +280 -0
- data/lib/sepa/wsdl/wsdl_nordea.xml +234 -0
- data/lib/sepa/wsdl/wsdl_nordea_cert.xml +187 -0
- data/lib/sepa/xml_parser.rb +291 -0
- data/lib/sepa/xml_schemas/application_request.xsd +135 -0
- data/lib/sepa/xml_schemas/application_response.xsd +311 -0
- data/lib/sepa/xml_schemas/cert_application_request.xsd +107 -0
- data/lib/sepa/xml_schemas/danske_pki.xsd +334 -0
- data/lib/sepa/xml_schemas/oasis-200401-wss-wssecurity-secext-1.0.xsd +195 -0
- data/lib/sepa/xml_schemas/oasis-200401-wss-wssecurity-utility-1.0.xsd +108 -0
- data/lib/sepa/xml_schemas/soap.xsd +126 -0
- data/lib/sepa/xml_schemas/wsdl.xml +310 -0
- data/lib/sepa/xml_schemas/xml.xsd +287 -0
- data/lib/sepa/xml_schemas/xmldsig-core-schema.xsd +318 -0
- data/lib/sepa/xml_templates/application_request/create_certificate.xml +10 -0
- data/lib/sepa/xml_templates/application_request/danske_get_bank_certificate.xml +10 -0
- data/lib/sepa/xml_templates/application_request/download_file.xml +32 -0
- data/lib/sepa/xml_templates/application_request/download_file_list.xml +29 -0
- data/lib/sepa/xml_templates/application_request/get_certificate.xml +10 -0
- data/lib/sepa/xml_templates/application_request/get_user_info.xml +26 -0
- data/lib/sepa/xml_templates/application_request/upload_file.xml +29 -0
- data/lib/sepa/xml_templates/soap/create_certificate.xml +15 -0
- data/lib/sepa/xml_templates/soap/danske_get_bank_certificate.xml +14 -0
- data/lib/sepa/xml_templates/soap/download_file.xml +16 -0
- data/lib/sepa/xml_templates/soap/download_file_list.xml +16 -0
- data/lib/sepa/xml_templates/soap/get_certificate.xml +13 -0
- data/lib/sepa/xml_templates/soap/get_user_info.xml +16 -0
- data/lib/sepa/xml_templates/soap/header.xml +37 -0
- data/lib/sepa/xml_templates/soap/upload_file.xml +16 -0
- data/lib/sepa.rb +21 -0
- data/lib/sepa_client_testing_mika.rb +32 -0
- data/lib/sepa_client_testing_tiere.rb +80 -0
- data/sepa.gemspec +29 -0
- data/test/sepa/application_request_test.rb +423 -0
- data/test/sepa/application_response_test.rb +238 -0
- data/test/sepa/cert_application_request_test.rb +99 -0
- data/test/sepa/client_test.rb +425 -0
- data/test/sepa/danske_test_keys/danskeroot.pem +25 -0
- data/test/sepa/danske_test_keys/encryption_pkcs.csr +0 -0
- data/test/sepa/danske_test_keys/signing_key.pem +27 -0
- data/test/sepa/danske_test_keys/signing_pkcs.csr +0 -0
- data/test/sepa/nordea_cert_request_soap_builder_test.rb +112 -0
- data/test/sepa/nordea_generic_soap_builder_test.rb +427 -0
- data/test/sepa/nordea_test_keys/nordea.crt +27 -0
- data/test/sepa/nordea_test_keys/nordea.key +19 -0
- data/test/sepa/nordea_test_keys/root_cert.cer +0 -0
- data/test/sepa/nordea_test_keys/testcert.csr +0 -0
- data/test/sepa/response_test.rb +269 -0
- data/test/sepa/sepa_test.rb +20 -0
- data/test/sepa/test_files/invalid.wsdl +1 -0
- data/test/sepa/test_files/test_responses/df.xml +20 -0
- data/test/sepa/test_files/test_responses/dfl.xml +20 -0
- data/test/sepa/test_files/test_responses/gui.xml +20 -0
- data/test/sepa/test_files/test_responses/uf.xml +20 -0
- data/test/sepa/user_file_type_test.rb +21 -0
- data/test/sepa/xml_parser_test.rb +73 -0
- data/test/test_helper.rb +9 -0
- metadata +256 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: d8d907ad1e367406e6f1a98af49893e5f6211b04
|
4
|
+
data.tar.gz: cc91cebf5cfc771b33e98c4136f948fde2626631
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 812949f6537bfd6e1df79f7a58b74033593e0788aa99103016eba8f9e578e2dd073337a11f58ff4fca586812d4b4842690e807f0c692c1f4bdaec9900481b02f
|
7
|
+
data.tar.gz: e8a469ea127b5003583a99a1c2cfc593fdb53ecfc43b95046bab97c67f1e83d1d9d3844e8442e46b0d78f376fdab2aeb4687a1c5dedb3f9ab931b5af1a2b877e
|
data/.gitignore
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
*.gem
|
2
|
+
*.rbc
|
3
|
+
.bundle
|
4
|
+
.config
|
5
|
+
coverage
|
6
|
+
Gemfile.lock
|
7
|
+
InstalledFiles
|
8
|
+
lib/bundler/man
|
9
|
+
pkg
|
10
|
+
rdoc
|
11
|
+
spec/reports
|
12
|
+
test/tmp
|
13
|
+
test/version_tmp
|
14
|
+
tmp
|
15
|
+
vendor
|
16
|
+
lib/debug.rb
|
17
|
+
lib/CSR.csr
|
18
|
+
lib/signing_key.pem
|
19
|
+
lib/encrypting_key.pem
|
20
|
+
lib/req_key.pem
|
21
|
+
lib/danske
|
22
|
+
|
23
|
+
# YARD artifacts
|
24
|
+
.yardoc
|
25
|
+
_yardoc
|
26
|
+
doc/
|
27
|
+
|
28
|
+
# ms-crap
|
29
|
+
*~
|
30
|
+
|
31
|
+
# osx-crap
|
32
|
+
.DS_Store
|
33
|
+
|
34
|
+
#Coverage
|
35
|
+
coverage
|
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
2.0.0-p247
|
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,8 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
Copyright (c) 2013 Devlab Oy, http://www.devlab.fi
|
3
|
+
|
4
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
5
|
+
|
6
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
7
|
+
|
8
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,236 @@
|
|
1
|
+
# Devlab / SEPA
|
2
|
+
|
3
|
+
This project aims to create an open source implementation of SEPA Financial Messages using Web Services. Project implementation will be done in Ruby. We will also create a REST API for this module.
|
4
|
+
|
5
|
+
## First milestone
|
6
|
+
|
7
|
+
* Support for SEPA Web Services
|
8
|
+
* Customer-to-Bank Statement. ISO standard "CustomerCreditTransferInitiationV03", XML schema "pain.001.001.03"
|
9
|
+
* Bank-to-Customer Statement. ISO standard "BankToCustomerStatementV02", XML schema "camt.053.001.02"
|
10
|
+
* Bank-to-Customer Debit/Credit Notification. ISO standard "BankToCustomerDebitCreditNotificationV02", XML schma "camt.054.001.02"
|
11
|
+
* Update README
|
12
|
+
|
13
|
+
## Installation
|
14
|
+
|
15
|
+
Add this line to your application's Gemfile:
|
16
|
+
|
17
|
+
gem 'sepa'
|
18
|
+
|
19
|
+
And then execute:
|
20
|
+
|
21
|
+
$ bundle
|
22
|
+
|
23
|
+
Or install it yourself as:
|
24
|
+
|
25
|
+
$ gem install sepa
|
26
|
+
|
27
|
+
## Usage
|
28
|
+
|
29
|
+
### Communicating with the bank
|
30
|
+
|
31
|
+
1. Require the gem:
|
32
|
+
|
33
|
+
require 'sepa'
|
34
|
+
|
35
|
+
2. Define the hash that will be passed to the gem when initializing it:
|
36
|
+
|
37
|
+
params = {
|
38
|
+
bank: :nordea,
|
39
|
+
private_key_path: "path/to/key", (OR private_key_plain : "Your private key in plain text form ")
|
40
|
+
cert_path: "path/to/key", (OR cert_plain : "Your certificate in plain text form ")
|
41
|
+
command: :command_as_symbol,
|
42
|
+
customer_id: '11111111',
|
43
|
+
environment: 'PRODUCTION',
|
44
|
+
status: 'NEW',
|
45
|
+
target_id: '11111111A1',
|
46
|
+
language: 'FI',
|
47
|
+
file_type: 'TITO',
|
48
|
+
content: payload,
|
49
|
+
file_reference: "11111111A12006030329501800000014"
|
50
|
+
}
|
51
|
+
|
52
|
+
3. Initialize a new instance of the client and pass the params hash
|
53
|
+
|
54
|
+
sepa_client = Sepa::Client.new(params)
|
55
|
+
|
56
|
+
4. There is only one method that can be called after initializing the client:
|
57
|
+
|
58
|
+
* Returns the whole soap response as a savon response object:
|
59
|
+
|
60
|
+
response = client.send
|
61
|
+
|
62
|
+
### Verifying the response
|
63
|
+
|
64
|
+
* Check that the hashes match in the response
|
65
|
+
|
66
|
+
response.hashes_match?
|
67
|
+
|
68
|
+
# You can also provide an optional parameter verbose:true
|
69
|
+
# if you want to see which hashes failed to verify.
|
70
|
+
|
71
|
+
response.hashes_match?(verbose: true)
|
72
|
+
|
73
|
+
* Check that the signature of the response is valid
|
74
|
+
|
75
|
+
response.signature_is_valid?
|
76
|
+
|
77
|
+
* Extract the certificate from the response
|
78
|
+
|
79
|
+
# Will return an OpenSSL::X509::Certificate object
|
80
|
+
response.certificate
|
81
|
+
|
82
|
+
* Check that the certificate is trusted against a root cert
|
83
|
+
|
84
|
+
# The root cert has to be of type OpenSSL::X509::Certificate
|
85
|
+
response.cert_is_trusted?(root_cert)
|
86
|
+
|
87
|
+
### Verifying the application response
|
88
|
+
|
89
|
+
1. Extract the application request from the request
|
90
|
+
|
91
|
+
ar = response.application_request
|
92
|
+
|
93
|
+
* Check that the hashes match in the application response
|
94
|
+
|
95
|
+
ar.hashes_match?
|
96
|
+
|
97
|
+
* Check that the signature of the application response is valid
|
98
|
+
|
99
|
+
ar.signature_is_valid?
|
100
|
+
|
101
|
+
* Extract the certificate from the application response
|
102
|
+
|
103
|
+
# Will return an OpenSSL::X509::Certificate object
|
104
|
+
ar.certificate
|
105
|
+
|
106
|
+
* Check that the certificate is trusted against a root cert
|
107
|
+
|
108
|
+
# The root cert has to be of type OpenSSL::X509::Certificate
|
109
|
+
ar.cert_is_trusted?(root_cert)
|
110
|
+
|
111
|
+
### For downloading Nordea certificate
|
112
|
+
|
113
|
+
1. Require the gem:
|
114
|
+
|
115
|
+
require 'sepa'
|
116
|
+
|
117
|
+
2. Define the hash that will be passed to the gem when initializing it:
|
118
|
+
|
119
|
+
params = {
|
120
|
+
bank: :nordea,
|
121
|
+
command: :get_certificate,
|
122
|
+
customer_id: '11111111',
|
123
|
+
environment: 'TEST',
|
124
|
+
csr_path: "path_to_your_local_csr_file", (OR csr_plain: "your csr in plain text format")
|
125
|
+
service: 'service'
|
126
|
+
}
|
127
|
+
|
128
|
+
3. Initialize a new instance of the client and pass the params hash
|
129
|
+
|
130
|
+
sepa_client = Sepa::Client.new(params)
|
131
|
+
sepa_client.call
|
132
|
+
|
133
|
+
4. Save the certificate from the response into a local file
|
134
|
+
|
135
|
+
### For downloading Danske bank certificates
|
136
|
+
|
137
|
+
1. Require the gem:
|
138
|
+
|
139
|
+
require 'sepa'
|
140
|
+
|
141
|
+
2. Define the hash that will be passed to the gem when initializing it:
|
142
|
+
|
143
|
+
params = {
|
144
|
+
bank: :danske,
|
145
|
+
target_id: 'Danske FI',
|
146
|
+
language: 'EN',
|
147
|
+
command: :get_bank_certificate,
|
148
|
+
bank_root_cert_serial: '1111110002',
|
149
|
+
customer_id: '360817',
|
150
|
+
environment: 'TEST',
|
151
|
+
}
|
152
|
+
|
153
|
+
3. Initialize a new instance of the client and pass the params hash
|
154
|
+
|
155
|
+
sepa_client = Sepa::Client.new(params)
|
156
|
+
sepa_client.call
|
157
|
+
|
158
|
+
4. Save the certificates from the response into a local file
|
159
|
+
|
160
|
+
***
|
161
|
+
|
162
|
+
### Parameter breakdown
|
163
|
+
|
164
|
+
* bank : The bank you want to send the request to as a symbol. Either :nordea or :danske
|
165
|
+
|
166
|
+
* private_key_plain: Your private key in plain text format
|
167
|
+
|
168
|
+
* private_key_path: Path to your local private key file
|
169
|
+
|
170
|
+
* cert_plain: Your certificate in plain text format.
|
171
|
+
|
172
|
+
* cert_path: Path to your local certificate file
|
173
|
+
|
174
|
+
* csr_plain: Your certificate signing request in plain text format
|
175
|
+
|
176
|
+
* csr_path: Path to your local certificate signing request file
|
177
|
+
|
178
|
+
* command: Either :download_file_list, :upload_file, :download_file, :get_user_info, :get_certificate or :get_bank_certificate, depending on what you want to do.
|
179
|
+
|
180
|
+
* customer_id: Your personal id with the bank.
|
181
|
+
|
182
|
+
* environment: Must be either PRODUCTION or TEST
|
183
|
+
|
184
|
+
* status: For filtering stuff. Must be either NEW, DOWNLOADED or ALL
|
185
|
+
|
186
|
+
* target_id: Some specification of the folder which to access in the bank.
|
187
|
+
|
188
|
+
* language: Language must be either FI, EN or SV
|
189
|
+
|
190
|
+
* file_type: File types to upload or download:
|
191
|
+
|
192
|
+
* LMP300 = Laskujen maksupalvelu (lähtevä)
|
193
|
+
|
194
|
+
* LUM2 = Valuuttamaksut (lähtevä)
|
195
|
+
|
196
|
+
* KTL = Saapuvat viitemaksut (saapuva)
|
197
|
+
|
198
|
+
* TITO = Konekielinen tiliote (saapuva)
|
199
|
+
|
200
|
+
* NDCORPAYS = Yrityksen maksut XML (lähtevä)
|
201
|
+
|
202
|
+
* NDCAMT53L = Konekielinen XML-tiliote (saapuva)
|
203
|
+
|
204
|
+
* NDCAMT54L = Saapuvat XML viitemaksu (saapuva)
|
205
|
+
|
206
|
+
* content: The actual payload to send. The creation of this file may be supported by the client at some point.
|
207
|
+
|
208
|
+
* file_reference: File reference for :download_file command
|
209
|
+
|
210
|
+
* pin: Your personal pin-code provided by the bank
|
211
|
+
|
212
|
+
* service: For testing value is service, otherwise ISSUER
|
213
|
+
|
214
|
+
* bank_root_cert_serial: Serial number for Danske bank certificate download (1111110002)
|
215
|
+
|
216
|
+
***
|
217
|
+
|
218
|
+
### Parsing data from bank response xml
|
219
|
+
Parsing based on specifications by Federation of Finnish Financial Services provided xml examples account statement [XML account statement](http://www.fkl.fi/teemasivut/sepa/tekninen_dokumentaatio/Dokumentit/FI_camt_053_sample.xml.xml) and debit credit notification [XML debit credit notification](http://www.fkl.fi/teemasivut/sepa/tekninen_dokumentaatio/Dokumentit/FI_camt_054_sample.xml.xml) and ISO20022 transaction reporting guide [ISO20022 Transaction reporting guide](http://www.fkl.fi/en/themes/sepa/sepa_documents/Dokumentit/ISO20022_Payment_Guide.pdf)
|
220
|
+
* Hardcode wanted specs into app_response.rb methods get_account_statement_content/get_debit_credit_notification_content
|
221
|
+
* Create new instance of ApplicationResponse
|
222
|
+
* method get_account_statement_content takes a bank statement file (xml) as a parameter and returns selected info in a hash
|
223
|
+
* method get_debit_credit_notification_content takes a debit credit notification file (xml) as a parameter and returns selected info in a hash
|
224
|
+
* method animate_response takes a full application response xml as a parameter and parses data into objects, can be used to take out different formats of Content-field, without predefined parameter specs
|
225
|
+
|
226
|
+
## Contributing
|
227
|
+
|
228
|
+
1. Fork it
|
229
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
230
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
231
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
232
|
+
5. Create new Pull Request
|
233
|
+
|
234
|
+
## License
|
235
|
+
|
236
|
+
Released under the [MIT License](http://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'sepa'
|
2
|
+
|
3
|
+
params = {
|
4
|
+
bank: :danske,
|
5
|
+
target_id: 'Danske FI',
|
6
|
+
language: 'EN',
|
7
|
+
command: :get_bank_certificate,
|
8
|
+
bank_root_cert_serial: '1111110002',
|
9
|
+
customer_id: '360817',
|
10
|
+
environment: 'TEST',
|
11
|
+
}
|
12
|
+
|
13
|
+
sepa_client = Sepa::Client.new(params)
|
14
|
+
|
15
|
+
sepa_client.send
|
@@ -0,0 +1,182 @@
|
|
1
|
+
module Sepa
|
2
|
+
class ApplicationRequest
|
3
|
+
def initialize(params)
|
4
|
+
# Used by most, both Nordea and Danske
|
5
|
+
@command = check_command(params.fetch(:command))
|
6
|
+
@customer_id = params.fetch(:customer_id)
|
7
|
+
@environment = params.fetch(:environment)
|
8
|
+
@target_id = params[:target_id]
|
9
|
+
@status = params[:status]
|
10
|
+
@file_type = params[:file_type]
|
11
|
+
@content = params[:content]
|
12
|
+
@file_reference = params[:file_reference]
|
13
|
+
|
14
|
+
@private_key, @cert, @pin, @service, @csr, @hmac,
|
15
|
+
@bank_root_cert_serial,@request_id = ''
|
16
|
+
|
17
|
+
# Set values for the previously defined attributes
|
18
|
+
initialize_required_fields_per_request(params)
|
19
|
+
end
|
20
|
+
|
21
|
+
def get_as_base64
|
22
|
+
load_template(@command)
|
23
|
+
set_nodes_contents
|
24
|
+
# No signature for Certificate Requests
|
25
|
+
if @command != :get_certificate && @command != :get_bank_certificate
|
26
|
+
process_signature
|
27
|
+
end
|
28
|
+
|
29
|
+
Base64.encode64(@ar.to_xml)
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def initialize_required_fields_per_request(params)
|
35
|
+
generic_commands = [:get_user_info, :upload_file, :download_file,
|
36
|
+
:download_file_list]
|
37
|
+
|
38
|
+
case @command
|
39
|
+
when *generic_commands
|
40
|
+
@private_key = params.fetch(:private_key)
|
41
|
+
@cert = params.fetch(:cert)
|
42
|
+
when :get_certificate
|
43
|
+
@service = params[:service]
|
44
|
+
@pin = params[:pin]
|
45
|
+
@csr = params[:csr]
|
46
|
+
@hmac = create_hmac_seal(@pin,@csr)
|
47
|
+
when :get_bank_certificate
|
48
|
+
@pin = params[:pin]
|
49
|
+
@request_id = params[:request_id]
|
50
|
+
@bank_root_cert_serial = params[:bank_root_cert_serial]
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def check_command(command)
|
55
|
+
valid_commands = [:get_certificate, :download_file_list, :download_file,
|
56
|
+
:get_user_info, :upload_file, :download_file,
|
57
|
+
:get_bank_certificate]
|
58
|
+
unless valid_commands.include?(command)
|
59
|
+
fail ArgumentError, "You didn't provide a proper command. " \
|
60
|
+
"Acceptable values are #{valid_commands.inspect}"
|
61
|
+
else
|
62
|
+
command
|
63
|
+
end
|
64
|
+
end
|
65
|
+
# Loads the application request template according to the command
|
66
|
+
def load_template(command)
|
67
|
+
template_dir = File.expand_path('../xml_templates/application_request',
|
68
|
+
__FILE__)
|
69
|
+
|
70
|
+
case command
|
71
|
+
|
72
|
+
when :get_certificate
|
73
|
+
path = "#{template_dir}/get_certificate.xml"
|
74
|
+
when :download_file_list
|
75
|
+
path = "#{template_dir}/download_file_list.xml"
|
76
|
+
when :get_user_info
|
77
|
+
path = "#{template_dir}/get_user_info.xml"
|
78
|
+
when :upload_file
|
79
|
+
path = "#{template_dir}/upload_file.xml"
|
80
|
+
when :download_file
|
81
|
+
path = "#{template_dir}/download_file.xml"
|
82
|
+
when :get_bank_certificate
|
83
|
+
path = "#{template_dir}/danske_get_bank_certificate.xml"
|
84
|
+
end
|
85
|
+
|
86
|
+
@ar = Nokogiri::XML(File.open(path))
|
87
|
+
end
|
88
|
+
|
89
|
+
|
90
|
+
def set_node(node, value)
|
91
|
+
@ar.at_css(node).content = value
|
92
|
+
end
|
93
|
+
|
94
|
+
# Set the nodes' contents according to the command
|
95
|
+
def set_nodes_contents
|
96
|
+
if @command != :get_bank_certificate
|
97
|
+
set_node("CustomerId", @customer_id)
|
98
|
+
set_node("Timestamp", Time.now.iso8601)
|
99
|
+
set_node("Environment", @environment)
|
100
|
+
set_node("SoftwareId", "Sepa Transfer Library version #{VERSION}")
|
101
|
+
set_node("Command",
|
102
|
+
@command.to_s.split(/[\W_]/).map {|c| c.capitalize}.join)
|
103
|
+
end
|
104
|
+
|
105
|
+
case @command
|
106
|
+
|
107
|
+
when :get_certificate
|
108
|
+
set_node("Service", @service)
|
109
|
+
set_node("Content", Base64.encode64(@csr.to_der))
|
110
|
+
set_node("HMAC", Base64.encode64(@hmac).chop)
|
111
|
+
when :download_file_list
|
112
|
+
set_node("Status", @status)
|
113
|
+
set_node("TargetId", @target_id)
|
114
|
+
set_node("FileType", @file_type)
|
115
|
+
when :download_file
|
116
|
+
set_node("Status", @status)
|
117
|
+
set_node("TargetId", @target_id)
|
118
|
+
set_node("FileType", @file_type)
|
119
|
+
set_node("FileReference", @file_reference)
|
120
|
+
when :upload_file
|
121
|
+
set_node("Content", Base64.encode64(@content))
|
122
|
+
set_node("FileType", @file_type)
|
123
|
+
set_node("TargetId", @target_id)
|
124
|
+
when :get_bank_certificate
|
125
|
+
set_node("elem|BankRootCertificateSerialNo", @bank_root_cert_serial)
|
126
|
+
set_node("elem|Timestamp", Time.now.iso8601)
|
127
|
+
set_node("elem|RequestId", @request_id)
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
def create_hmac_seal(pin, csr)
|
132
|
+
hmacseal = OpenSSL::HMAC.digest('sha1',pin,csr.to_der)
|
133
|
+
hmacseal
|
134
|
+
end
|
135
|
+
|
136
|
+
def remove_node(doc, node, xmlns)
|
137
|
+
doc.at_css("xmlns|#{node}", 'xmlns' => xmlns).remove
|
138
|
+
end
|
139
|
+
|
140
|
+
def add_node_to_root(doc, node)
|
141
|
+
doc.root.add_child(node)
|
142
|
+
end
|
143
|
+
|
144
|
+
def calculate_digest(doc)
|
145
|
+
sha1 = OpenSSL::Digest::SHA1.new
|
146
|
+
Base64.encode64(sha1.digest(doc.canonicalize))
|
147
|
+
end
|
148
|
+
|
149
|
+
def add_value_to_signature(node, value)
|
150
|
+
node = @ar.at_css("dsig|#{node}",
|
151
|
+
'dsig' => 'http://www.w3.org/2000/09/xmldsig#')
|
152
|
+
node.content = value
|
153
|
+
end
|
154
|
+
|
155
|
+
def calculate_signature(private_key)
|
156
|
+
sha1 = OpenSSL::Digest::SHA1.new
|
157
|
+
node = @ar.at_css("dsig|SignedInfo",
|
158
|
+
'dsig' => 'http://www.w3.org/2000/09/xmldsig#')
|
159
|
+
signature = private_key.sign(sha1, node.canonicalize)
|
160
|
+
Base64.encode64(signature)
|
161
|
+
end
|
162
|
+
|
163
|
+
def format_cert(cert)
|
164
|
+
cert = cert.to_s
|
165
|
+
cert = cert.split('-----BEGIN CERTIFICATE-----')[1]
|
166
|
+
cert = cert.split('-----END CERTIFICATE-----')[0]
|
167
|
+
cert.gsub!(/\s+/, "")
|
168
|
+
end
|
169
|
+
|
170
|
+
def process_signature
|
171
|
+
signature_node = remove_node(@ar,
|
172
|
+
'Signature',
|
173
|
+
'http://www.w3.org/2000/09/xmldsig#')
|
174
|
+
digest = calculate_digest(@ar)
|
175
|
+
add_node_to_root(@ar, signature_node)
|
176
|
+
add_value_to_signature('DigestValue', digest)
|
177
|
+
signature = calculate_signature(@private_key)
|
178
|
+
add_value_to_signature('SignatureValue', signature)
|
179
|
+
add_value_to_signature('X509Certificate',format_cert(@cert))
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|
@@ -0,0 +1,123 @@
|
|
1
|
+
module Sepa
|
2
|
+
class ApplicationResponse
|
3
|
+
def initialize(ar)
|
4
|
+
@ar = ar
|
5
|
+
|
6
|
+
if !@ar.respond_to?(:canonicalize)
|
7
|
+
fail ArgumentError,
|
8
|
+
"The application response you provided is not a valid Nokogiri::XML" \
|
9
|
+
" file."
|
10
|
+
elsif !valid_against_ar_schema?(@ar)
|
11
|
+
fail ArgumentError,
|
12
|
+
"The application response you provided doesn't validate against" \
|
13
|
+
" application response schema."
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
# Checks that the hash value reported in the signature matches the actual
|
18
|
+
# one.
|
19
|
+
def hashes_match?
|
20
|
+
ar = @ar.clone
|
21
|
+
|
22
|
+
digest_value = ar.at_css(
|
23
|
+
'xmlns|DigestValue',
|
24
|
+
'xmlns' => 'http://www.w3.org/2000/09/xmldsig#'
|
25
|
+
).content.strip
|
26
|
+
|
27
|
+
ar.at_css(
|
28
|
+
"xmlns|Signature",
|
29
|
+
'xmlns' => 'http://www.w3.org/2000/09/xmldsig#'
|
30
|
+
).remove
|
31
|
+
|
32
|
+
actual_digest = calculate_digest(ar)
|
33
|
+
|
34
|
+
if digest_value == actual_digest
|
35
|
+
true
|
36
|
+
else
|
37
|
+
false
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# Extracts the X509 certificate from the application response.
|
42
|
+
def certificate
|
43
|
+
cert_value = @ar.at_css(
|
44
|
+
'xmlns|X509Certificate',
|
45
|
+
'xmlns' => 'http://www.w3.org/2000/09/xmldsig#'
|
46
|
+
).content.gsub(/\s+/, "")
|
47
|
+
|
48
|
+
cert = process_cert_value(cert_value)
|
49
|
+
|
50
|
+
begin
|
51
|
+
OpenSSL::X509::Certificate.new(cert)
|
52
|
+
rescue => e
|
53
|
+
fail OpenSSL::X509::CertificateError,
|
54
|
+
"The certificate embedded in the application response could not be " \
|
55
|
+
"processed. It's most likely corrupted. " \
|
56
|
+
"OpenSSL had this to say: #{e}."
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
# Checks that the signature is signed with the private key of the
|
61
|
+
# certificate's public key.
|
62
|
+
def signature_is_valid?
|
63
|
+
node = @ar.at_css('xmlns|SignedInfo',
|
64
|
+
'xmlns' => 'http://www.w3.org/2000/09/xmldsig#')
|
65
|
+
|
66
|
+
node = node.canonicalize
|
67
|
+
|
68
|
+
signature = @ar.at_css(
|
69
|
+
'xmlns|SignatureValue',
|
70
|
+
'xmlns' => 'http://www.w3.org/2000/09/xmldsig#'
|
71
|
+
).content
|
72
|
+
|
73
|
+
signature = Base64.decode64(signature)
|
74
|
+
|
75
|
+
certificate.public_key.verify(OpenSSL::Digest::SHA1.new, signature, node)
|
76
|
+
end
|
77
|
+
|
78
|
+
# Checks that the certificate in the application response is signed with the
|
79
|
+
# private key of the public key of the certificate as parameter.
|
80
|
+
def cert_is_trusted?(root_cert)
|
81
|
+
if root_cert.subject == certificate.issuer
|
82
|
+
certificate.verify(root_cert.public_key)
|
83
|
+
else
|
84
|
+
fail SecurityError,
|
85
|
+
"The issuer of the certificate doesn't match the subject of the " \
|
86
|
+
"root certificate."
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
private
|
91
|
+
|
92
|
+
def calculate_digest(node)
|
93
|
+
sha1 = OpenSSL::Digest::SHA1.new
|
94
|
+
|
95
|
+
canon_node = node.canonicalize(
|
96
|
+
mode=Nokogiri::XML::XML_C14N_EXCLUSIVE_1_0,
|
97
|
+
inclusive_namespaces=nil,with_comments=false
|
98
|
+
)
|
99
|
+
|
100
|
+
Base64.encode64(sha1.digest(canon_node)).gsub(/\s+/, "")
|
101
|
+
end
|
102
|
+
|
103
|
+
def valid_against_ar_schema?(doc)
|
104
|
+
schemas_path = File.expand_path('../../../lib/sepa/xml_schemas',
|
105
|
+
__FILE__)
|
106
|
+
|
107
|
+
Dir.chdir(schemas_path) do
|
108
|
+
xsd = Nokogiri::XML::Schema(IO.read('application_response.xsd'))
|
109
|
+
xsd.valid?(doc)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
# Takes the certificate from the application response, adds begin and end
|
114
|
+
# certificate texts and splits it into multiple lines so that OpenSSL
|
115
|
+
# can read it.
|
116
|
+
def process_cert_value(cert_value)
|
117
|
+
cert = "-----BEGIN CERTIFICATE-----\n"
|
118
|
+
cert += cert_value.to_s.gsub(/\s+/, "").scan(/.{1,64}/).join("\n")
|
119
|
+
cert += "\n"
|
120
|
+
cert += "-----END CERTIFICATE-----"
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|