epics 2.9.0 → 2.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: cc38fae79ba6b5ebcbafa535bfd7b4364b708deff8d1d45caa44ed9273a338e1
4
- data.tar.gz: 3a54d33face295d3f0718baeb6343d8aff05946b27aaf11f9df0871869e266bf
3
+ metadata.gz: 33e735bd9012ec4df1bc24d5f1621345adfa6aeba90dbf5c3adcc9be9673c696
4
+ data.tar.gz: f8c5b93115aeb9b60d3be9cbcd36251b6046f5093ed92482c2b4b1d8d4555e90
5
5
  SHA512:
6
- metadata.gz: 3827df11227abf25613c8b27356cbb45465d2be54b58421725bb9625338be4c1bedab8683c3b0f9e9630bc3a63700bfcc897fff130baae465475e53f5112aa43
7
- data.tar.gz: 168a79d4581dcc1053a130081d70bf08741f7d7f96582863963653aab189f866c303b6c582d15584851443d5c65756139b3956ca81c3dfbf0715afc5ba3040ae
6
+ metadata.gz: 6021ed35c892dc544032a95e89ef5a8361f27ea022a5a2776dfea3adea1e7ebbbb2642e145a0c4efb01157c228953c171a511747d21087e520a1b59411f82f81
7
+ data.tar.gz: 71ef90f98028d9e4bff31aa077d52fdbd7cadc12e8126792f7e71bb6e00b4b409a1ad9e36b54e2e345115e602734a7ddaaa26f3b214e54503055929a5600f114
data/CHANGELOG.md CHANGED
@@ -1,5 +1,10 @@
1
1
  ### Unreleased
2
2
 
3
+ ### 2.10.0
4
+
5
+ - [ENHANCEMENT] Added X.509 certificates support for INI and HIA (thanks to @vnoviskyi)
6
+ - [ENHANCEMENT] Added Z01 order type (thanks to @Nymuxyzo)
7
+
3
8
  ### 2.9.0
4
9
 
5
10
  - [ENHANCEMENT] Added HEV order type (thanks to @jplot)
data/README.md CHANGED
@@ -41,7 +41,7 @@ Once the paperwork is done, your bank should provide you with:
41
41
  Take these parameters and start setting up an UserID (repeat this for every user you want to initialize):
42
42
 
43
43
  ```ruby
44
- e = Epics::Client.setup("my-super-secret", "https://ebics.sandbox", "EBICS_HOST_ID", "EBICS_USER_ID", "EBICS_PARTNER_ID")
44
+ e = Epics::Client.setup("my-super-secret", "https://ebics.sandbox", "EBICS_HOST_ID", "EBICS_USER_ID", "EBICS_PARTNER_ID", 4096)
45
45
  ```
46
46
 
47
47
  To use the keys later, just store them in a file
@@ -200,6 +200,76 @@ that are hiding some strange names from you:
200
200
  If you need more sophisticated EBICS order types, please read the next section
201
201
  about the supported functionalities.
202
202
 
