xml-kit 0.1.14 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +23 -5
  3. data/.travis.yml +7 -5
  4. data/CHANGELOG.md +60 -0
  5. data/README.md +14 -22
  6. data/bin/cibuild +1 -1
  7. data/lib/xml/kit.rb +11 -2
  8. data/lib/xml/kit/certificate.rb +6 -4
  9. data/lib/xml/kit/crypto.rb +14 -0
  10. data/lib/xml/kit/crypto/oaep_cipher.rb +5 -2
  11. data/lib/xml/kit/crypto/rsa_cipher.rb +4 -2
  12. data/lib/xml/kit/crypto/symmetric_cipher.rb +30 -9
  13. data/lib/xml/kit/crypto/unknown_cipher.rb +6 -1
  14. data/lib/xml/kit/decryption.rb +29 -20
  15. data/lib/xml/kit/document.rb +5 -4
  16. data/lib/xml/kit/encrypted_data.rb +51 -0
  17. data/lib/xml/kit/encrypted_key.rb +35 -0
  18. data/lib/xml/kit/encryption.rb +27 -18
  19. data/lib/xml/kit/fingerprint.rb +1 -1
  20. data/lib/xml/kit/key_info.rb +71 -0
  21. data/lib/xml/kit/key_info/key_value.rb +19 -0
  22. data/lib/xml/kit/key_info/retrieval_method.rb +19 -0
  23. data/lib/xml/kit/key_info/rsa_key_value.rb +15 -0
  24. data/lib/xml/kit/key_pair.rb +8 -3
  25. data/lib/xml/kit/namespaces.rb +12 -12
  26. data/lib/xml/kit/self_signed_certificate.rb +16 -3
  27. data/lib/xml/kit/signature.rb +9 -2
  28. data/lib/xml/kit/signatures.rb +4 -1
  29. data/lib/xml/kit/templatable.rb +75 -24
  30. data/lib/xml/kit/templates/certificate.builder +1 -5
  31. data/lib/xml/kit/templates/encrypted_data.builder +9 -0
  32. data/lib/xml/kit/templates/encrypted_key.builder +9 -0
  33. data/lib/xml/kit/templates/key_info.builder +14 -0
  34. data/lib/xml/kit/templates/key_value.builder +5 -0
  35. data/lib/xml/kit/templates/retrieval_method.builder +3 -0
  36. data/lib/xml/kit/templates/rsa_key_value.builder +6 -0
  37. data/lib/xml/kit/templates/signature.builder +1 -1
  38. data/lib/xml/kit/version.rb +1 -1
  39. data/xml-kit.gemspec +4 -4
  40. metadata +29 -18
  41. data/.rubocop_todo.yml +0 -22
  42. data/lib/xml/kit/templates/encryption.builder +0 -16
@@ -3,9 +3,11 @@
3
3
  module Xml
4
4
  module Kit
5
5
  class SelfSignedCertificate
6
- SUBJECT = '/C=CA/ST=AB/L=Calgary/O=XmlKit/OU=XmlKit/CN=XmlKit'.freeze
6
+ SUBJECT = '/C=CA/ST=AB/L=Calgary/O=XmlKit/OU=XmlKit/CN=XmlKit'
7
7
 
8
- def create(algorithm: 'AES-256-CBC', passphrase: nil, key_pair: OpenSSL::PKey::RSA.new(2048))
8
+ def create(algorithm: 'AES-256-CBC',
9
+ passphrase: nil,
10
+ key_pair: OpenSSL::PKey::RSA.new(2048))
9
11
  certificate = certificate_for(key_pair.public_key)
10
12
  certificate.sign(key_pair, OpenSSL::Digest::SHA256.new)
11
13
  [certificate.to_pem, export(key_pair, algorithm, passphrase)]
@@ -24,14 +26,25 @@ module Xml
24
26
 
25
27
  def certificate_for(public_key)
26
28
  certificate = OpenSSL::X509::Certificate.new
27
- certificate.subject = certificate.issuer = OpenSSL::X509::Name.parse(SUBJECT)
29
+ certificate.subject =
30
+ certificate.issuer = OpenSSL::X509::Name.parse(SUBJECT)
28
31
  certificate.not_before = Time.now
29
32
  certificate.not_after = certificate.not_before + 30 * 24 * 60 * 60 # 30 days
30
33
  certificate.public_key = public_key
31
34
  certificate.serial = 0x0
32
35
  certificate.version = 2
36
+ apply_ski_extension_to(certificate)
33
37
  certificate
34
38
  end
