as2 0.11.0 → 0.12.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: 7a467f304fd7955e9f787078b8bbda6277e111e84e75d2cc02c935d6ccc00916
4
- data.tar.gz: b75996e0b5064d1b4133f9ed8db0b9f629cb44746ade335edcd3a2fa04de0edf
3
+ metadata.gz: c823318d35a8e64297b050461d423414e07951268caa8fa1565fb5df4068b34f
4
+ data.tar.gz: 23322af75cf03f24e6a5df4d6d9b17e54ebdacd5a6569859a399cd1629f74735
5
5
  SHA512:
6
- metadata.gz: b8b50c0291eed98d9f74333e094bf1a59cf4bedb987592082dddc04118e319dd85121f8434391fb473783320b6388ec836d0fe54901b02b8c3cdf70f95eb93b6
7
- data.tar.gz: db4e30dba47bd613b3b9b5e0e47d33951e527a31249af79ce7f2073c66035cc9a3aa3a8ba5a88c0163b4e06eec814d6ac2bf48ee060935cc22ec57762bdd2b10
6
+ metadata.gz: a81d120d47bb35d6be109ea7dc873757b3e6f54b4236383a52234d57509cd9ad205beecf0e07faf7965ea5d1a5182da019868e4202ef2eac3c4aee133800b517
7
+ data.tar.gz: 4367841d4bcb9f3376c3ba963a247b0e9036e5e6dae011d2819bd646f4864e9d4ffa4cd3da0decb8f7104913e70ef185ee050fd004b96063b95894a36710466f
data/CHANGELOG.md CHANGED
@@ -1,3 +1,8 @@
1
+ ## 0.12.0 February 16, 2024
2
+
3
+ Allow configuration of which base64 encoding scheme to apply to outbound message bodies.
4
+ Improves compatibility with some versions of MuleSoft. [#36](https://github.com/alexdean/as2/pull/36)
5
+
1
6
  ## 0.11.0 September 14, 2023
2
7
 
3
8
  * Allow configuration of which encryption cipher to use when sending outbound messages. [#35](https://github.com/alexdean/as2/pull/35)
data/README.md CHANGED
@@ -14,23 +14,25 @@ and with [OpenAS2](https://github.com/OpenAS2/OpenAs2App).
14
14
  These limitations may be removed over time as demand (and pull requests!) come
15
15
  along.
16
16
 
17
- 1. RFC defines a number of optional features that partners can pick and choose
18
- amongst. We currently have hard-coded options for many of these. Our current
19
- choices are likely the most common ones in use, but we do not offer all the
20
- configuration options needed for a fully-compliant implementation. https://datatracker.ietf.org/doc/html/rfc4130#section-2.4.2
21
- 1. Encrypted or Unencrypted Data: We assume all messages are encrypted. An
22
- error will result if partner sends us an unencrypted message.
23
- 2. Signed or Unsigned Data: We error if partner sends an unsigned message.
24
- Partners can request unsigned MDNs, but we always send signed MDNs.
25
- 3. Optional Use of Receipt: We always send a receipt.
26
- 4. Use of Synchronous or Asynchronous Receipts: We do not support asynchronous
27
- delivery of MDNs.
28
- 5. Security Formatting: We should be reasonably compliant here.
29
- 6. Hash Function, Message Digest Choices: We currently always use sha256 for
30
- signing. Since [#20](https://github.com/alexdean/as2/pull/20) we have supported
31
- allowing partners to request which algorithm we use for MIC generation in MDNs.
32
- 2. AS2 partners may agree to use separate certificates for data encryption and data signing.
33
- We do not support separate certificates for these purposes.
17
+ RFC defines a number of optional features that partners can pick and choose
18
+ amongst. We currently have hard-coded options for many of these. Our current
19
+ choices are likely the most common ones in use, but we do not offer all the
20
+ configuration options needed for a fully-compliant implementation.
21
+
22
+ https://datatracker.ietf.org/doc/html/rfc4130#section-2.4.2
23
+
24
+
25
+ 1. Encrypted or Unencrypted Data: We assume all messages are encrypted. An
26
+ error will result if partner sends us an unencrypted message.
27
+ 2. Signed or Unsigned Data: We error if partner sends an unsigned message.
28
+ Partners can request unsigned MDNs, but we always send signed MDNs.
29
+ 3. Optional Use of Receipt: We always send a receipt.
30
+ 4. Use of Synchronous or Asynchronous Receipts: We do not support asynchronous
31
+ delivery of MDNs.
32
+ 5. Security Formatting: We should be reasonably compliant here.
33
+ 6. Hash Function, Message Digest Choices: We currently always use sha256 for
34
+ signing. Since [#20](https://github.com/alexdean/as2/pull/20) we have supported
35
+ allowing partners to request which algorithm we use for MIC generation in MDNs.
34
36
 
35
37
  ## Installation
36
38
 
@@ -48,6 +50,87 @@ Or install it yourself as:
48
50
 
49
51
  $ gem install as2
50
52
 
53
+ ## Configuration
54
+
55
+ Configuration objects need to be initialized for the local system and once for each partner.
56
+
57
+ See scripts in `examples` directory for more usage info.
58
+
59
+ A a certificate can be specified as either:
60
+
61
+ * a string path to a file containing a PEM-encoded X509 certificate
62
+ * or an instance of `OpenSSL::X509::Certificate`
63
+
64
+ A private key can be specified as either:
65
+
66
+ * a string path to a file containing a PEM-encoded private key
67
+ * or an instance of `OpenSSL::PKey::PKey`
68
+
69
+ ### Local System
70
+
71
+ Supported options:
72
+
73
+ * `name`: AS2 id for the local system. (Used as `As2-From` in outbound messages.)
74
+ * `url`: URL of this system. Mainly for informational purposes.
75
+ * `domain`: DNS domain name of this system. Mainly for informational purposes.
76
+ * `certificate`: Certificate used for signing outbound messages.
77
+ * `pkey`: Private key used for decrypting incoming messages.
78
+
79
+ ### Partners
80
+
81
+ Supported options:
82
+
83
+ * `name`: AS2 id for this partner. (Used as `As2-To` in outbound messages.)
84
+ * `url`: URL to POST outbound messages to.
85
+ * `certificate`: Certificate to use for both encryption and signature verification.
86
+ * If this is specified, it will be used for both `encryption_certificate` and `signing_certificate`.
87
+ * `encryption_certificate`: Certificate to use for encrypting outbound messages.
88
+ * Only required if `certificate` is not set.
89
+ * `signing_certificate`: Certificate to use when verifying signatures on incoming messages.
90
+ * Only required if `certificate` is not set.
91
+ * `encryption_cipher`: Cipher to use when encrypting outbound messages. A default value is used if this is not specified.
92
+ * Call `As2::Client.valid_encryption_ciphers` for valid options.
93
+ * `tls_verify_mode`: Optional. Set to `OpenSSL::SSL::VERIFY_NONE` if partner is using a self-signed certificate for HTTPS.
94
+ * `mdn_format`: Format to use when building MDNs to send to partners.
95
+ * `v0`: older/original format which is less compatible with other AS2 systems, but is the default for backwards-compatibility reasons.
96
+ * `v1`: improved format with better compatibility with other AS2 systems.
97
+ * `outbound_format`: Format to use when building outbound messages.
98
+ * `v0`: older/original format which is less compatible with other AS2 systems, but is the default for backwards-compatibility reasons.
99
+ * `v1`: improved format with better compatibility with other AS2 systems.
100
+ * `base64_scheme`: What type of base64 encoding to perform on outbound message bodies.
101
+ * `rfc4648`: older/original format which is less compatible with other AS2 systems, but is the default for backwards-compatibility reasons.
102
+ * `rfc2045`: format understood by more AS2 systems & recommended for new integrations.
103
+
104
+ ### Example
105
+
106
+ ```ruby
107
+ As2.configure do |conf|
108
+ conf.name = 'RUBYAS2'
109
+ conf.url = 'http://localhost:3000/as2'
110
+ conf.certificate = 'test/certificates/server.crt'
111
+ conf.pkey = 'test/certificates/server.key'
112
+ conf.domain = 'localhost'
113
+
114
+ conf.add_partner do |partner|
115
+ partner.name = 'MENDELSON'
116
+ partner.url = 'http://localhost:8080/as2/HttpReceiver'
117
+ partner.certificate = 'test/certificates/client.crt'
118
+ partner.outbound_format = 'v1'
119
+ partner.mdn_format = 'v1'
120
+ partner.base64_scheme = 'rfc2045'
121
+ end
122
+
123
+ conf.add_partner do |partner|
124
+ partner.name = 'OPENAS2'
125
+ partner.url = 'http://localhost:4088'
126
+ partner.certificate = 'test/certificates/client.crt'
127
+ partner.outbound_format = 'v1'
128
+ partner.mdn_format = 'v1'
129
+ partner.base64_scheme = 'rfc2045'
130
+ end
131
+ end
132
+ ```
133
+
51
134
  ## Usage
52
135
 
53
136
  Generate self signed server certificate:
data/examples/server.rb CHANGED
@@ -21,12 +21,18 @@ As2.configure do |conf|
21
21
  partner.name = 'MENDELSON'
22
22
  partner.url = 'http://localhost:8080/as2/HttpReceiver'
23
23
  partner.certificate = 'test/certificates/client.crt'
24
+ partner.outbound_format = 'v1'
25
+ partner.mdn_format = 'v1'
26
+ partner.base64_scheme = 'rfc2045'
24
27
  end
25
28
 
26
29
  conf.add_partner do |partner|
27
30
  partner.name = 'OPENAS2'
28
31
  partner.url = 'http://localhost:4088'
29
32
  partner.certificate = 'test/certificates/client.crt'
33
+ partner.outbound_format = 'v1'
34
+ partner.mdn_format = 'v1'
35
+ partner.base64_scheme = 'rfc2045'
30
36
  end
31
37
  end
32
38
 
data/lib/as2/client.rb CHANGED
@@ -172,7 +172,7 @@ module As2
172
172
  document_payload << "Content-Transfer-Encoding: base64\r\n"
173
173
  document_payload << "Content-Disposition: attachment; filename=#{file_name}\r\n"
174
174
  document_payload << "\r\n"
175
- document_payload << Base64.strict_encode64(document_content)
175
+ document_payload << base64_encode(document_content)
176
176
 
177
177
  signature = OpenSSL::PKCS7.sign(@server_info.certificate, @server_info.pkey, document_payload)
178
178
  signature.detached = true
@@ -201,7 +201,7 @@ module As2
201
201
  document_payload << "Content-Transfer-Encoding: base64\r\n"
202
202
  document_payload << "Content-Disposition: attachment; filename=#{file_name}\r\n"
203
203
  document_payload << "\r\n"
204
- document_payload << Base64.strict_encode64(document_content)
204
+ document_payload << base64_encode(document_content)
205
205
 
206
206
  signature = OpenSSL::PKCS7.sign(@server_info.certificate, @server_info.pkey, document_payload)
207
207
  signature.detached = true
@@ -211,7 +211,7 @@ module As2
211
211
  # strip off the '-----BEGIN PKCS7-----' / '-----END PKCS7-----' delimiters
212
212
  bare_pem_signature.gsub!(/^-----[^\n]+\n/, '')
213
213
  # and update to canonical \r\n line endings
214
- bare_pem_signature.gsub!(/(?<!\r)\n/, "\r\n")
214
+ bare_pem_signature = As2.canonicalize_line_endings(bare_pem_signature)
215
215
 
216
216
  # this is a hack until i can determine a better way to get the micalg parameter
217
217
  # from the pkcs7 signature generated above...
@@ -315,6 +315,11 @@ module As2
315
315
 
316
316
  private
317
317
 
318
+ def base64_encode(content)
319
+ encoded = As2.base64_encode(content, scheme: @partner.base64_scheme)
320
+ As2.canonicalize_line_endings(encoded)
321
+ end
322
+
318
323
  # extract the MDN body from a multipart/signed wrapper & attempt to verify
319
324
  # the signature
320
325
  #
data/lib/as2/config.rb CHANGED
@@ -12,10 +12,20 @@ module As2
12
12
  end
13
13
  end
14
14
 
15
- class Partner < Struct.new :name, :url, :encryption_certificate, :encryption_cipher, :signing_certificate, :tls_verify_mode, :mdn_format, :outbound_format
15
+ class Partner < Struct.new :name, :url, :encryption_certificate, :encryption_cipher, :signing_certificate, :tls_verify_mode, :mdn_format, :outbound_format, :base64_scheme
16
16
  def initialize
17
17
  # set default.
18
18
  self.encryption_cipher = 'aes-256-cbc'
19
+ self.base64_scheme = 'rfc4648'
20
+ end
21
+
22
+ def base64_scheme=(scheme)
23
+ scheme_s = scheme.to_s
24
+ valid_schemes = As2.valid_base64_schemes
25
+ if !valid_schemes.include?(scheme_s)
26
+ raise ArgumentError, "base64_scheme '#{scheme_s}' must be one of #{valid_schemes.inspect}"
27
+ end
28
+ self['base64_scheme'] = scheme_s
19
29
  end
20
30
 
21
31
  def url=(url)
data/lib/as2/server.rb CHANGED
@@ -165,7 +165,7 @@ module As2
165
165
  # strip off the '-----BEGIN PKCS7-----' / '-----END PKCS7-----' delimiters
166
166
  bare_pem_signature.gsub!(/^-----[^\n]+\n/, '')
167
167
  # and update to canonical \r\n line endings
168
- bare_pem_signature.gsub!(/(?<!\r)\n/, "\r\n")
168
+ bare_pem_signature = As2.canonicalize_line_endings(bare_pem_signature)
169
169
 
170
170
  # this is a hack until i can determine a better way to get the micalg parameter
171
171
  # from the pkcs7 signature generated above...
data/lib/as2/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module As2
2
- VERSION = "0.11.0"
2
+ VERSION = "0.12.0"
3
3
  end
data/lib/as2.rb CHANGED
@@ -23,6 +23,54 @@ module As2
23
23
  "<#{server_info.name}-#{Time.now.strftime('%Y%m%d-%H%M%S')}-#{SecureRandom.uuid}@#{server_info.domain}>"
24
24
  end
25
25
 
26
+ def self.valid_base64_schemes
27
+ [
28
+ 'rfc2045',
29
+ 'rfc4648'
30
+ ]
31
+ end
32
+
33
+ # create a base64 string from content, based on the given encoding scheme
34
+ #
35
+ # @param [String] content
36
+ # @param [String] scheme one of As2.valid_base64_schemes
37
+ # @return [String]
38
+ def self.base64_encode(content, scheme: 'rfc4648')
39
+ case scheme.to_s
40
+ when 'rfc2045'
41
+ # "This method complies with RFC 2045."
42
+ # https://ruby-doc.org/stdlib-3.0.4/libdoc/base64/rdoc/Base64.html#method-i-encode64
43
+ # https://www.rfc-editor.org/rfc/rfc2045#section-6.8
44
+ then Base64.encode64(content)
45
+ when 'rfc4648'
46
+ # "This method complies with RFC 4648."
47
+ # https://ruby-doc.org/stdlib-3.0.4/libdoc/base64/rdoc/Base64.html#method-i-strict_encode64
48
+ # https://www.rfc-editor.org/rfc/rfc4648#section-4
49
+ then Base64.strict_encode64(content)
50
+ else
51
+ raise ArgumentError, "unsupported scheme '#{scheme}'. choose one of: #{valid_base64_schemes}"
52
+ end
53
+ end
54
+
55
+ # canonicalize all line endings in the given text.
56
+ #
57
+ # "\n" becomes "\r\n"
58
+ # "\r\n" remains "\r\n"
59
+ #
60
+ # Conversion to canonical form:
61
+ # The entire body ... is converted to a universal canonical
62
+ # form. ... For example, in the case of text/plain data, the text
63
+ # must be converted to a supported character set and lines must
64
+ # be delimited with CRLF delimiters in accordance with RFC 822.
65
+ #
66
+ # https://www.rfc-editor.org/rfc/rfc2049#page-9
67
+ #
68
+ # @param [String] content
69
+ # @return [String] content, but with all bare \n replaced by \r\n
70
+ def self.canonicalize_line_endings(content)
71
+ content.gsub(/(?<!\r)\n/, "\r\n")
72
+ end
73
+
26
74
  # Select which algorithm to use for calculating a MIC, based on preferences
27
75
  # stated by sender & our list of available algorithms.
28
76
  #
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.11.0
4
+ version: 0.12.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-09-14 00:00:00.000000000 Z
12
+ date: 2024-02-16 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: mail
@@ -205,7 +205,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
205
205
  - !ruby/object:Gem::Version
206
206
  version: '0'
207
207
  requirements: []
208
- rubygems_version: 3.1.4
208
+ rubygems_version: 3.4.10
209
209
  signing_key:
210
210
  specification_version: 4
211
211
  summary: Simple AS2 server and client implementation