as2 0.9.0 → 0.11.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 +4 -4
- data/CHANGELOG.md +17 -0
- data/lib/as2/client.rb +17 -6
- data/lib/as2/config.rb +38 -5
- data/lib/as2/message.rb +9 -9
- data/lib/as2/server.rb +2 -2
- data/lib/as2/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7a467f304fd7955e9f787078b8bbda6277e111e84e75d2cc02c935d6ccc00916
|
4
|
+
data.tar.gz: b75996e0b5064d1b4133f9ed8db0b9f629cb44746ade335edcd3a2fa04de0edf
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b8b50c0291eed98d9f74333e094bf1a59cf4bedb987592082dddc04118e319dd85121f8434391fb473783320b6388ec836d0fe54901b02b8c3cdf70f95eb93b6
|
7
|
+
data.tar.gz: db4e30dba47bd613b3b9b5e0e47d33951e527a31249af79ce7f2073c66035cc9a3aa3a8ba5a88c0163b4e06eec814d6ac2bf48ee060935cc22ec57762bdd2b10
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,20 @@
|
|
1
|
+
## 0.11.0 September 14, 2023
|
2
|
+
|
3
|
+
* Allow configuration of which encryption cipher to use when sending outbound messages. [#35](https://github.com/alexdean/as2/pull/35)
|
4
|
+
|
5
|
+
## 0.10.0 September 13, 2023
|
6
|
+
|
7
|
+
support for separate signing & encryption certificates for partners. [#34](https://github.com/alexdean/as2/pull/34)
|
8
|
+
|
9
|
+
BREAKING CHANGES:
|
10
|
+
|
11
|
+
* `As2::Config::Partner`
|
12
|
+
* Added `signing_certificate` and `encryption_certificate`
|
13
|
+
* Removed `certificate`.
|
14
|
+
* `certificate=` is still supported, and assigns the same certificate to both.
|
15
|
+
* `As2::Client#parse_signed_mdn`: requires `signing_certificate:` rather than `certificate:`.
|
16
|
+
* `As2::Message.verify`: requires `signing_certificate:` rather than `certificate:`.
|
17
|
+
|
1
18
|
## 0.9.0, August 28, 2023
|
2
19
|
|
3
20
|
* Bugfix for quoting AS2-From/AS2-To identifiers
|
data/lib/as2/client.rb
CHANGED
@@ -8,6 +8,10 @@ module As2
|
|
8
8
|
['v0', 'v1']
|
9
9
|
end
|
10
10
|
|
11
|
+
def self.valid_encryption_ciphers
|
12
|
+
OpenSSL::Cipher.ciphers
|
13
|
+
end
|
14
|
+
|
11
15
|
# @param [As2::Config::Partner,String] partner The partner to send a message to.
|
12
16
|
# If a string is given, it should be a partner name which has been registered
|
13
17
|
# via a call to #add_partner.
|
@@ -45,6 +49,10 @@ module As2
|
|
45
49
|
# * If content parameter is specified, file_name is only used to tell the
|
46
50
|
# partner the original name of the file.
|
47
51
|
#
|
52
|
+
# TODO: refactor to separate "build an outbound message" from "send an outbound message"
|
53
|
+
# main benefit would be allowing the test suite to be more straightforward.
|
54
|
+
# (wouldn't need webmock just to verify what kind of message we built...)
|
55
|
+
#
|
48
56
|
# @param [String] file_name
|
49
57
|
# @param [String] content
|
50
58
|
# @param [String] content_type This is the MIME Content-Type describing the `content` param,
|
@@ -82,8 +90,11 @@ module As2
|
|
82
90
|
file_name: file_name
|
83
91
|
)
|
84
92
|
|
85
|
-
|
86
|
-
|
93
|
+
encrypted = OpenSSL::PKCS7.encrypt(
|
94
|
+
[@partner.encryption_certificate],
|
95
|
+
request_body,
|
96
|
+
@partner.encryption_cipher_instance
|
97
|
+
)
|
87
98
|
|
88
99
|
# > HTTP can handle binary data and so there is no need to use the
|
89
100
|
# > content transfer encodings of MIME
|
@@ -257,7 +268,7 @@ module As2
|
|
257
268
|
if mdn_content_type.start_with?('multipart/signed')
|
258
269
|
result = parse_signed_mdn(
|
259
270
|
multipart_signed_message: response_content,
|
260
|
-
|
271
|
+
signing_certificate: @partner.signing_certificate
|
261
272
|
)
|
262
273
|
mdn_report = result[:mdn_report]
|
263
274
|
report[:signature_verification_error] = result[:signature_verification_error]
|
@@ -314,7 +325,7 @@ module As2
|
|
314
325
|
# * :mdn_mime_body [Mail::Message] The 'inner' MDN body, with signature removed
|
315
326
|
# * :signature_verification_error [String] Any error which resulted when checking the
|
316
327
|
# signature. If this is empty it means the signature was valid.
|
317
|
-
def parse_signed_mdn(multipart_signed_message:,
|
328
|
+
def parse_signed_mdn(multipart_signed_message:, signing_certificate:)
|
318
329
|
smime = nil
|
319
330
|
|
320
331
|
begin
|
@@ -347,7 +358,7 @@ module As2
|
|
347
358
|
# based on As2::Message version
|
348
359
|
# TODO: test cases based on valid/invalid responses. (response signed with wrong certificate, etc.)
|
349
360
|
# See notes in As2::Message.verify for reasoning on flag usage
|
350
|
-
smime.verify [
|
361
|
+
smime.verify [signing_certificate], OpenSSL::X509::Store.new, nil, OpenSSL::PKCS7::NOVERIFY | OpenSSL::PKCS7::NOINTERN
|
351
362
|
|
352
363
|
signature_verification_error = smime.error_string
|
353
364
|
else
|
@@ -383,7 +394,7 @@ module As2
|
|
383
394
|
result = As2::Message.verify(
|
384
395
|
content: content,
|
385
396
|
signature_text: signature_text,
|
386
|
-
|
397
|
+
signing_certificate: signing_certificate
|
387
398
|
)
|
388
399
|
|
389
400
|
signature_verification_error = result[:error]
|
data/lib/as2/config.rb
CHANGED
@@ -12,7 +12,12 @@ module As2
|
|
12
12
|
end
|
13
13
|
end
|
14
14
|
|
15
|
-
class Partner < Struct.new :name, :url, :
|
15
|
+
class Partner < Struct.new :name, :url, :encryption_certificate, :encryption_cipher, :signing_certificate, :tls_verify_mode, :mdn_format, :outbound_format
|
16
|
+
def initialize
|
17
|
+
# set default.
|
18
|
+
self.encryption_cipher = 'aes-256-cbc'
|
19
|
+
end
|
20
|
+
|
16
21
|
def url=(url)
|
17
22
|
if url.kind_of? String
|
18
23
|
self['url'] = URI.parse url
|
@@ -40,7 +45,30 @@ module As2
|
|
40
45
|
end
|
41
46
|
|
42
47
|
def certificate=(certificate)
|
43
|
-
|
48
|
+
cert = As2::Config.build_certificate(certificate)
|
49
|
+
self['encryption_certificate'] = cert
|
50
|
+
self['signing_certificate'] = cert
|
51
|
+
end
|
52
|
+
|
53
|
+
def encryption_certificate=(certificate)
|
54
|
+
self['encryption_certificate'] = As2::Config.build_certificate(certificate)
|
55
|
+
end
|
56
|
+
|
57
|
+
def encryption_cipher=(cipher)
|
58
|
+
cipher_s = cipher.to_s
|
59
|
+
valid_ciphers = As2::Client.valid_encryption_ciphers
|
60
|
+
if !valid_ciphers.include?(cipher_s)
|
61
|
+
raise ArgumentError, "encryption_cipher '#{cipher_s}' must be one of #{valid_ciphers.inspect}"
|
62
|
+
end
|
63
|
+
self['encryption_cipher'] = cipher_s
|
64
|
+
end
|
65
|
+
|
66
|
+
def encryption_cipher_instance
|
67
|
+
OpenSSL::Cipher.new(encryption_cipher)
|
68
|
+
end
|
69
|
+
|
70
|
+
def signing_certificate=(certificate)
|
71
|
+
self['signing_certificate'] = As2::Config.build_certificate(certificate)
|
44
72
|
end
|
45
73
|
|
46
74
|
# if set, will be used for SSL transmissions.
|
@@ -87,14 +115,18 @@ module As2
|
|
87
115
|
unless partner.name
|
88
116
|
raise 'Partner name is required'
|
89
117
|
end
|
90
|
-
unless partner.
|
91
|
-
raise 'Partner certificate is required'
|
118
|
+
unless partner.signing_certificate
|
119
|
+
raise 'Partner signing certificate is required'
|
120
|
+
end
|
121
|
+
unless partner.encryption_certificate
|
122
|
+
raise 'Partner encryption certificate is required'
|
92
123
|
end
|
93
124
|
unless partner.url
|
94
125
|
raise 'Partner URL is required'
|
95
126
|
end
|
96
127
|
Config.partners[partner.name] = partner
|
97
|
-
Config.store.add_cert partner.
|
128
|
+
Config.store.add_cert partner.signing_certificate
|
129
|
+
Config.store.add_cert partner.encryption_certificate
|
98
130
|
end
|
99
131
|
end
|
100
132
|
|
@@ -123,6 +155,7 @@ module As2
|
|
123
155
|
@partners ||= {}
|
124
156
|
end
|
125
157
|
|
158
|
+
# TODO: deprecate this.
|
126
159
|
def store
|
127
160
|
@store ||= OpenSSL::X509::Store.new
|
128
161
|
end
|
data/lib/as2/message.rb
CHANGED
@@ -53,7 +53,7 @@ module As2
|
|
53
53
|
# * :valid [boolean] was the verification successful or not?
|
54
54
|
# * :error [String, nil] a verification error message.
|
55
55
|
# will be empty when `valid` is true.
|
56
|
-
def self.verify(content:, signature_text:,
|
56
|
+
def self.verify(content:, signature_text:, signing_certificate:)
|
57
57
|
begin
|
58
58
|
signature = OpenSSL::PKCS7.new(signature_text)
|
59
59
|
|
@@ -76,9 +76,9 @@ module As2
|
|
76
76
|
#
|
77
77
|
# https://www.openssl.org/docs/manmaster/man3/PKCS7_verify.html
|
78
78
|
#
|
79
|
-
# we want this so we can be sure that the `
|
79
|
+
# we want this so we can be sure that the `signing_certificate` we supply
|
80
80
|
# was actually used to sign the message. otherwise we could get a positive
|
81
|
-
# verification even if `
|
81
|
+
# verification even if `signing_certificate` didn't sign the message
|
82
82
|
# we're checking.
|
83
83
|
#
|
84
84
|
# ## NOVERIFY
|
@@ -87,9 +87,9 @@ module As2
|
|
87
87
|
#
|
88
88
|
# ie: we won't attempt to connect signer (in the first param) to a root
|
89
89
|
# CA (in `store`, which is empty). alternately, we could instead remove
|
90
|
-
# this flag, and add `
|
91
|
-
# we'd only be verifying that `
|
92
|
-
valid = signature.verify([
|
90
|
+
# this flag, and add `signing_certificate` to `store`. but what's the point?
|
91
|
+
# we'd only be verifying that `signing_certificate` is connected to `signing_certificate`.
|
92
|
+
valid = signature.verify([signing_certificate], store, content, OpenSSL::PKCS7::NOVERIFY | OpenSSL::PKCS7::NOINTERN)
|
93
93
|
|
94
94
|
# when `signature.verify` fails, signature.error_string will be populated.
|
95
95
|
error = signature.error_string
|
@@ -121,7 +121,7 @@ module As2
|
|
121
121
|
@decrypted_message ||= @pkcs7.decrypt @private_key, @public_certificate
|
122
122
|
end
|
123
123
|
|
124
|
-
def valid_signature?(
|
124
|
+
def valid_signature?(partner_signing_certificate)
|
125
125
|
content_type = mail.header_fields.find { |h| h.name == 'Content-Type' }.content_type
|
126
126
|
# TODO: substantial overlap between this code & the fallback/rescue code in
|
127
127
|
# As2::Client#verify_mdn_signature
|
@@ -149,7 +149,7 @@ module As2
|
|
149
149
|
result = self.class.verify(
|
150
150
|
content: content,
|
151
151
|
signature_text: signature_text,
|
152
|
-
|
152
|
+
signing_certificate: partner_signing_certificate
|
153
153
|
)
|
154
154
|
|
155
155
|
output = result[:valid]
|
@@ -186,7 +186,7 @@ module As2
|
|
186
186
|
retry_output = self.class.verify(
|
187
187
|
content: content,
|
188
188
|
signature_text: signature_text,
|
189
|
-
|
189
|
+
signing_certificate: partner_signing_certificate
|
190
190
|
)
|
191
191
|
|
192
192
|
if retry_output[:valid]
|
data/lib/as2/server.rb
CHANGED
@@ -40,7 +40,7 @@ module As2
|
|
40
40
|
request = Rack::Request.new(env)
|
41
41
|
message = Message.new(request.body.read, @server_info.pkey, @server_info.certificate)
|
42
42
|
|
43
|
-
unless message.valid_signature?(partner.
|
43
|
+
unless message.valid_signature?(partner.signing_certificate)
|
44
44
|
if @signature_failure_handler
|
45
45
|
@signature_failure_handler.call({
|
46
46
|
env: env,
|
@@ -48,7 +48,7 @@ module As2
|
|
48
48
|
verification_error: message.verification_error
|
49
49
|
})
|
50
50
|
else
|
51
|
-
raise "Could not verify signature"
|
51
|
+
raise "Could not verify signature. #{message.verification_error}"
|
52
52
|
end
|
53
53
|
end
|
54
54
|
|
data/lib/as2/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: as2
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.11.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- OfficeLuv
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: exe
|
11
11
|
cert_chain: []
|
12
|
-
date: 2023-
|
12
|
+
date: 2023-09-14 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: mail
|