39
+
40
+ def apply_ski_extension_to(certificate)
41
+ extensions = OpenSSL::X509::ExtensionFactory.new
42
+ extensions.subject_certificate = certificate
43
+ extensions.issuer_certificate = certificate
44
+ certificate.add_extension(
45
+ extensions.create_extension('subjectKeyIdentifier', 'hash', false)
46
+ )
47
+ end
35
48
  end
36
49
  end
37
50
  end
@@ -2,6 +2,10 @@
2
2
 
3
3
  module Xml
4
4
  module Kit
5
+ # An implementation of the Signature element.
6
+ # https://www.w3.org/TR/xmldsig-core1/#sec-Signature
7
+ #
8
+ # @since 0.1.0
5
9
  class Signature
6
10
  SIGNATURE_METHODS = {
7
11
  SHA1: 'http://www.w3.org/2000/09/xmldsig#rsa-sha1',
@@ -11,7 +15,7 @@ module Xml
11
15
  SHA512: 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha512',
12
16
  }.freeze
13
17
  DIGEST_METHODS = {
14
- SHA1: 'http://www.w3.org/2000/09/xmldsig#SHA1',
18
+ SHA1: 'http://www.w3.org/2000/09/xmldsig#sha1',
15
19
  SHA224: 'http://www.w3.org/2001/04/xmldsig-more#sha224',
16
20
  SHA256: 'http://www.w3.org/2001/04/xmlenc#sha256',
17
21
  SHA384: 'http://www.w3.org/2001/04/xmldsig-more#sha384',
@@ -23,7 +27,10 @@ module Xml
23
27
  attr_reader :reference_id
24
28
  attr_reader :signature_method
25
29
 
26
- def initialize(reference_id, signature_method: :SH256, digest_method: :SHA256, certificate:)
30
+ def initialize(reference_id,
31
+ signature_method: :SH256,
32
+ digest_method: :SHA256,
33
+ certificate:)
27
34
  @certificate = certificate
28
35
  @digest_method = DIGEST_METHODS[digest_method]
29
36
  @reference_id = reference_id
@@ -39,7 +39,10 @@ module Xml
39
39
  end
40
40
 
41
41
  # @!visibility private
42
- def self.sign(xml: ::Builder::XmlMarkup.new, key_pair:, signature_method: :SHA256, digest_method: :SHA256)
42
+ def self.sign(xml: ::Builder::XmlMarkup.new,
43
+ key_pair:,
44
+ signature_method: :SHA256,
45
+ digest_method: :SHA256)
43
46
  signatures = new(
44
47
  key_pair: key_pair,
45
48
  signature_method: signature_method,
@@ -17,22 +17,79 @@ module Xml
17
17
  # The [Xml::Kit::Certificate] that contains the public key to use for encrypting the document.
18
18
  attr_accessor :encryption_certificate
19
19
 
20
+ # Allows you to specify the digest method algorithm. (Default: SHA256)
21
+ # A list of digest methods can be found in [Xml::Kit::Signature].
22
+ attr_accessor :digest_method
23
+
24
+ # Allows you to specify the signature method algorithm. (Default: SHA256)
25
+ # A list of signature methods can be found in [Xml::Kit::Signature].
26
+ attr_accessor :signature_method
27
+
20
28
  # Returns the generated XML document with an XML Digital Signature and XML Encryption.
21
- def to_xml(xml: ::Builder::XmlMarkup.new)
22
- signatures.complete(render(self, xml: xml))
29
+ def to_xml(xml: ::Builder::XmlMarkup.new, pretty: false)
30
+ result = signatures.complete(render(self, xml: xml))
31
+ pretty ? Nokogiri::XML(result).to_xml(indent: 2) : result
32
+ end
33
+
34
+ # Generates an {#Xml::Kit::EncryptedKey} section. https://www.w3.org/TR/xmlenc-core1/#sec-EncryptedKey
35
+ #
36
+ # @since 0.3.0
37
+ # @param xml [Builder::XmlMarkup] the xml builder instance
38
+ # @param id [String] the id of EncryptedKey element
39
+ def encrypt_key_for(xml:, id:, key_info: nil)
40
+ ::Xml::Kit::EncryptedKey.new(
41
+ id: id,
42
+ asymmetric_cipher: asymmetric_cipher,
43
+ symmetric_cipher: symmetric_cipher,
44
+ key_info: key_info
45
+ ).to_xml(xml: xml)
46
+ end
47
+
48
+ # @deprecated Use {#encrypt_data_for} instead of this
49
+ def encryption_for(*args, &block)
50
+ ::Xml::Kit.deprecate(
51
+ 'encryption_for is deprecated. Use encrypt_data_for instead.'
52
+ )
53
+ encrypt_data_for(*args, &block)
54
+ end
55
+
56
+ # Generates an {#Xml::Kit::EncryptedData} section. https://www.w3.org/TR/xmlenc-core1/#sec-EncryptedData
57
+ #
58
+ # @since 0.3.0
59
+ # @param xml [Builder::XmlMarkup] the xml builder instance
60
+ # @param key_info [Xml::Kit::KeyInfo] the key info to render in the EncryptedData
61
+ def encrypt_data_for(xml:, key_info: nil)
62
+ return yield xml unless encrypt?
63
+
64
+ temp = ::Builder::XmlMarkup.new
65
+ yield temp
66
+ ::Xml::Kit::EncryptedData.new(
67
+ signatures.complete(temp.target!),
68
+ symmetric_cipher: symmetric_cipher,
69
+ asymmetric_cipher: asymmetric_cipher,
70
+ key_info: key_info
71
+ ).to_xml(xml: xml)
72
+ end
73
+
74
+ # Provides a default RSA asymmetric cipher. Can be overridden to provide custom ciphers.
75
+ #
76
+ # @abstract
77
+ # @since 0.3.0
78
+ def asymmetric_cipher(algorithm: Crypto::RsaCipher::ALGORITHM)
79
+ raise Xml::Kit::Error, 'encryption_certificate is not specified.' unless encryption_certificate
80
+
81
+ @asymmetric_cipher ||= Crypto.cipher_for(
82
+ algorithm,
83
+ encryption_certificate.public_key
84
+ )
23
85
  end
24
86
 
25
- def encryption_for(xml:)
26
- if encrypt?
27
- temp = ::Builder::XmlMarkup.new
28
- yield temp
29
- ::Xml::Kit::Encryption.new(
30
- signatures.complete(temp.target!),
31
- encryption_certificate.public_key
32
- ).to_xml(xml: xml)
33
- else
34
- yield xml
35
- end
87
+ # Provides a default aes256-cbc symmetric cipher. Can be overridden to provide custom ciphers.
88
+ #
89
+ # @abstract
90
+ # @since 0.3.0
91
+ def symmetric_cipher
92
+ @symmetric_cipher ||= Crypto::SymmetricCipher.new
36
93
  end
37
94
 
38
95
  def render(model, options)
@@ -48,9 +105,11 @@ module Xml
48
105
  # Allows you to specify which key pair to use for generating an XML digital signature.
49
106
  #
50
107
  # @param key_pair [Xml::Kit::KeyPair] the key pair to use for signing.
51
- def sign_with(key_pair)
108
+ def sign_with(key_pair, signature_method: :SHA256, digest_method: :SHA256)
52
109
  self.signing_key_pair = key_pair
53
110
  self.embed_signature = true
111
+ self.signature_method = signature_method
112
+ self.digest_method = digest_method
54
113
  signatures.sign_with(key_pair)
55
114
  end
56
115
 
@@ -72,19 +131,11 @@ module Xml
72
131
  def signatures
73
132
  @signatures ||= ::Xml::Kit::Signatures.new(
74
133
  key_pair: signing_key_pair,
75
- digest_method: digest_method,
76
- signature_method: signature_method
134
+ digest_method: digest_method || :SHA256,
135
+ signature_method: signature_method || :SHA256
77
136
  )
78
137
  end
79
138
 
80
- def digest_method
81
- :SHA256
82
- end
83
-
84
- def signature_method
85
- :SHA256
86
- end
87
-
88
139
  # @!visibility private
89
140
  def encrypt?
90
141
  encrypt && encryption_certificate
@@ -1,9 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  xml.KeyDescriptor use ? { use: use } : {} do
4
- xml.KeyInfo "xmlns": ::Xml::Kit::Namespaces::XMLDSIG do
5
- xml.X509Data do
6
- xml.X509Certificate stripped
7
- end
8
- end
4
+ render key_info, xml: xml
9
5
  end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ xml.EncryptedData xmlns: ::Xml::Kit::Namespaces::XMLENC do
4
+ xml.EncryptionMethod Algorithm: symmetric_cipher.algorithm
5
+ render key_info, xml: xml
6
+ xml.CipherData do
7
+ xml.CipherValue symmetric_cipher_value
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ xml.EncryptedKey Id: id, xmlns: ::Xml::Kit::Namespaces::XMLENC do
4
+ xml.EncryptionMethod Algorithm: asymmetric_cipher.algorithm
5
+ render(key_info, xml: xml) if key_info
6
+ xml.CipherData do
7
+ xml.CipherValue cipher_value
8
+ end
9
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ xml.KeyInfo xmlns: ::Xml::Kit::Namespaces::XMLDSIG do
4
+ xml.KeyName key_name if key_name
5
+ render(key_value, xml: xml) if @key_value
6
+ render(retrieval_method, xml: xml) if @retrieval_method
7
+ if x509_data
8
+ xml.X509Data do
9
+ xml.X509SKI subject_key_identifier
10
+ xml.X509Certificate ::Xml::Kit::Certificate.strip(x509_data.to_pem)
11
+ end
12
+ end
13
+ render(encrypted_key, xml: xml) if encrypted_key
14
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ xml.KeyValue do
4
+ render(rsa, xml: xml) if @rsa
5
+ end
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ xml.RetrievalMethod xmlns: ::Xml::Kit::Namespaces::XMLDSIG, URI: uri, Type: type
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ xml.RSAKeyValue do
4
+ xml.Modulus modulus
5
+ xml.Exponent exponent
6
+ end
@@ -6,7 +6,7 @@ xml.Signature 'xmlns' => ::Xml::Kit::Namespaces::XMLDSIG do
6
6
  xml.SignatureMethod Algorithm: signature_method
7
7
  xml.Reference URI: "##{reference_id}" do
8
8
  xml.Transforms do
9
- xml.Transform Algorithm: "#{::Xml::Kit::Namespaces::XMLDSIG}enveloped-signature"
9
+ xml.Transform Algorithm: ::Xml::Kit::Namespaces::ENVELOPED_SIG
10
10
  xml.Transform Algorithm: ::Xml::Kit::Namespaces::CANONICALIZATION
11
11
  end
12
12
  xml.DigestMethod Algorithm: digest_method
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Xml
4
4
  module Kit
5
- VERSION = '0.1.14'.freeze
5
+ VERSION = '0.5.0'
6
6
  end
7
7
  end
@@ -14,7 +14,7 @@ Gem::Specification.new do |spec|
14
14
  spec.description = 'A simple toolkit for working with XML.'
15
15
  spec.homepage = 'https://github.com/saml-kit/xml-kit'
16
16
  spec.license = 'MIT'
17
- spec.required_ruby_version = '>= 2.2.0'
17
+ spec.required_ruby_version = Gem::Requirement.new('>= 2.5.0')
18
18
 
19
19
  spec.files = `git ls-files -z`.split("\x0").reject do |f|
20
20
  f.match(%r{^(test|spec|features)/})
@@ -26,13 +26,13 @@ Gem::Specification.new do |spec|
26
26
 
27
27
  spec.add_dependency 'activemodel', '>= 4.2.0'
28
28
  spec.add_dependency 'builder', '~> 3.2'
29
- spec.add_dependency 'nokogiri', '>= 1.8.5'
29
+ spec.add_dependency 'nokogiri', '~> 1.10'
30
30
  spec.add_dependency 'tilt', '>= 1.4.1'
31
31
  spec.add_dependency 'xmldsig', '~> 0.6'
32
- spec.add_development_dependency 'bundler', '~> 1.16'
32
+ spec.add_development_dependency 'bundler', '~> 2.0'
33
33
  spec.add_development_dependency 'bundler-audit', '~> 0.6'
34
34
  spec.add_development_dependency 'ffaker', '~> 2.7'
35
- spec.add_development_dependency 'rake', '~> 10.0'
35
+ spec.add_development_dependency 'rake', '~> 13.0'
36
36
  spec.add_development_dependency 'rspec', '~> 3.0'
37
37
  spec.add_development_dependency 'rubocop', '~> 0.52'
38
38
  spec.add_development_dependency 'rubocop-rspec', '~> 1.22'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: xml-kit
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.14
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - mo khan
8
- autorequire:
8
+ autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2018-11-21 00:00:00.000000000 Z
11
+ date: 2021-01-31 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activemodel
@@ -42,16 +42,16 @@ dependencies:
42
42
  name: nokogiri
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
- - - ">="
45
+ - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: 1.8.5
47
+ version: '1.10'
48
48
  type: :runtime
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
- - - ">="
52
+ - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: 1.8.5
54
+ version: '1.10'
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: tilt
57
57
  requirement: !ruby/object:Gem::Requirement
@@ -86,14 +86,14 @@ dependencies:
86
86
  requirements:
87
87
  - - "~>"
88
88
  - !ruby/object:Gem::Version
89
- version: '1.16'
89
+ version: '2.0'
90
90
  type: :development
91
91
  prerelease: false
92
92
  version_requirements: !ruby/object:Gem::Requirement
93
93
  requirements:
94
94
  - - "~>"
95
95
  - !ruby/object:Gem::Version
96
- version: '1.16'
96
+ version: '2.0'
97
97
  - !ruby/object:Gem::Dependency
98
98
  name: bundler-audit
99
99
  requirement: !ruby/object:Gem::Requirement
@@ -128,14 +128,14 @@ dependencies:
128
128
  requirements:
129
129
  - - "~>"
130
130
  - !ruby/object:Gem::Version
131
- version: '10.0'
131
+ version: '13.0'
132
132
  type: :development
133
133
  prerelease: false
134
134
  version_requirements: !ruby/object:Gem::Requirement
135
135
  requirements:
136
136
  - - "~>"
137
137
  - !ruby/object:Gem::Version
138
- version: '10.0'
138
+ version: '13.0'
139
139
  - !ruby/object:Gem::Dependency
140
140
  name: rspec
141
141
  requirement: !ruby/object:Gem::Requirement
@@ -203,9 +203,10 @@ files:
203
203
  - ".gitlab-ci.yml"
204
204
  - ".rspec"
205
205
  - ".rubocop.yml"
206
- - ".rubocop_todo.yml"
207
206
  - ".travis.yml"
207
+ - CHANGELOG.md
208
208
  - Gemfile
209
+ - Gemfile.lock
209
210
  - LICENSE.txt
210
211
  - README.md
211
212
  - Rakefile
@@ -225,9 +226,15 @@ files:
225
226
  - lib/xml/kit/decryption.rb
226
227
  - lib/xml/kit/decryption_error.rb
227
228
  - lib/xml/kit/document.rb
229
+ - lib/xml/kit/encrypted_data.rb
230
+ - lib/xml/kit/encrypted_key.rb
228
231
  - lib/xml/kit/encryption.rb
229
232
  - lib/xml/kit/fingerprint.rb
230
233
  - lib/xml/kit/id.rb
234
+ - lib/xml/kit/key_info.rb
235
+ - lib/xml/kit/key_info/key_value.rb
236
+ - lib/xml/kit/key_info/retrieval_method.rb
237
+ - lib/xml/kit/key_info/rsa_key_value.rb
231
238
  - lib/xml/kit/key_pair.rb
232
239
  - lib/xml/kit/namespaces.rb
233
240
  - lib/xml/kit/self_signed_certificate.rb
@@ -236,8 +243,13 @@ files:
236
243
  - lib/xml/kit/templatable.rb
237
244
  - lib/xml/kit/template.rb
238
245
  - lib/xml/kit/templates/certificate.builder
239
- - lib/xml/kit/templates/encryption.builder
246
+ - lib/xml/kit/templates/encrypted_data.builder
247
+ - lib/xml/kit/templates/encrypted_key.builder
248
+ - lib/xml/kit/templates/key_info.builder
249
+ - lib/xml/kit/templates/key_value.builder
240
250
  - lib/xml/kit/templates/nil_class.builder
251
+ - lib/xml/kit/templates/retrieval_method.builder
252
+ - lib/xml/kit/templates/rsa_key_value.builder
241
253
  - lib/xml/kit/templates/signature.builder
242
254
  - lib/xml/kit/version.rb
243
255
  - xml-kit.gemspec
@@ -246,7 +258,7 @@ licenses:
246
258
  - MIT
247
259
  metadata:
248
260
  yard.run: yri
249
- post_install_message:
261
+ post_install_message:
250
262
  rdoc_options: []
251
263
  require_paths:
252
264
  - lib
@@ -254,16 +266,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
254
266
  requirements:
255
267
  - - ">="
256
268
  - !ruby/object:Gem::Version
257
- version: 2.2.0
269
+ version: 2.5.0
258
270
  required_rubygems_version: !ruby/object:Gem::Requirement
259
271
  requirements:
260
272
  - - ">="
261
273
  - !ruby/object:Gem::Version
262
274
  version: '0'
263
275
  requirements: []
264
- rubyforge_project:
265
- rubygems_version: 2.7.6
266
- signing_key:
276
+ rubygems_version: 3.2.3
277
+ signing_key:
267
278
  specification_version: 4
268
279
  summary: A simple toolkit for working with XML.
269
280
  test_files: []