203
+ ### Using X.509 Certificates
204
+
205
+ Epics supports using X.509 self-signed certificates for INI and HIA requests, as required by some banks. This is in addition to the classic key-based workflow.
206
+
207
+ #### When to Use
208
+
209
+ Some banks require X.509 certificates for EBICS initialization (INI/HIA).
210
+
211
+ You can generate your own X.509 certificate using Ruby’s OpenSSL library:
212
+
213
+ This examples showcases the generation of the X.509 certificate A file and can be applied the same way for the others.
214
+ ```ruby
215
+ key = client.a.key # or e key, or x key
216
+ name = OpenSSL::X509::Name.parse('/CN=Test Certificate/O=MyOrg/C=DE')
217
+ cert = OpenSSL::X509::Certificate.new
218
+ cert.version = 2
219
+ cert.serial = SecureRandom.random_number(2**64)
220
+ cert.subject = name
221
+ cert.issuer = name
222
+ cert.public_key = key.public_key
223
+ cert.not_before = Time.current
224
+ cert.not_after = cert.not_before + 1.year
225
+
226
+ ef = OpenSSL::X509::ExtensionFactory.new
227
+ ef.subject_certificate = cert
228
+ ef.issuer_certificate = cert
229
+ cert.add_extension(ef.create_extension('basicConstraints', 'CA:FALSE', true))
230
+ cert.add_extension(ef.create_extension('keyUsage', 'digitalSignature,nonRepudiation,keyEncipherment', true))
231
+
232
+ cert.sign(key, OpenSSL::Digest.new('SHA256'))
233
+ cert
234
+
235
+ # Save to file
236
+ File.write("cert_a.pem", cert.to_pem)
237
+ ```
238
+ You can now use the contents of the generated certificate file in PEM format as your
239
+ `x_509_certificate_a_content`, `x_509_certificate_x_content`, or `x_509_certificate_e_content`
240
+ in the client initialization.
241
+
242
+ **Note:** For production environments, your bank may require certificates issued by a trusted authority. Be sure to confirm your bank’s requirements before proceeding.
243
+
244
+ #### Initializing the Client with X.509 Certificates
245
+ ```ruby
246
+ # Load your certificate data (PEM or DER encoded)
247
+ certificate_a = File.read("cert_a.pem")
248
+ certificate_x = File.read("cert_x.pem")
249
+ certificate_e = File.read("cert_e.pem")
250
+
251
+ client = Epics::Client.new(
252
+ keys, # your key data as before
253
+ 'passphrase',
254
+ 'url',
255
+ 'host',
256
+ 'user',
257
+ 'partner',
258
+ x_509_certificate_a_content: certificate_a,
259
+ x_509_certificate_x_content: certificate_x,
260
+ x_509_certificate_e_content: certificate_e,
261
+ debug_mode: true # Optional: enables verbose logging of EBICS requests/responses
262
+ )
263
+ ```
264
+ ### Example: Generating the Initialization Letter with Certificates
265
+
266
+ ```ruby
267
+ renderer = Epics::LetterRenderer.new(client)
268
+ letter = renderer.render("Your Bank Name")
269
+ File.write("initialization_letter.txt", letter)
270
+ ```
271
+ If all three certificates are present, the INI letter will use certificate hashes as required for certificate-based registration.
272
+
203
273
  ## Issues and Feature Requests
204
274
 
