epics 2.10.0 → 3.0.0.rc1
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 +4 -4
- data/.gitignore +2 -1
- data/.semaphore/semaphore.yml +17 -8
- data/CHANGELOG.md +97 -0
- data/EBICS_REFERENCES.md +169 -0
- data/README.md +219 -50
- data/lib/epics/azv.rb +3 -10
- data/lib/epics/b2b.rb +3 -10
- data/lib/epics/bka.rb +3 -14
- data/lib/epics/builders/body_builder/base.rb +26 -0
- data/lib/epics/builders/body_builder/v2.rb +9 -0
- data/lib/epics/builders/body_builder/v3.rb +9 -0
- data/lib/epics/builders/body_builder.rb +6 -0
- data/lib/epics/builders/data_encryption_info_builder.rb +29 -0
- data/lib/epics/builders/data_transfer_builder/base.rb +52 -0
- data/lib/epics/builders/data_transfer_builder/v2.rb +9 -0
- data/lib/epics/builders/data_transfer_builder/v3.rb +12 -0
- data/lib/epics/builders/data_transfer_builder.rb +6 -0
- data/lib/epics/builders/header_builder/base.rb +26 -0
- data/lib/epics/builders/header_builder/v2.rb +9 -0
- data/lib/epics/builders/header_builder/v3.rb +9 -0
- data/lib/epics/builders/header_builder.rb +6 -0
- data/lib/epics/builders/mutable_builder.rb +28 -0
- data/lib/epics/builders/order_details_builder/base.rb +100 -0
- data/lib/epics/builders/order_details_builder/v2.rb +21 -0
- data/lib/epics/builders/order_details_builder/v3.rb +51 -0
- data/lib/epics/builders/order_details_builder.rb +10 -0
- data/lib/epics/builders/request_builder.rb +25 -0
- data/lib/epics/builders/static_builder/base.rb +75 -0
- data/lib/epics/builders/static_builder/v2.rb +9 -0
- data/lib/epics/builders/static_builder/v3.rb +9 -0
- data/lib/epics/builders/static_builder.rb +9 -0
- data/lib/epics/builders/transfer_receipt_builder.rb +19 -0
- data/lib/epics/builders/xml_builder/base.rb +92 -0
- data/lib/epics/builders/xml_builder/v24.rb +31 -0
- data/lib/epics/builders/xml_builder/v25.rb +31 -0
- data/lib/epics/builders/xml_builder/v3.rb +31 -0
- data/lib/epics/builders/xml_builder.rb +7 -0
- data/lib/epics/builders.rb +13 -0
- data/lib/epics/c2s.rb +3 -10
- data/lib/epics/c52.rb +2 -14
- data/lib/epics/c53.rb +2 -14
- data/lib/epics/c54.rb +2 -14
- data/lib/epics/c5n.rb +3 -14
- data/lib/epics/ccs.rb +3 -10
- data/lib/epics/cct.rb +3 -10
- data/lib/epics/cd1.rb +3 -10
- data/lib/epics/cdb.rb +3 -10
- data/lib/epics/cdd.rb +3 -10
- data/lib/epics/cds.rb +3 -10
- data/lib/epics/cdz.rb +3 -14
- data/lib/epics/cip.rb +3 -10
- data/lib/epics/client.rb +491 -123
- data/lib/epics/crypt/aes.rb +56 -0
- data/lib/epics/crypt/x_509.rb +52 -0
- data/lib/epics/crypt.rb +5 -0
- data/lib/epics/crz.rb +3 -14
- data/lib/epics/factories/crypt/aes_factory.rb +5 -0
- data/lib/epics/factories/crypt.rb +4 -0
- data/lib/epics/factories/request_factory/base.rb +236 -0
- data/lib/epics/factories/request_factory/v2.rb +194 -0
- data/lib/epics/factories/request_factory/v24.rb +32 -0
- data/lib/epics/factories/request_factory/v25.rb +31 -0
- data/lib/epics/factories/request_factory/v3.rb +305 -0
- data/lib/epics/factories/request_factory.rb +8 -0
- data/lib/epics/factories.rb +5 -0
- data/lib/epics/fdl.rb +3 -18
- data/lib/epics/ful.rb +6 -0
- data/lib/epics/generic_request.rb +31 -92
- data/lib/epics/generic_upload_request.rb +14 -67
- data/lib/epics/haa.rb +3 -9
- data/lib/epics/hac.rb +3 -14
- data/lib/epics/handlers/auth_signature_handler.rb +102 -0
- data/lib/epics/handlers/order_data_handler/base.rb +98 -0
- data/lib/epics/handlers/order_data_handler/v2.rb +39 -0
- data/lib/epics/handlers/order_data_handler/v24.rb +11 -0
- data/lib/epics/handlers/order_data_handler/v25.rb +11 -0
- data/lib/epics/handlers/order_data_handler/v3.rb +41 -0
- data/lib/epics/handlers/order_data_handler.rb +8 -0
- data/lib/epics/handlers/user_signature_handler/base.rb +10 -0
- data/lib/epics/handlers/user_signature_handler/v2.rb +17 -0
- data/lib/epics/handlers/user_signature_handler/v3.rb +17 -0
- data/lib/epics/handlers/user_signature_handler.rb +6 -0
- data/lib/epics/handlers.rb +6 -0
- data/lib/epics/hev.rb +2 -15
- data/lib/epics/hia.rb +4 -55
- data/lib/epics/hkd.rb +3 -9
- data/lib/epics/hpb.rb +3 -13
- data/lib/epics/hpd.rb +3 -9
- data/lib/epics/htd.rb +3 -9
- data/lib/epics/ini.rb +4 -46
- data/lib/epics/keyring.rb +17 -0
- data/lib/epics/letter_renderer.rb +17 -22
- data/lib/epics/ptk.rb +3 -14
- data/lib/epics/response.rb +34 -29
- data/lib/epics/services/crypt_service.rb +62 -0
- data/lib/epics/services/digest_resolver/base.rb +30 -0
- data/lib/epics/services/digest_resolver/v2.rb +13 -0
- data/lib/epics/services/digest_resolver/v3.rb +19 -0
- data/lib/epics/services/digest_resolver.rb +6 -0
- data/lib/epics/services.rb +5 -0
- data/lib/epics/signature.rb +50 -0
- data/lib/epics/{key.rb → signature_algorithm/base.rb} +14 -21
- data/lib/epics/signature_algorithm/rsa.rb +50 -0
- data/lib/epics/signature_algorithm/rsapkcs1.rb +16 -0
- data/lib/epics/signature_algorithm/rsapss.rb +20 -0
- data/lib/epics/signature_algorithm.rb +2 -0
- data/lib/epics/sta.rb +3 -14
- data/lib/epics/version.rb +1 -1
- data/lib/epics/vmk.rb +3 -14
- data/lib/epics/wss.rb +3 -9
- data/lib/epics/xct.rb +3 -10
- data/lib/epics/xds.rb +3 -10
- data/lib/epics/xe2.rb +3 -10
- data/lib/epics/xe3.rb +3 -10
- data/lib/epics/xek.rb +5 -0
- data/lib/epics/yct.rb +6 -0
- data/lib/epics/z01.rb +2 -14
- data/lib/epics/z52.rb +2 -14
- data/lib/epics/z53.rb +2 -14
- data/lib/epics/z54.rb +2 -14
- data/lib/epics/zsr.rb +5 -0
- data/lib/epics.rb +69 -5
- data/lib/letter/ini.erb +27 -6
- data/spec/aes_spec.rb +46 -0
- data/spec/auth_signature_handler_spec.rb +58 -0
- data/spec/builders_spec.rb +616 -0
- data/spec/certificate_handling_spec.rb +189 -0
- data/spec/client_setup_block_spec.rb +41 -0
- data/spec/client_spec.rb +143 -91
- data/spec/crypt_service_spec.rb +132 -0
- data/spec/date_range_spec.rb +46 -0
- data/spec/digest_resolver_spec.rb +164 -0
- data/spec/fixtures/bank_x509.pem +3 -0
- data/spec/fixtures/xml/haa.xml +40 -1
- data/spec/fixtures/xml/haa_receipt.xml +33 -30
- data/spec/fixtures/xml/haa_response_h004.xml +4 -0
- data/spec/fixtures/xml/haa_response_h005.xml +38 -0
- data/spec/fixtures/xml/hia_request_order_data.xml +24 -1
- data/spec/fixtures/xml/hpb_request.xml +1 -1
- data/spec/fixtures/xml/hpb_response_x509.xml +16 -0
- data/spec/fixtures/xml/htd_order_data_h005.xml +55 -0
- data/spec/fixtures/xml/jruby/hia.xml +20 -22
- data/spec/fixtures/xml/jruby/ini.xml +1 -3
- data/spec/fixtures/xml/ruby/hia.xml +21 -1
- data/spec/fixtures/xml/ruby/ini.xml +21 -1
- data/spec/handlers_spec.rb +267 -0
- data/spec/hpb_malformed_certificate_spec.rb +36 -0
- data/spec/key_file_decryption_spec.rb +27 -0
- data/spec/key_file_preservation_spec.rb +78 -0
- data/spec/keyring_spec.rb +349 -0
- data/spec/middleware/parse_ebics_spec.rb +2 -1
- data/spec/order_id_spec.rb +65 -0
- data/spec/orders/azv_spec.rb +63 -7
- data/spec/orders/b2b_spec.rb +68 -3
- data/spec/orders/bka_spec.rb +34 -4
- data/spec/orders/c2s_spec.rb +62 -9
- data/spec/orders/c52_spec.rb +34 -4
- data/spec/orders/c53_spec.rb +34 -4
- data/spec/orders/c54_spec.rb +36 -4
- data/spec/orders/c5n_spec.rb +36 -4
- data/spec/orders/camt_parity_spec.rb +80 -0
- data/spec/orders/ccs_spec.rb +62 -9
- data/spec/orders/cct_spec.rb +83 -9
- data/spec/orders/cd1_spec.rb +50 -7
- data/spec/orders/cdb_spec.rb +64 -7
- data/spec/orders/cdd_spec.rb +64 -7
- data/spec/orders/cds_spec.rb +74 -0
- data/spec/orders/cdz_spec.rb +67 -4
- data/spec/orders/cip_spec.rb +61 -9
- data/spec/orders/crz_spec.rb +67 -4
- data/spec/orders/fdl_spec.rb +143 -13
- data/spec/orders/ful_spec.rb +63 -0
- data/spec/orders/haa_spec.rb +115 -9
- data/spec/orders/hac_spec.rb +89 -10
- data/spec/orders/hev_spec.rb +1 -6
- data/spec/orders/hia_spec.rb +25 -22
- data/spec/orders/hkd_spec.rb +23 -4
- data/spec/orders/hpb_spec.rb +7 -4
- data/spec/orders/hpd_spec.rb +23 -4
- data/spec/orders/htd_spec.rb +23 -4
- data/spec/orders/ini_spec.rb +23 -18
- data/spec/orders/ptk_spec.rb +27 -4
- data/spec/orders/sta_spec.rb +129 -11
- data/spec/orders/vmk_spec.rb +36 -4
- data/spec/orders/wss_spec.rb +29 -3
- data/spec/orders/x_509_certificate_spec.rb +9 -1
- data/spec/orders/xct_spec.rb +63 -0
- data/spec/orders/xds_spec.rb +13 -6
- data/spec/orders/xe2_spec.rb +64 -7
- data/spec/orders/xe3_spec.rb +64 -7
- data/spec/orders/xek_spec.rb +31 -0
- data/spec/orders/yct_spec.rb +43 -0
- data/spec/orders/z01_spec.rb +34 -4
- data/spec/orders/z52_spec.rb +34 -4
- data/spec/orders/z53_spec.rb +34 -4
- data/spec/orders/z54_spec.rb +36 -4
- data/spec/orders/zsr_spec.rb +31 -0
- data/spec/request_factory_spec.rb +444 -0
- data/spec/response_business_error_spec.rb +45 -0
- data/spec/response_validation_spec.rb +23 -0
- data/spec/{key_spec.rb → signature_algorithm/rsa_pss_spec.rb} +4 -13
- data/spec/support/ebics_matcher.rb +17 -9
- data/spec/support/ebics_shared_examples.rb +642 -0
- data/spec/support/h005_client_helper.rb +23 -0
- data/spec/upload_digest_spec.rb +69 -0
- data/spec/user_signature_handler_spec.rb +25 -0
- data/spec/version_support_error_spec.rb +76 -0
- data/spec/xsd/{ebics_hev.xsd → H000/ebics_hev.xsd} +0 -0
- data/spec/xsd/H003/ebics.xsd +11 -0
- data/spec/xsd/H003/ebics_keymgmt_request.xsd +555 -0
- data/spec/xsd/H003/ebics_keymgmt_response.xsd +131 -0
- data/spec/xsd/H003/ebics_orders.xsd +1811 -0
- data/spec/xsd/H003/ebics_request.xsd +354 -0
- data/spec/xsd/H003/ebics_response.xsd +161 -0
- data/spec/xsd/{ebics_signature.xsd → H003/ebics_signature.xsd} +0 -0
- data/spec/xsd/H003/ebics_types.xsd +2361 -0
- data/spec/xsd/H003/xmldsig-core-schema.xsd +318 -0
- data/spec/xsd/H004/ebics_signature.xsd +217 -0
- data/spec/xsd/H005/ebics_H005.xsd +11 -0
- data/spec/xsd/H005/ebics_keymgmt_request_H005.xsd +523 -0
- data/spec/xsd/H005/ebics_keymgmt_response_H005.xsd +137 -0
- data/spec/xsd/H005/ebics_orders_H005.xsd +2094 -0
- data/spec/xsd/H005/ebics_request_H005.xsd +349 -0
- data/spec/xsd/H005/ebics_response_H005.xsd +167 -0
- data/spec/xsd/H005/ebics_signature_S002.xsd +177 -0
- data/spec/xsd/H005/ebics_types_H005.xsd +1885 -0
- data/spec/xsd/H005/xmldsig-core-schema.xsd +318 -0
- metadata +200 -36
- data/lib/epics/header_request.rb +0 -58
- data/lib/epics/middleware/xmlsig.rb +0 -16
- data/lib/epics/signer.rb +0 -40
- data/lib/epics/x_509_certificate.rb +0 -15
- data/lib/letter/ini_with_certs.erb +0 -335
- data/spec/generic_upload_spec.rb +0 -58
- data/spec/hpb_spec.rb +0 -15
- data/spec/signer_spec.rb +0 -34
- /data/spec/xsd/{ebics_H004.xsd → H004/ebics_H004.xsd} +0 -0
- /data/spec/xsd/{ebics_keymgmt_request_H004.xsd → H004/ebics_keymgmt_request_H004.xsd} +0 -0
- /data/spec/xsd/{ebics_keymgmt_response_H004.xsd → H004/ebics_keymgmt_response_H004.xsd} +0 -0
- /data/spec/xsd/{ebics_orders_H004.xsd → H004/ebics_orders_H004.xsd} +0 -0
- /data/spec/xsd/{ebics_request_H004.xsd → H004/ebics_request_H004.xsd} +0 -0
- /data/spec/xsd/{ebics_response_H004.xsd → H004/ebics_response_H004.xsd} +0 -0
- /data/spec/xsd/{ebics_types_H004.xsd → H004/ebics_types_H004.xsd} +0 -0
- /data/spec/xsd/{xmldsig-core-schema.xsd → H004/xmldsig-core-schema.xsd} +0 -0
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
RSpec.describe 'X.509 certificate handling' do
|
|
2
|
+
let(:url) { 'https://example.com/ebics' }
|
|
3
|
+
let(:passphrase) { 'secret' }
|
|
4
|
+
|
|
5
|
+
def setup_client(**options)
|
|
6
|
+
Epics::Client.setup(passphrase, url, 'HOST01', 'USER01', 'PARTNER01', 1024, **options)
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def foreign_certificate
|
|
10
|
+
key = OpenSSL::PKey::RSA.generate(1024)
|
|
11
|
+
Epics::Crypt::X509.new(generate_x_509_crt(key, '/C=GB/O=Other/CN=other.example.org'))
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
describe 'Client.setup on H005' do
|
|
15
|
+
subject(:client) { setup_client(version: Epics::Keyring::VERSION_30) }
|
|
16
|
+
|
|
17
|
+
it 'generates a certificate for every user key' do
|
|
18
|
+
certificates = [client.keyring.user_signature, client.keyring.user_authentication,
|
|
19
|
+
client.keyring.user_encryption].map(&:certificate)
|
|
20
|
+
|
|
21
|
+
expect(certificates).to all(be_a(Epics::Crypt::X509))
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
it 'generates certificates that match their key' do
|
|
25
|
+
signature = client.keyring.user_signature
|
|
26
|
+
|
|
27
|
+
expect(signature.certificate.certificate.public_key.to_pem)
|
|
28
|
+
.to eq(signature.key.key.public_key.to_pem)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
it 'names the subject after the client identity' do
|
|
32
|
+
expect(client.keyring.user_signature.certificate.certificate.subject.to_s)
|
|
33
|
+
.to include('CN=USER01', 'OU=PARTNER01', 'O=HOST01')
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
it 'accepts a custom subject' do
|
|
37
|
+
client = setup_client(version: Epics::Keyring::VERSION_30, certificate_subject: '/CN=Custom/O=Acme/C=CH')
|
|
38
|
+
|
|
39
|
+
expect(client.keyring.user_signature.certificate.certificate.subject.to_s).to include('CN=Custom')
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
it 'accepts a custom validity' do
|
|
43
|
+
not_after = Time.utc(2030, 1, 1)
|
|
44
|
+
client = setup_client(version: Epics::Keyring::VERSION_30, certificate_not_after: not_after)
|
|
45
|
+
|
|
46
|
+
expect(client.keyring.user_signature.certificate.certificate.not_after).to eq(not_after)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
it 'keeps a supplied certificate instead of generating one' do
|
|
50
|
+
supplied = foreign_certificate
|
|
51
|
+
client = setup_client(version: Epics::Keyring::VERSION_30,
|
|
52
|
+
x_509_certificate_a_content: supplied.to_pem)
|
|
53
|
+
|
|
54
|
+
expect(client.keyring.user_signature.certificate.fingerprint).to eq(supplied.fingerprint)
|
|
55
|
+
expect(client.keyring.user_authentication.certificate).not_to be_nil
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
it 'can be turned off' do
|
|
59
|
+
client = setup_client(version: Epics::Keyring::VERSION_30, generate_certificates: false)
|
|
60
|
+
|
|
61
|
+
expect(client.keyring.user_signature.certificate).to be_nil
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
it 'does not mutate the options hash it was given' do
|
|
65
|
+
options = { version: Epics::Keyring::VERSION_30, signature_version: Epics::Signature::A_VERSION_5 }
|
|
66
|
+
Epics::Client.setup(passphrase, url, 'HOST01', 'USER01', 'PARTNER01', 1024, options)
|
|
67
|
+
|
|
68
|
+
expect(options).to include(signature_version: Epics::Signature::A_VERSION_5)
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
describe 'Client.setup on H004' do
|
|
73
|
+
subject(:client) { setup_client }
|
|
74
|
+
|
|
75
|
+
it 'generates no certificates' do
|
|
76
|
+
expect(client.keyring.user_signature.certificate).to be_nil
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
it 'still accepts them explicitly' do
|
|
80
|
+
client = setup_client(generate_certificates: true)
|
|
81
|
+
|
|
82
|
+
expect(client.keyring.user_signature.certificate).to be_a(Epics::Crypt::X509)
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
describe 'persistence' do
|
|
87
|
+
let(:client) { setup_client(version: Epics::Keyring::VERSION_30) }
|
|
88
|
+
|
|
89
|
+
def reload(json, **options)
|
|
90
|
+
Epics::Client.new(json, passphrase, url, 'HOST01', 'USER01', 'PARTNER01',
|
|
91
|
+
version: Epics::Keyring::VERSION_30, **options)
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
it 'restores user certificates from a key file' do
|
|
95
|
+
fingerprints = %i[user_signature user_authentication user_encryption]
|
|
96
|
+
.map { |slot| client.keyring.send(slot).certificate.fingerprint }
|
|
97
|
+
reloaded = reload(client.send(:dump_keys))
|
|
98
|
+
|
|
99
|
+
expect(%i[user_signature user_authentication user_encryption]
|
|
100
|
+
.map { |slot| reloaded.keyring.send(slot).certificate.fingerprint }).to eq(fingerprints)
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
it 'keeps a restored certificate when no option is passed' do
|
|
104
|
+
reloaded = reload(client.send(:dump_keys))
|
|
105
|
+
|
|
106
|
+
expect(reloaded.keyring.user_signature.certificate).not_to be_nil
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
it 'lets an explicit option override a restored certificate' do
|
|
110
|
+
supplied = foreign_certificate
|
|
111
|
+
reloaded = reload(client.send(:dump_keys), x_509_certificate_a_content: supplied.to_pem)
|
|
112
|
+
|
|
113
|
+
expect(reloaded.keyring.user_signature.certificate.fingerprint).to eq(supplied.fingerprint)
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
it 'keeps the bare form for keys without a certificate' do
|
|
117
|
+
h004 = setup_client
|
|
118
|
+
dumped = JSON.parse(h004.send(:dump_keys))
|
|
119
|
+
|
|
120
|
+
expect(dumped.values).to all(be_a(String))
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
describe 'invalid certificate content' do
|
|
125
|
+
it 'raises and names the option' do
|
|
126
|
+
expect do
|
|
127
|
+
setup_client(version: Epics::Keyring::VERSION_30, x_509_certificate_a_content: 'not-a-pem')
|
|
128
|
+
end.to raise_error(Epics::InvalidCertificateError, /x_509_certificate_a_content/)
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
it 'ignores empty content' do
|
|
132
|
+
client = setup_client(version: Epics::Keyring::VERSION_30, x_509_certificate_x_content: '')
|
|
133
|
+
|
|
134
|
+
expect(client.keyring.user_authentication.certificate).to be_a(Epics::Crypt::X509)
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
# H005 carries the public key inside ds:X509Data, so a request built without a
|
|
139
|
+
# certificate would contain no key material at all.
|
|
140
|
+
describe 'H005 key management without certificates', without_certificates: true do
|
|
141
|
+
let(:client) do
|
|
142
|
+
setup_client(version: Epics::Keyring::VERSION_30, generate_certificates: false)
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
it 'refuses to build INI' do
|
|
146
|
+
expect { Epics::INI.new(client).key_signature }
|
|
147
|
+
.to raise_error(Epics::MissingCertificateError, /x_509_certificate_a_content/)
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
it 'refuses to build HIA' do
|
|
151
|
+
expect { Epics::HIA.new(client).order_data }
|
|
152
|
+
.to raise_error(Epics::MissingCertificateError, /x_509_certificate_x_content/)
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
describe 'H005 key management with certificates' do
|
|
157
|
+
let(:client) { setup_client(version: Epics::Keyring::VERSION_30) }
|
|
158
|
+
|
|
159
|
+
it 'carries the certificate in INI order data' do
|
|
160
|
+
expect(Epics::INI.new(client).key_signature)
|
|
161
|
+
.to include("<ds:X509Certificate>#{client.keyring.user_signature.certificate.data}</ds:X509Certificate>")
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
it 'carries both certificates in HIA order data' do
|
|
165
|
+
order_data = Epics::HIA.new(client).order_data
|
|
166
|
+
|
|
167
|
+
expect(order_data).to include(client.keyring.user_authentication.certificate.data)
|
|
168
|
+
expect(order_data).to include(client.keyring.user_encryption.certificate.data)
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
it 'validates INI order data against the S002 schema' do
|
|
172
|
+
schema = Nokogiri::XML::Schema(File.open(File.join(File.dirname(__FILE__), 'xsd', 'H005',
|
|
173
|
+
'ebics_signature_S002.xsd')))
|
|
174
|
+
|
|
175
|
+
expect(schema.validate(Nokogiri::XML(Epics::INI.new(client).key_signature))).to be_empty
|
|
176
|
+
end
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
describe 'H004 key management' do
|
|
180
|
+
let(:client) { setup_client }
|
|
181
|
+
|
|
182
|
+
it 'still sends the raw modulus and exponent' do
|
|
183
|
+
order_data = Epics::INI.new(client).key_signature
|
|
184
|
+
|
|
185
|
+
expect(order_data).to include('Modulus')
|
|
186
|
+
expect(order_data).not_to include('X509Data')
|
|
187
|
+
end
|
|
188
|
+
end
|
|
189
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
RSpec.describe 'Epics::Client.setup with a block' do
|
|
2
|
+
# Keys are generated after #initialize returns, so the block cannot be forwarded to
|
|
3
|
+
# .new -- it would see an empty keyring.
|
|
4
|
+
it 'yields a client whose keys exist' do
|
|
5
|
+
yielded = nil
|
|
6
|
+
Epics::Client.setup('secret', 'https://example.com/ebics', 'HOST01', 'USER01', 'PARTNER01', 1024) do |client|
|
|
7
|
+
yielded = { signature: client.signature_key, authentication: client.authentication_key,
|
|
8
|
+
encryption: client.encryption_key }
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
expect(yielded.values).to all(be_a(Epics::SignatureAlgorithm::Base))
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
it 'yields the client that is returned' do
|
|
15
|
+
yielded = nil
|
|
16
|
+
returned = Epics::Client.setup('secret', 'https://example.com/ebics', 'HOST01', 'USER01', 'PARTNER01', 1024) do |c|
|
|
17
|
+
yielded = c
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
expect(yielded).to be(returned)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
it 'can render the initialisation letter from inside the block' do
|
|
24
|
+
expect do
|
|
25
|
+
Epics::Client.setup('secret', 'https://example.com/ebics', 'HOST01', 'USER01', 'PARTNER01', 1024,
|
|
26
|
+
version: Epics::Keyring::VERSION_30) do |client|
|
|
27
|
+
client.ini_letter('Test Bank')
|
|
28
|
+
end
|
|
29
|
+
end.not_to raise_error
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
it 'yields after certificates have been generated on H005' do
|
|
33
|
+
yielded = nil
|
|
34
|
+
Epics::Client.setup('secret', 'https://example.com/ebics', 'HOST01', 'USER01', 'PARTNER01', 1024,
|
|
35
|
+
version: Epics::Keyring::VERSION_30) do |client|
|
|
36
|
+
yielded = client.keyring.user_signature.certificate
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
expect(yielded).to be_a(Epics::Crypt::X509)
|
|
40
|
+
end
|
|
41
|
+
end
|
data/spec/client_spec.rb
CHANGED
|
@@ -12,18 +12,13 @@ RSpec.describe Epics::Client do
|
|
|
12
12
|
|
|
13
13
|
it 'holds all keys, user and bank' do
|
|
14
14
|
expect(subject.keys).to match(a_hash_including(
|
|
15
|
-
'E002' => be_a(Epics::
|
|
16
|
-
'X002' => be_a(Epics::
|
|
17
|
-
'A006' => be_a(Epics::
|
|
18
|
-
'SIZBN001.E002' => be_a(Epics::
|
|
19
|
-
'SIZBN001.X002' => be_a(Epics::
|
|
15
|
+
'E002' => be_a(Epics::SignatureAlgorithm::RsaPkcs1),
|
|
16
|
+
'X002' => be_a(Epics::SignatureAlgorithm::RsaPkcs1),
|
|
17
|
+
'A006' => be_a(Epics::SignatureAlgorithm::RsaPss),
|
|
18
|
+
'SIZBN001.E002' => be_a(Epics::SignatureAlgorithm::RsaPkcs1),
|
|
19
|
+
'SIZBN001.X002' => be_a(Epics::SignatureAlgorithm::RsaPkcs1)
|
|
20
20
|
))
|
|
21
21
|
end
|
|
22
|
-
|
|
23
|
-
it 'assigns x_509_certificates_content' do
|
|
24
|
-
subject.x_509_certificates_content = { a: 'cert_a', x: 'cert_x', e: 'cert_e' }
|
|
25
|
-
expect(subject.x_509_certificates_content).to eq({ a: 'cert_a', x: 'cert_x', e: 'cert_e' })
|
|
26
|
-
end
|
|
27
22
|
end
|
|
28
23
|
|
|
29
24
|
context 'environment settings' do
|
|
@@ -56,33 +51,33 @@ RSpec.describe Epics::Client do
|
|
|
56
51
|
end
|
|
57
52
|
end
|
|
58
53
|
|
|
59
|
-
describe '#
|
|
54
|
+
describe '#encryption_key' do
|
|
60
55
|
it 'the encryption key' do
|
|
61
|
-
expect(subject.
|
|
56
|
+
expect(subject.encryption_key.public_digest).to eq('rwIxSUJAVEFDQ0sdYe+CybdspMllDG6ArNtdCzUbT1E=')
|
|
62
57
|
end
|
|
63
58
|
end
|
|
64
59
|
|
|
65
|
-
describe '#
|
|
66
|
-
it 'the
|
|
67
|
-
expect(subject.
|
|
60
|
+
describe '#authentication_key' do
|
|
61
|
+
it 'the authentication key' do
|
|
62
|
+
expect(subject.authentication_key.public_digest).to eq('Jjcu97qg595PPn+0OvqBOBIskMIiStNYYXyjgWHeBhE=')
|
|
68
63
|
end
|
|
69
64
|
end
|
|
70
65
|
|
|
71
|
-
describe '#
|
|
72
|
-
it 'the
|
|
73
|
-
expect(subject.
|
|
66
|
+
describe '#signature_key' do
|
|
67
|
+
it 'the signing key' do
|
|
68
|
+
expect(subject.signature_key.public_digest).to eq('9ay3tc+I3MgJBaroeD7XJfOtHcq7IR23fljWefl0dzk=')
|
|
74
69
|
end
|
|
75
70
|
end
|
|
76
71
|
|
|
77
|
-
describe '#
|
|
72
|
+
describe '#bank_encryption_key' do
|
|
78
73
|
it 'the banks encryption key' do
|
|
79
|
-
expect(subject.
|
|
74
|
+
expect(subject.bank_encryption_key.public_digest).to eq('dFAYe281vj9NB7w+VoWIdfHnjY9hNbZLbHsDOu76QAE=')
|
|
80
75
|
end
|
|
81
76
|
end
|
|
82
77
|
|
|
83
|
-
describe '#
|
|
78
|
+
describe '#bank_authentication_key' do
|
|
84
79
|
it 'the banks signing key' do
|
|
85
|
-
expect(subject.
|
|
80
|
+
expect(subject.bank_authentication_key.public_digest).to eq('dFAYe281vj9NB7w+VoWIdfHnjY9hNbZLbHsDOu76QAE=')
|
|
86
81
|
end
|
|
87
82
|
end
|
|
88
83
|
|
|
@@ -100,7 +95,8 @@ RSpec.describe Epics::Client do
|
|
|
100
95
|
|
|
101
96
|
describe '#HPB' do
|
|
102
97
|
let(:e_key) do
|
|
103
|
-
Epics::
|
|
98
|
+
Epics::SignatureAlgorithm::RsaPss.new(OpenSSL::PKey::RSA.new(File.read(File.join(File.dirname(__FILE__),
|
|
99
|
+
'fixtures', 'bank_e.pem'))))
|
|
104
100
|
end
|
|
105
101
|
|
|
106
102
|
before do
|
|
@@ -110,7 +106,7 @@ RSpec.describe Epics::Client do
|
|
|
110
106
|
'hpb_response_ebics_ns.xml')))
|
|
111
107
|
end
|
|
112
108
|
|
|
113
|
-
it { expect(subject.HPB).to match([be_a(Epics::
|
|
109
|
+
it { expect(subject.HPB).to match([be_a(Epics::SignatureAlgorithm::RsaPkcs1), be_a(Epics::SignatureAlgorithm::RsaPkcs1)]) }
|
|
114
110
|
|
|
115
111
|
it 'changes the SIZBN001.(E|X)002 keys' do
|
|
116
112
|
expect { subject.HPB }.to(change { subject.keys['SIZBN001.E002'] })
|
|
@@ -135,6 +131,47 @@ RSpec.describe Epics::Client do
|
|
|
135
131
|
it { expect(subject.keys['SIZBN001.E002'].public_digest).to eq(e_key.public_digest) }
|
|
136
132
|
it { expect(subject.keys['SIZBN001.X002'].public_digest).to eq(e_key.public_digest) }
|
|
137
133
|
end
|
|
134
|
+
|
|
135
|
+
describe 'with X.509 certificates (EBICS 3.0 / H005 format)' do
|
|
136
|
+
let(:bank_cert) { OpenSSL::X509::Certificate.new(File.read(File.join(File.dirname(__FILE__), 'fixtures', 'bank_x509.pem'))) }
|
|
137
|
+
|
|
138
|
+
before do
|
|
139
|
+
allow(subject).to receive(:download).with(Epics::HPB).and_return(File.read(File.join(File.dirname(__FILE__),
|
|
140
|
+
'fixtures', 'xml', 'hpb_response_x509.xml')))
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
it 'parses X002 bank authentication key from certificate' do
|
|
144
|
+
subject.HPB
|
|
145
|
+
expect(subject.bank_authentication_key).not_to be_nil
|
|
146
|
+
expect(subject.bank_authentication_key).to be_a(Epics::SignatureAlgorithm::RsaPkcs1)
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
it 'parses E002 bank encryption key from certificate' do
|
|
150
|
+
subject.HPB
|
|
151
|
+
expect(subject.bank_encryption_key).not_to be_nil
|
|
152
|
+
expect(subject.bank_encryption_key).to be_a(Epics::SignatureAlgorithm::RsaPkcs1)
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
it 'stores certificate in keyring for bank authentication' do
|
|
156
|
+
subject.HPB
|
|
157
|
+
expect(subject.keyring.bank_authentication.certificate).not_to be_nil
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
it 'stores certificate in keyring for bank encryption' do
|
|
161
|
+
subject.HPB
|
|
162
|
+
expect(subject.keyring.bank_encryption.certificate).not_to be_nil
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
it 'returns authentication and encryption keys' do
|
|
166
|
+
expect(subject.HPB).to match([be_a(Epics::SignatureAlgorithm::RsaPkcs1),
|
|
167
|
+
be_a(Epics::SignatureAlgorithm::RsaPkcs1)])
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
it 'parses public key from certificate correctly' do
|
|
171
|
+
subject.HPB
|
|
172
|
+
expect(subject.bank_authentication_key.key.to_pem).to eq(bank_cert.public_key.to_pem)
|
|
173
|
+
end
|
|
174
|
+
end
|
|
138
175
|
end
|
|
139
176
|
|
|
140
177
|
describe '#CDB' do
|
|
@@ -225,6 +262,73 @@ RSpec.describe Epics::Client do
|
|
|
225
262
|
end
|
|
226
263
|
end
|
|
227
264
|
|
|
265
|
+
describe '#HTD (H005)' do
|
|
266
|
+
let(:subject) do
|
|
267
|
+
Epics::Client.new(key, 'secret', 'https://zkbisotb.zkb.ch/ebicsweb/ebicsweb', 'ZKBISOTB', 'EBIX', 'EBICS',
|
|
268
|
+
version: Epics::Keyring::VERSION_30)
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
before do
|
|
272
|
+
allow(subject).to receive(:download).and_return(File.read(File.join(File.dirname(__FILE__), 'fixtures', 'xml',
|
|
273
|
+
'htd_order_data_h005.xml')))
|
|
274
|
+
end
|
|
275
|
+
|
|
276
|
+
it 'parses H005 Service elements and returns array of service hashes' do
|
|
277
|
+
subject.HTD
|
|
278
|
+
expect(subject.instance_variable_get('@order_types')).to match_array([
|
|
279
|
+
{ service_name: 'OTH', scope: 'BIL',
|
|
280
|
+
service_option: 'CH004TPE', container: 'ZIP', msg_name: 'msc' },
|
|
281
|
+
{ service_name: 'STM', scope: 'BIL',
|
|
282
|
+
service_option: 'CH004TPE', container: 'ZIP', msg_name: 'pain.001' },
|
|
283
|
+
{ service_name: 'SCT', scope: 'BIL',
|
|
284
|
+
service_option: 'CH004TPE', container: 'ZIP', msg_name: 'pain.008' },
|
|
285
|
+
{ service_name: 'CAMT', scope: 'BIL',
|
|
286
|
+
service_option: 'CH004TPE', container: 'ZIP', msg_name: 'camt.052' },
|
|
287
|
+
{ service_name: 'CAMT', scope: 'BIL',
|
|
288
|
+
service_option: 'CH004TPE', container: 'ZIP', msg_name: 'camt.053' }
|
|
289
|
+
])
|
|
290
|
+
end
|
|
291
|
+
end
|
|
292
|
+
|
|
293
|
+
describe '#HAA' do
|
|
294
|
+
before do
|
|
295
|
+
allow(subject).to receive(:download).and_return(File.read(File.join(File.dirname(__FILE__), 'fixtures', 'xml',
|
|
296
|
+
'haa_response_h004.xml')))
|
|
297
|
+
end
|
|
298
|
+
|
|
299
|
+
it 'returns order types as array of strings for H004' do
|
|
300
|
+
expect(subject.HAA).to match_array(%w[PTK HPD HTD STA HVD HPB HAA HVT HVU HVZ INI SPR PUB HIA HCA HSA HVE
|
|
301
|
+
HVS CCS CCT CIP CD1 CDB CDD])
|
|
302
|
+
end
|
|
303
|
+
end
|
|
304
|
+
|
|
305
|
+
describe '#HAA (H005)' do
|
|
306
|
+
let(:subject) do
|
|
307
|
+
Epics::Client.new(key, 'secret', 'https://zkbisotb.zkb.ch/ebicsweb/ebicsweb', 'ZKBISOTB', 'EBIX', 'EBICS',
|
|
308
|
+
version: Epics::Keyring::VERSION_30)
|
|
309
|
+
end
|
|
310
|
+
|
|
311
|
+
before do
|
|
312
|
+
allow(subject).to receive(:download).and_return(File.read(File.join(File.dirname(__FILE__), 'fixtures', 'xml',
|
|
313
|
+
'haa_response_h005.xml')))
|
|
314
|
+
end
|
|
315
|
+
|
|
316
|
+
it 'parses H005 Service elements and returns array of service hashes' do
|
|
317
|
+
expect(subject.HAA).to match_array([
|
|
318
|
+
{ service_name: 'OTH', scope: 'BIL', service_option: 'CH004TPE',
|
|
319
|
+
container: 'ZIP', msg_name: 'msc' },
|
|
320
|
+
{ service_name: 'STM', scope: 'BIL', service_option: 'CH004TPE',
|
|
321
|
+
container: 'ZIP', msg_name: 'pain.001' },
|
|
322
|
+
{ service_name: 'SCT', scope: 'BIL', service_option: 'CH004TPE',
|
|
323
|
+
container: 'ZIP', msg_name: 'pain.008' },
|
|
324
|
+
{ service_name: 'CAMT', scope: 'BIL', service_option: 'CH004TPE',
|
|
325
|
+
container: 'ZIP', msg_name: 'camt.052' },
|
|
326
|
+
{ service_name: 'CAMT', scope: 'BIL', service_option: 'CH004TPE',
|
|
327
|
+
container: 'ZIP', msg_name: 'camt.053' }
|
|
328
|
+
])
|
|
329
|
+
end
|
|
330
|
+
end
|
|
331
|
+
|
|
228
332
|
describe '#C53/C52/C54/Z52/Z53/Z54 types with zipped data' do
|
|
229
333
|
before do
|
|
230
334
|
allow(subject).to receive(:download).and_return(File.read(File.join(File.dirname(__FILE__), 'fixtures',
|
|
@@ -251,77 +355,25 @@ RSpec.describe Epics::Client do
|
|
|
251
355
|
end
|
|
252
356
|
end
|
|
253
357
|
|
|
254
|
-
describe '
|
|
255
|
-
|
|
256
|
-
Epics::Client.new(key, 'secret', 'https://194.180.18.30/ebicsweb/ebicsweb', 'SIZBN001', 'EBIX', 'EBICS'
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
it 'returns an instance of Epics::X509Certificate' do
|
|
261
|
-
epics_client.x_509_certificates_content = {
|
|
262
|
-
a: generate_x_509_crt(epics_client.a.key, '/C=GB/O=TestOrg/CN=test.example.org'),
|
|
263
|
-
x: generate_x_509_crt(epics_client.x.key, '/C=GB/O=TestOrg/CN=test.example.org'),
|
|
264
|
-
e: generate_x_509_crt(epics_client.e.key, '/C=GB/O=TestOrg/CN=test.example.org')
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
expect(epics_client.x_509_certificate(:a)).to be_a(Epics::X509Certificate)
|
|
268
|
-
expect(epics_client.x_509_certificate(:x)).to be_a(Epics::X509Certificate)
|
|
269
|
-
expect(epics_client.x_509_certificate(:e)).to be_a(Epics::X509Certificate)
|
|
270
|
-
end
|
|
271
|
-
end
|
|
272
|
-
|
|
273
|
-
context 'with nil or empty content' do
|
|
274
|
-
it 'returns nil when content is nil' do
|
|
275
|
-
epics_client.x_509_certificates_content = { a: nil, x: nil, e: nil }
|
|
276
|
-
expect(epics_client.x_509_certificate(:a)).to be_nil
|
|
277
|
-
expect(epics_client.x_509_certificate(:x)).to be_nil
|
|
278
|
-
expect(epics_client.x_509_certificate(:e)).to be_nil
|
|
279
|
-
end
|
|
280
|
-
|
|
281
|
-
it 'returns nil when content is empty string' do
|
|
282
|
-
epics_client.x_509_certificates_content = { a: '', x: '', e: '' }
|
|
283
|
-
expect(epics_client.x_509_certificate(:a)).to be_nil
|
|
284
|
-
expect(epics_client.x_509_certificate(:x)).to be_nil
|
|
285
|
-
expect(epics_client.x_509_certificate(:e)).to be_nil
|
|
286
|
-
end
|
|
287
|
-
end
|
|
288
|
-
end
|
|
358
|
+
describe 'x509 certificate assignment via options' do
|
|
359
|
+
it 'assigns certificates to signatures from constructor options' do
|
|
360
|
+
epics_client = Epics::Client.new(key, 'secret', 'https://194.180.18.30/ebicsweb/ebicsweb', 'SIZBN001', 'EBIX', 'EBICS',
|
|
361
|
+
x_509_certificate_a_content: generate_x_509_crt(OpenSSL::PKey::RSA.new(2048), '/C=GB/O=TestOrg/CN=test.example.org'),
|
|
362
|
+
x_509_certificate_x_content: generate_x_509_crt(OpenSSL::PKey::RSA.new(2048), '/C=GB/O=TestOrg/CN=test.example.org'),
|
|
363
|
+
x_509_certificate_e_content: generate_x_509_crt(OpenSSL::PKey::RSA.new(2048), '/C=GB/O=TestOrg/CN=test.example.org'))
|
|
289
364
|
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
end
|
|
294
|
-
context 'with valid certificates content' do
|
|
295
|
-
it 'returns SHA256 hash' do
|
|
296
|
-
epics_client.x_509_certificates_content = {
|
|
297
|
-
a: generate_x_509_crt(epics_client.a.key, '/C=GB/O=TestOrg/CN=test.example.org'),
|
|
298
|
-
x: generate_x_509_crt(epics_client.x.key, '/C=GB/O=TestOrg/CN=test.example.org'),
|
|
299
|
-
e: generate_x_509_crt(epics_client.e.key, '/C=GB/O=TestOrg/CN=test.example.org')
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
expect(epics_client.x_509_certificate_hash(:a)).to be_a(String)
|
|
303
|
-
expect(epics_client.x_509_certificate_hash(:x)).to be_a(String)
|
|
304
|
-
expect(epics_client.x_509_certificate_hash(:e)).to be_a(String)
|
|
305
|
-
expect(epics_client.x_509_certificate_hash(:a).size).to eq(64)
|
|
306
|
-
expect(epics_client.x_509_certificate_hash(:x).size).to eq(64)
|
|
307
|
-
expect(epics_client.x_509_certificate_hash(:e).size).to eq(64)
|
|
308
|
-
end
|
|
365
|
+
expect(epics_client.keyring.user_signature.certificate).to be_a(Epics::Crypt::X509)
|
|
366
|
+
expect(epics_client.keyring.user_authentication.certificate).to be_a(Epics::Crypt::X509)
|
|
367
|
+
expect(epics_client.keyring.user_encryption.certificate).to be_a(Epics::Crypt::X509)
|
|
309
368
|
end
|
|
310
369
|
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
expect(epics_client.x_509_certificate_hash(:a)).to be_nil
|
|
315
|
-
expect(epics_client.x_509_certificate_hash(:x)).to be_nil
|
|
316
|
-
expect(epics_client.x_509_certificate_hash(:e)).to be_nil
|
|
317
|
-
end
|
|
370
|
+
it 'leaves certificates nil when no options provided' do
|
|
371
|
+
epics_client = Epics::Client.new(key, 'secret', 'https://194.180.18.30/ebicsweb/ebicsweb', 'SIZBN001', 'EBIX',
|
|
372
|
+
'EBICS')
|
|
318
373
|
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
expect(epics_client.x_509_certificate_hash(:x)).to be_nil
|
|
323
|
-
expect(epics_client.x_509_certificate_hash(:e)).to be_nil
|
|
324
|
-
end
|
|
374
|
+
expect(epics_client.keyring.user_signature.certificate).to be_nil
|
|
375
|
+
expect(epics_client.keyring.user_authentication.certificate).to be_nil
|
|
376
|
+
expect(epics_client.keyring.user_encryption.certificate).to be_nil
|
|
325
377
|
end
|
|
326
378
|
end
|
|
327
379
|
end
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
RSpec.describe Epics::Services::CryptService do
|
|
2
|
+
subject(:service) { described_class.new }
|
|
3
|
+
|
|
4
|
+
let(:rsa_key) { OpenSSL::PKey::RSA.generate(2048) }
|
|
5
|
+
let(:rsa_algo) { Epics::SignatureAlgorithm::Rsa.new(rsa_key) }
|
|
6
|
+
|
|
7
|
+
describe '#encrypt' do
|
|
8
|
+
let(:data) { 'data to encrypt' }
|
|
9
|
+
let(:rsa_pss_algo) { Epics::SignatureAlgorithm::RsaPss.new(rsa_key) }
|
|
10
|
+
|
|
11
|
+
context 'with A006 (RSA-PSS sign)' do
|
|
12
|
+
let(:signature) { Epics::Signature.new(Epics::Signature::A_VERSION_6, rsa_pss_algo) }
|
|
13
|
+
|
|
14
|
+
it 'delegates to signature_algorithm#sign' do
|
|
15
|
+
expect(rsa_pss_algo).to receive(:sign).with(data).and_call_original
|
|
16
|
+
service.encrypt(signature, data)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
context 'with X002' do
|
|
21
|
+
let(:signature) { Epics::Signature.new(Epics::Signature::X_VERSION_2, rsa_pss_algo) }
|
|
22
|
+
|
|
23
|
+
it 'delegates to signature_algorithm#sign' do
|
|
24
|
+
expect(rsa_pss_algo).to receive(:sign).with(data).and_call_original
|
|
25
|
+
service.encrypt(signature, data)
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
context 'with A005 (RSA PKCS#1 private encrypt)' do
|
|
30
|
+
let(:signature) { Epics::Signature.new(Epics::Signature::A_VERSION_5, rsa_algo) }
|
|
31
|
+
|
|
32
|
+
it 'delegates to signature_algorithm#private_encrypt' do
|
|
33
|
+
expect(rsa_algo).to receive(:private_encrypt).with(data).and_call_original
|
|
34
|
+
service.encrypt(signature, data)
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
describe '#encrypt_by_key / #decrypt_by_key' do
|
|
40
|
+
let(:plaintext) { 'Hello EBICS world!' }
|
|
41
|
+
|
|
42
|
+
it 'round-trips: decrypt(encrypt(data)) == data' do
|
|
43
|
+
aes_key = OpenSSL::Cipher::AES.new(128, :CBC).random_key
|
|
44
|
+
encrypted = service.encrypt_by_key(aes_key, plaintext)
|
|
45
|
+
decrypted = service.decrypt_by_key(aes_key, encrypted)
|
|
46
|
+
expect(decrypted).to eq(plaintext)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
it 'produces ciphertext different from plaintext' do
|
|
50
|
+
aes_key = OpenSSL::Cipher::AES.new(128, :CBC).random_key
|
|
51
|
+
encrypted = service.encrypt_by_key(aes_key, plaintext)
|
|
52
|
+
expect(encrypted).not_to eq(plaintext)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
it 'produces different ciphertext with different keys' do
|
|
56
|
+
key1 = OpenSSL::Cipher::AES.new(128, :CBC).random_key
|
|
57
|
+
key2 = OpenSSL::Cipher::AES.new(128, :CBC).random_key
|
|
58
|
+
enc1 = service.encrypt_by_key(key1, plaintext)
|
|
59
|
+
enc2 = service.encrypt_by_key(key2, plaintext)
|
|
60
|
+
expect(enc1).not_to eq(enc2)
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
describe '#encrypt_transaction_key' do
|
|
65
|
+
it 'encrypts with RSA public key and is decryptable with private key' do
|
|
66
|
+
transaction_key = OpenSSL::Cipher::AES.new(128, :CBC).random_key
|
|
67
|
+
encrypted = service.encrypt_transaction_key(rsa_algo, transaction_key)
|
|
68
|
+
decrypted = rsa_key.private_decrypt(encrypted)
|
|
69
|
+
expect(decrypted).to eq(transaction_key)
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
describe '#hash' do
|
|
74
|
+
it 'returns SHA256 digest by default' do
|
|
75
|
+
result = service.hash('test')
|
|
76
|
+
expect(result).to eq(OpenSSL::Digest::SHA256.digest('test'))
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
it 'supports alternative algorithms' do
|
|
80
|
+
result = service.hash('test', 'sha512')
|
|
81
|
+
expect(result).to eq(OpenSSL::Digest::SHA512.digest('test'))
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
it 'returns binary digest' do
|
|
85
|
+
result = service.hash('test')
|
|
86
|
+
expect(result.encoding).to eq(Encoding::BINARY)
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
describe '#calculate_digest' do
|
|
91
|
+
it 'returns SHA256 hash of "exponent modulus" (hex, lowercase, no leading zeros)' do
|
|
92
|
+
exponent_hex = rsa_key.e.to_s(16).gsub(/^0*/, '').downcase
|
|
93
|
+
modulus_hex = rsa_key.n.to_s(16).gsub(/^0*/, '').downcase
|
|
94
|
+
expected = OpenSSL::Digest::SHA256.digest("#{exponent_hex} #{modulus_hex}")
|
|
95
|
+
|
|
96
|
+
result = service.calculate_digest(rsa_algo)
|
|
97
|
+
expect(result).to eq(expected)
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
it 'strips leading zeros from exponent and modulus' do
|
|
101
|
+
key_text = service.calculate_key('000ABC', '00DEF123')
|
|
102
|
+
expect(key_text).to eq('abc def123')
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
describe '#calculate_key' do
|
|
107
|
+
it 'concatenates exponent and modulus lowercase without leading zeros' do
|
|
108
|
+
expect(service.calculate_key('10001', 'ABCDEF')).to eq('10001 abcdef')
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
it 'strips leading zeros' do
|
|
112
|
+
expect(service.calculate_key('010001', '00ABCDEF')).to eq('10001 abcdef')
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
describe '#calculate_certificate_fingerprint' do
|
|
117
|
+
let(:dn) { '/C=DE/O=TestBank/CN=test.ebics.org' }
|
|
118
|
+
let(:cert_pem) { generate_x_509_crt(rsa_key, dn) }
|
|
119
|
+
let(:x509) { Epics::Crypt::X509.new(cert_pem) }
|
|
120
|
+
|
|
121
|
+
it 'returns SHA256 hash of DER-encoded certificate' do
|
|
122
|
+
expected = OpenSSL::Digest::SHA256.digest(x509.to_der)
|
|
123
|
+
result = service.calculate_certificate_fingerprint(x509)
|
|
124
|
+
expect(result).to eq(expected)
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
it 'returns binary digest' do
|
|
128
|
+
result = service.calculate_certificate_fingerprint(x509)
|
|
129
|
+
expect(result.encoding).to eq(Encoding::BINARY)
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
end
|