205
275
  [Railslove](http://railslove.com) is commited to provide the best developer tools for integrating
@@ -258,8 +328,8 @@ EPICS_VERIFY_SSL=false
258
328
  ## Contributing
259
329
  Railslove has a [Contributor License Agreement (CLA)](https://github.com/railslove/epics/blob/master/CONTRIBUTING.md) which clarifies the intellectual property rights for contributions from individuals or entities. To ensure every developer has signed the CLA, we use [CLA Assistant](https://cla-assistant.io/).
260
330
 
261
- After checking out the repo, run `bin/setup` to install dependencies.
262
- Then, run `rspec` to run the tests.
331
+ After checking out the repo, run `bin/setup` to install dependencies.
332
+ Then, run `rspec` to run the tests.
263
333
  You can also run `bin/console` for an interactive prompt that will allow you to experiment.
264
334
 
265
335
  0. Contact team@railslove.com for information about the CLA
data/lib/epics/client.rb CHANGED
@@ -1,12 +1,14 @@
1
1
  class Epics::Client
2
2
  extend Forwardable
3
3
 
4
- attr_accessor :passphrase, :url, :host_id, :user_id, :partner_id, :keys, :keys_content, :locale, :product_name
5
- attr_writer :iban, :bic, :name
4
+ attr_accessor :passphrase, :url, :host_id, :user_id, :partner_id, :keys, :keys_content, :locale, :product_name,
5
+ :x_509_certificates_content, :debug_mode
6
6
 
7
+ attr_writer :iban, :bic, :name
8
+
7
9
  def_delegators :connection, :post
8
-
9
- def initialize(keys_content, passphrase, url, host_id, user_id, partner_id, locale: Epics::DEFAULT_LOCALE, product_name: Epics::DEFAULT_PRODUCT_NAME)
10
+
11
+ def initialize(keys_content, passphrase, url, host_id, user_id, partner_id, options = {})
10
12
  self.keys_content = keys_content.respond_to?(:read) ? keys_content.read : keys_content if keys_content
11
13
  self.passphrase = passphrase
12
14
  self.keys = extract_keys if keys_content
@@ -14,8 +16,14 @@ class Epics::Client
14
16
  self.host_id = host_id
15
17
  self.user_id = user_id
16
18
  self.partner_id = partner_id
17
- self.locale = locale
18
- self.product_name = product_name
19
+ self.locale = options[:locale] || Epics::DEFAULT_LOCALE
20
+ self.product_name = options[:product_name] || Epics::DEFAULT_PRODUCT_NAME
21
+ self.debug_mode = !!options[:debug_mode]
22
+ self.x_509_certificates_content = {
23
+ a: options[:x_509_certificate_a_content],
24
+ x: options[:x_509_certificate_x_content],
25
+ e: options[:x_509_certificate_e_content]
26
+ }
19
27
  end
20
28
 
21
29
  def inspect
@@ -61,8 +69,8 @@ class Epics::Client
61
69
  @order_types ||= (self.HTD; @order_types)
62
70
  end
63
71
 
64
- def self.setup(passphrase, url, host_id, user_id, partner_id, keysize = 2048)
65
- client = new(nil, passphrase, url, host_id, user_id, partner_id)
72
+ def self.setup(passphrase, url, host_id, user_id, partner_id, keysize = 2048, options = {})
73
+ client = new(nil, passphrase, url, host_id, user_id, partner_id, options)
66
74
  client.keys = %w(A006 X002 E002).each_with_object({}) do |type, memo|
67
75
  memo[type] = Epics::Key.new( OpenSSL::PKey::RSA.generate(keysize) )
68
76
  end
@@ -225,6 +233,10 @@ class Epics::Client
225
233
  download_and_unzip(Epics::C5N, from: from, to: to)
226
234
  end
227
235
 
236
+ def Z01(from, to)
237
+ download_and_unzip(Epics::Z01, from: from, to: to)
238
+ end
239
+
228
240
  def Z52(from, to)
229
241
  download_and_unzip(Epics::Z52, from: from, to: to)
230
242
  end
@@ -273,6 +285,19 @@ class Epics::Client
273
285
  def save_keys(path)
274
286
  File.write(path, dump_keys)
275
287
  end
288
+
289
+ def x_509_certificate(type)
290
+ content = x_509_certificates_content[type.to_sym]
291
+ return if content.nil? || content.empty?
292
+ Epics::X509Certificate.new(content)
293
+ end
294
+
295
+ def x_509_certificate_hash(type)
296
+ content = x_509_certificates_content[type.to_sym]
297
+ return if content.nil? || content.empty?
298
+ cert = OpenSSL::X509::Certificate.new(content)
299
+ Digest::SHA256.hexdigest(cert.to_der).upcase
300
+ end
276
301
 
277
302
  private
278
303
 
@@ -313,7 +338,7 @@ class Epics::Client
313
338
  faraday.use Epics::XMLSIG, { client: self }
314
339
  faraday.use Epics::ParseEbics, { client: self}
315
340
  # faraday.use MyAdapter
316
- # faraday.response :logger # log requests to STDOUT
341
+ faraday.response :logger, ::Logger.new(STDOUT), bodies: true if debug_mode # log requests/response to STDOUT
317
342
  end
318
343
  end
319
344
 
@@ -105,4 +105,18 @@ class Epics::GenericRequest
105
105
  }
106
106
  end.to_xml(save_with: Nokogiri::XML::Node::SaveOptions::AS_XML, encoding: 'utf-8')
107
107
  end
108
+
109
+ private
110
+
111
+ def x509_data_xml(xml, x_509_certificate)
112
+ return unless x_509_certificate
113
+
114
+ xml.send('ds:X509Data') do
115
+ xml.send('ds:X509IssuerSerial') do
116
+ xml.send('ds:X509IssuerName', x_509_certificate.issuer)
117
+ xml.send('ds:X509SerialNumber', x_509_certificate.version)
118
+ end
119
+ xml.send('ds:X509Certificate', x_509_certificate.data)
120
+ end
121
+ end
108
122
  end
data/lib/epics/hia.rb CHANGED
@@ -26,6 +26,7 @@ class Epics::HIA < Epics::GenericRequest
26
26
  Nokogiri::XML::Builder.new do |xml|
27
27
  xml.HIARequestOrderData('xmlns:ds' => 'http://www.w3.org/2000/09/xmldsig#', 'xmlns' => 'urn:org:ebics:H004') {
28
28
  xml.AuthenticationPubKeyInfo {
29
+ x509_data_xml(xml, client.x_509_certificate(:x))
29
30
  xml.PubKeyValue {
30
31
  xml.send('ds:RSAKeyValue') {
31
32
  xml.send('ds:Modulus', Base64.strict_encode64([client.x.n].pack("H*")))
@@ -35,6 +36,7 @@ class Epics::HIA < Epics::GenericRequest
35
36
  xml.AuthenticationVersion 'X002'
36
37
  }
37
38
  xml.EncryptionPubKeyInfo{
39
+ x509_data_xml(xml, client.x_509_certificate(:e))
38
40
  xml.PubKeyValue {
39
41
  xml.send('ds:RSAKeyValue') {
40
42
  xml.send('ds:Modulus', Base64.strict_encode64([client.e.n].pack("H*")))
data/lib/epics/ini.rb CHANGED
@@ -26,6 +26,7 @@ class Epics::INI < Epics::GenericRequest
26
26
  Nokogiri::XML::Builder.new do |xml|
27
27
  xml.SignaturePubKeyOrderData('xmlns:ds' => 'http://www.w3.org/2000/09/xmldsig#', 'xmlns' => 'http://www.ebics.org/S001') {
28
28
  xml.SignaturePubKeyInfo {
29
+ x509_data_xml(xml, client.x_509_certificate(:a))
29
30
  xml.PubKeyValue {
30
31
  xml.send('ds:RSAKeyValue') {
31
32
  xml.send('ds:Modulus', Base64.strict_encode64([client.a.n].pack("H*")))
@@ -1,7 +1,6 @@
1
1
  class Epics::LetterRenderer
2
2
  extend Forwardable
3
3
 
4
- TEMPLATE_PATH = File.join(File.dirname(__FILE__), '../letter/', 'ini.erb')
5
4
  I18N_SCOPE = 'epics.letter'
6
5
 
7
6
  def initialize(client)
@@ -12,11 +11,32 @@ class Epics::LetterRenderer
12
11
  I18n.translate(key, **{ locale: @client.locale, scope: I18N_SCOPE }.merge(options))
13
12
  end
14
13
 
15
- alias_method :t, :translate
14
+ alias t translate
16
15
 
17
16
  def_delegators :@client, :host_id, :user_id, :partner_id, :a, :x, :e
18
17
 
19
18
  def render(bankname)
20
- ERB.new(File.read(TEMPLATE_PATH)).result(binding)
19
+ template_path = File.join(File.dirname(__FILE__), '../letter/', template_filename)
20
+ ERB.new(File.read(template_path)).result(binding)
21
+ end
22
+
23
+ def template_filename
24
+ use_x_509_certificate_template? ? 'ini_with_certs.erb' : 'ini.erb'
25
+ end
26
+
27
+ def use_x_509_certificate_template?
28
+ x_509_certificate_a_hash && x_509_certificate_x_hash && x_509_certificate_e_hash
29
+ end
30
+
31
+ def x_509_certificate_a_hash
32
+ @client.x_509_certificate_hash(:a)
33
+ end
34
+
35
+ def x_509_certificate_x_hash
36
+ @client.x_509_certificate_hash(:x)
37
+ end
38
+
39
+ def x_509_certificate_e_hash
40
+ @client.x_509_certificate_hash(:e)
21
41
  end
22
42
  end
data/lib/epics/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Epics
4
- VERSION = '2.9.0'
4
+ VERSION = '2.10.0'
5
5
  end
@@ -0,0 +1,15 @@
1
+ class Epics::X509Certificate
2
+ extend Forwardable
3
+
4
+ attr_reader :certificate
5
+
6
+ def_delegators :certificate, :issuer, :version
7
+
8
+ def initialize(crt_content)
9
+ @certificate = OpenSSL::X509::Certificate.new(crt_content)
10
+ end
11
+
12
+ def data
13
+ Base64.strict_encode64(@certificate.to_der)
14
+ end
15
+ end
data/lib/epics/z01.rb ADDED
@@ -0,0 +1,17 @@
1
+ class Epics::Z01 < Epics::GenericRequest
2
+ def header
3
+ client.header_request.build(
4
+ nonce: nonce,
5
+ timestamp: timestamp,
6
+ order_type: 'Z01',
7
+ order_attribute: 'DZHNN',
8
+ order_params: {
9
+ DateRange: {
10
+ Start: options[:from],
11
+ End: options[:to]
12
+ }
13
+ },
14
+ mutable: { TransactionPhase: 'Initialisation' }
15
+ )
16
+ end
17
+ end
data/lib/epics.rb CHANGED
@@ -32,6 +32,7 @@ require "epics/c52"
32
32
  require "epics/c53"
33
33
  require "epics/c54"
34
34
  require "epics/c5n"
35
+ require "epics/z01"
35
36
  require "epics/z52"
36
37
  require "epics/z53"
37
38
  require "epics/z54"
@@ -58,6 +59,7 @@ require "epics/hia"
58
59
  require "epics/ini"
59
60
  require "epics/hev"
60
61
  require "epics/signer"
62
+ require "epics/x_509_certificate"
61
63
  require "epics/client"
62
64
 
63
65
  I18n.load_path += Dir[File.join(File.dirname(__FILE__), 'letter/locales', '*.yml')]
@@ -0,0 +1,335 @@
1
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
2
+ <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
3
+ <head>
4
+ <meta http-equiv="content-type" content="text/html; charset=utf-8">
5
+ <meta charset="UTF-8" />
6
+ <title>EBICS ini</title>
7
+ <style>
8
+ code {
9
+ font-size: 0.6rem;
10
+ overflow-wrap: break-word;
11
+ }
12
+ .code {
13
+ border-style: solid;
14
+ border-width: 1px;
15
+ padding: 8px;
16
+ background-color: azure;
17
+ }
18
+ .strong {
19
+ font-weight: bold;
20
+ }
21
+ h2 {
22
+ text-align: center;
23
+ }
24
+ td {
25
+ min-width: 150px;
26
+ }
27
+ table {
28
+ display: flex;
29
+ justify-content: center;
30
+ }
31
+ .column {
32
+ height: 100px;
33
+ }
34
+ </style>
35
+ </head>
36
+ <body>
37
+ <div>
38
+ <h2><%= t('initialization_letter.a') %></h2>
39
+ <table>
40
+ <tr>
41
+ <td>
42
+ <div class="column">
43
+ <table>
44
+ <tr>
45
+ <td class="strong">
46
+ <%= t('date') %> :
47
+ </td>
48
+ <td>
49
+ <%= Date.today.strftime('%d.%m.%Y') %>
50
+ </td>
51
+ </tr>
52
+ <tr>
53
+ <td class="strong">
54
+ <%= t('time') %> :
55
+ </td>
56
+ <td>
57
+ <%= Time.now.strftime('%H:%M:%S') %>
58
+ </td>
59
+ </tr>
60
+ <tr>
61
+ <td class="strong">
62
+ <%= t('recipient') %> :
63
+ </td>
64
+ <td>
65
+ <%= bankname %>
66
+ </td>
67
+ </tr>
68
+ </table>
69
+ </div>
70
+ </td>
71
+ <td>
72
+ <div class="column">
73
+ <table>
74
+ <tr>
75
+ <td class="strong">
76
+ Host-ID :
77
+ </td>
78
+ <td>
79
+ <%= host_id %>
80
+ </td>
81
+ </tr>
82
+ <tr>
83
+ <td class="strong">
84
+ User-ID :
85
+ </td>
86
+ <td>
87
+ <%= user_id %>
88
+ </td>
89
+ </tr>
90
+ <tr>
91
+ <td class="strong">
92
+ Partner-ID :
93
+ </td>
94
+ <td>
95
+ <%= partner_id %>
96
+ </td>
97
+ </tr>
98
+ <tr>
99
+ <td class="strong">
100
+ <%= t('version') %> :
101
+ </td>
102
+ <td>
103
+ A006
104
+ </td>
105
+ </tr>
106
+ </table>
107
+ </div>
108
+ </td>
109
+ </tr>
110
+ </table>
111
+ <p><%= t('certificate') %> :</p>
112
+ <pre class="code"><code><%= @client.x_509_certificates_content[:a] %></code></pre>
113
+ <p><%= t('hash') %> (SHA-256) :</p>
114
+ <p class="code"><code><%= x_509_certificate_a_hash.scan(/../).join(":") %></code></p>
115
+ <p><%= t('confirmation') %></p>
116
+ <br/>
117
+ <br/>
118
+ <br/>
119
+ <br/>
120
+ <table>
121
+ <tr>
122
+ <td>
123
+ _________________________
124
+ </td>
125
+ <td>
126
+ _________________________
127
+ </td>
128
+ <td>
129
+ _________________________
130
+ </td>
131
+ </tr>
132
+ <tr>
133
+ <td>
134
+ <%= t('issued_in') %>
135
+ </td>
136
+ <td>
137
+ <%= t('name') %>
138
+ </td>
139
+ <td>
140
+ <%= t('signature') %>
141
+ </td>
142
+ </tr>
143
+ </table>
144
+ </div>
145
+ <div style="page-break-after:always"></div>
146
+ <div>
147
+ <h2><%= t('initialization_letter.x') %></h2>
148
+ <table>
149
+ <tr>
150
+ <td>
151
+ <div class="column">
152
+ <table>
153
+ <tr>
154
+ <td class="strong">
155
+ <%= t('date') %> :
156
+ </td>
157
+ <td>
158
+ <%= Date.today.strftime('%d.%m.%Y') %>
159
+ </td>
160
+ </tr>
161
+ <tr>
162
+ <td class="strong">
163
+ <%= t('time') %> :
164
+ </td>
165
+ <td>
166
+ <%= Time.now.strftime('%H:%M:%S') %>
167
+ </td>
168
+ </tr>
169
+ <tr>
170
+ <td class="strong">
171
+ <%= t('recipient') %> :
172
+ </td>
173
+ <td>
174
+ <%= bankname %>
175
+ </td>
176
+ </tr>
177
+ </table>
178
+ </div>
179
+ </td>
180
+ <td>
181
+ <div class="column">
182
+ <table>
183
+ <tr>
184
+ <td class="strong">
185
+ Host-ID :
186
+ </td>
187
+ <td>
188
+ <%= host_id %>
189
+ </td>
190
+ </tr>
191
+ <tr>
192
+ <td class="strong">
193
+ User-ID :
194
+ </td>
195
+ <td>
196
+ <%= user_id %>
197
+ </td>
198
+ </tr>
199
+ <tr>
200
+ <td class="strong">
201
+ Partner-ID :
202
+ </td>
203
+ <td>
204
+ <%= partner_id %>
205
+ </td>
206
+ </tr>
207
+ <tr>
208
+ <td class="strong">
209
+ <%= t('version') %> :
210
+ </td>
211
+ <td>
212
+ X002
213
+ </td>
214
+ </tr>
215
+ </table>
216
+ </div>
217
+ </td>
218
+ </tr>
219
+ </table>
220
+ <p><%= t('certificate') %> :</p>
221
+ <pre class="code"><code><%= @client.x_509_certificates_content[:x] %></code></pre>
222
+ <p><%= t('hash') %> (SHA-256) :</p>
223
+ <p class="code"><code><%= x_509_certificate_x_hash.scan(/../).join(":") %></code></p>
224
+ </div>
225
+ <div style="page-break-after:always"></div>
226
+ <div>
227
+ <h2><%= t('initialization_letter.e') %></h2>
228
+ <table>
229
+ <tr>
230
+ <td>
231
+ <div class="column">
232
+ <table>
233
+ <tr>
234
+ <td class="strong">
235
+ <%= t('date') %> :
236
+ </td>
237
+ <td>
238
+ <%= Date.today.strftime('%d.%m.%Y') %>
239
+ </td>
240
+ </tr>
241
+ <tr>
242
+ <td class="strong">
243
+ <%= t('time') %> :
244
+ </td>
245
+ <td>
246
+ <%= Time.now.strftime('%H:%M:%S') %>
247
+ </td>
248
+ </tr>
249
+ <tr>
250
+ <td class="strong">
251
+ <%= t('recipient') %> :
252
+ </td>
253
+ <td>
254
+ <%= bankname %>
255
+ </td>
256
+ </tr>
257
+ </table>
258
+ </div>
259
+ </td>
260
+ <td>
261
+ <div class="column">
262
+ <table>
263
+ <tr>
264
+ <td class="strong">
265
+ Host-ID :
266
+ </td>
267
+ <td>
268
+ <%= host_id %>
269
+ </td>
270
+ </tr>
271
+ <tr>
272
+ <td class="strong">
273
+ User-ID :
274
+ </td>
275
+ <td>
276
+ <%= user_id %>
277
+ </td>
278
+ </tr>
279
+ <tr>
280
+ <td class="strong">
281
+ Partner-ID :
282
+ </td>
283
+ <td>
284
+ <%= partner_id %>
285
+ </td>
286
+ </tr>
287
+ <tr>
288
+ <td class="strong">
289
+ <%= t('version') %> :
290
+ </td>
291
+ <td>
292
+ E002
293
+ </td>
294
+ </tr>
295
+ </table>
296
+ </div>
297
+ </td>
298
+ </tr>
299
+ </table>
300
+ <p><%= t('certificate') %> :</p>
301
+ <pre class="code"><code><%= @client.x_509_certificates_content[:e] %></code></pre>
302
+ <p><%= t('hash') %> (SHA-256) :</p>
303
+ <p class="code"><code><%= x_509_certificate_e_hash.scan(/../).join(":") %></code></p>
304
+ <p><%= t('confirmation') %></p>
305
+ <br/>
306
+ <br/>
307
+ <br/>
308
+ <br/>
309
+ <table>
310
+ <tr>
311
+ <td>
312
+ _________________________
313
+ </td>
314
+ <td>
315
+ _________________________
316
+ </td>
317
+ <td>
318
+ _________________________
319
+ </td>
320
+ </tr>
321
+ <tr>
322
+ <td>
323
+ <%= t('issued_in') %>
324
+ </td>
325
+ <td>
326
+ <%= t('name') %>
327
+ </td>
328
+ <td>
329
+ <%= t('signature') %>
330
+ </td>
331
+ </tr>
332
+ </table>
333
+ </div>
334
+ </body>
335
+ </html>