saml-kit 0.3.0 → 0.3.1

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.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/exe/saml-kit-create-self-signed-certificate +1 -1
  3. data/exe/saml-kit-decode-http-post +1 -3
  4. data/exe/saml-kit-decode-http-redirect +2 -3
  5. data/lib/saml/kit.rb +1 -14
  6. data/lib/saml/kit/assertion.rb +14 -11
  7. data/lib/saml/kit/bindings/url_builder.rb +1 -1
  8. data/lib/saml/kit/builders.rb +2 -2
  9. data/lib/saml/kit/builders/assertion.rb +3 -2
  10. data/lib/saml/kit/builders/authentication_request.rb +3 -2
  11. data/lib/saml/kit/builders/encrypted_assertion.rb +20 -0
  12. data/lib/saml/kit/builders/identity_provider_metadata.rb +4 -3
  13. data/lib/saml/kit/builders/logout_request.rb +3 -2
  14. data/lib/saml/kit/builders/logout_response.rb +3 -2
  15. data/lib/saml/kit/builders/metadata.rb +4 -3
  16. data/lib/saml/kit/builders/response.rb +14 -5
  17. data/lib/saml/kit/builders/service_provider_metadata.rb +2 -1
  18. data/lib/saml/kit/builders/templates/assertion.builder +21 -23
  19. data/lib/saml/kit/builders/templates/encrypted_assertion.builder +5 -0
  20. data/lib/saml/kit/configuration.rb +2 -2
  21. data/lib/saml/kit/document.rb +11 -1
  22. data/lib/saml/kit/metadata.rb +13 -6
  23. data/lib/saml/kit/namespaces.rb +0 -11
  24. data/lib/saml/kit/signature.rb +1 -1
  25. data/lib/saml/kit/trustable.rb +7 -1
  26. data/lib/saml/kit/version.rb +1 -1
  27. data/lib/saml/kit/xml_templatable.rb +37 -0
  28. data/saml-kit.gemspec +1 -3
  29. metadata +10 -56
  30. data/lib/saml/kit/builders/templates/certificate.builder +0 -7
  31. data/lib/saml/kit/builders/templates/nil_class.builder +0 -0
  32. data/lib/saml/kit/builders/templates/xml_encryption.builder +0 -16
  33. data/lib/saml/kit/builders/templates/xml_signature.builder +0 -20
  34. data/lib/saml/kit/builders/xml_encryption.rb +0 -20
  35. data/lib/saml/kit/builders/xml_signature.rb +0 -40
  36. data/lib/saml/kit/certificate.rb +0 -96
  37. data/lib/saml/kit/crypto.rb +0 -17
  38. data/lib/saml/kit/crypto/oaep_cipher.rb +0 -22
  39. data/lib/saml/kit/crypto/rsa_cipher.rb +0 -23
  40. data/lib/saml/kit/crypto/simple_cipher.rb +0 -38
  41. data/lib/saml/kit/crypto/unknown_cipher.rb +0 -18
  42. data/lib/saml/kit/fingerprint.rb +0 -50
  43. data/lib/saml/kit/id.rb +0 -14
  44. data/lib/saml/kit/key_pair.rb +0 -29
  45. data/lib/saml/kit/self_signed_certificate.rb +0 -28
  46. data/lib/saml/kit/signatures.rb +0 -57
  47. data/lib/saml/kit/templatable.rb +0 -67
  48. data/lib/saml/kit/template.rb +0 -33
  49. data/lib/saml/kit/xml.rb +0 -80
  50. data/lib/saml/kit/xml_decryption.rb +0 -44
@@ -1,17 +0,0 @@
1
- require 'saml/kit/crypto/oaep_cipher'
2
- require 'saml/kit/crypto/rsa_cipher'
3
- require 'saml/kit/crypto/simple_cipher'
4
- require 'saml/kit/crypto/unknown_cipher'
5
-
6
- module Saml
7
- module Kit
8
- module Crypto
9
- DECRYPTORS = [ SimpleCipher, RsaCipher, OaepCipher, UnknownCipher ]
10
-
11
- # @!visibility private
12
- def self.decryptor_for(algorithm, key)
13
- DECRYPTORS.find { |x| x.matches?(algorithm) }.new(algorithm, key)
14
- end
15
- end
16
- end
17
- end
@@ -1,22 +0,0 @@
1
- module Saml
2
- module Kit
3
- module Crypto
4
- class OaepCipher
5
- ALGORITHMS = {
6
- 'http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p' => true,
7
- }
8
- def initialize(algorithm, key)
9
- @key = key
10
- end
11
-
12
- def self.matches?(algorithm)
13
- ALGORITHMS[algorithm]
14
- end
15
-
16
- def decrypt(cipher_text)
17
- @key.private_decrypt(cipher_text, OpenSSL::PKey::RSA::PKCS1_OAEP_PADDING)
18
- end
19
- end
20
- end
21
- end
22
- end
@@ -1,23 +0,0 @@
1
- module Saml
2
- module Kit
3
- module Crypto
4
- class RsaCipher
5
- ALGORITHMS = {
6
- 'http://www.w3.org/2001/04/xmlenc#rsa-1_5' => true,
7
- }
8
-
9
- def initialize(algorithm, key)
10
- @key = key
11
- end
12
-
13
- def self.matches?(algorithm)
14
- ALGORITHMS[algorithm]
15
- end
16
-
17
- def decrypt(cipher_text)
18
- @key.private_decrypt(cipher_text)
19
- end
20
- end
21
- end
22
- end
23
- end
@@ -1,38 +0,0 @@
1
- module Saml
2
- module Kit
3
- module Crypto
4
- class SimpleCipher
5
- ALGORITHMS = {
6
- 'http://www.w3.org/2001/04/xmlenc#tripledes-cbc' => 'DES-EDE3-CBC',
7
- 'http://www.w3.org/2001/04/xmlenc#aes128-cbc' => 'AES-128-CBC',
8
- 'http://www.w3.org/2001/04/xmlenc#aes192-cbc' => 'AES-192-CBC',
9
- 'http://www.w3.org/2001/04/xmlenc#aes256-cbc' => 'AES-256-CBC',
10
- }
11
-
12
- def initialize(algorithm, private_key)
13
- @algorithm = algorithm
14
- @private_key = private_key
15
- end
16
-
17
- def self.matches?(algorithm)
18
- ALGORITHMS[algorithm]
19
- end
20
-
21
- def decrypt(cipher_text)
22
- cipher = OpenSSL::Cipher.new(ALGORITHMS[@algorithm])
23
- cipher.decrypt
24
- iv = cipher_text[0..cipher.iv_len-1]
25
- data = cipher_text[cipher.iv_len..-1]
26
- #cipher.padding = 0
27
- cipher.key = @private_key
28
- cipher.iv = iv
29
-
30
- Saml::Kit.logger.debug ['-key', @private_key].inspect
31
- Saml::Kit.logger.debug ['-iv', iv].inspect
32
-
33
- cipher.update(data) + cipher.final
34
- end
35
- end
36
- end
37
- end
38
- end
@@ -1,18 +0,0 @@
1
- module Saml
2
- module Kit
3
- module Crypto
4
- class UnknownCipher
5
- def initialize(algorithm, key)
6
- end
7
-
8
- def self.matches?(algorithm)
9
- true
10
- end
11
-
12
- def decrypt(cipher_text)
13
- cipher_text
14
- end
15
- end
16
- end
17
- end
18
- end
@@ -1,50 +0,0 @@
1
- module Saml
2
- module Kit
3
- # This generates a fingerprint for an X509 Certificate.
4
- #
5
- # certificate, _ = Saml::Kit::SelfSignedCertificate.new("password").create
6
- #
7
- # puts Saml::Kit::Fingerprint.new(certificate).to_s
8
- # # B7:AB:DC:BD:4D:23:58:65:FD:1A:99:0C:5F:89:EA:87:AD:F1:D7:83:34:7A:E9:E4:88:12:DD:46:1F:38:05:93
9
- #
10
- # {include:file:spec/saml/fingerprint_spec.rb}
11
- class Fingerprint
12
- # The OpenSSL::X509::Certificate
13
- attr_reader :x509
14
-
15
- def initialize(raw_certificate)
16
- @x509 = Certificate.to_x509(raw_certificate)
17
- end
18
-
19
- # Generates a formatted fingerprint using the specified hash algorithm.
20
- #
21
- # @param algorithm [OpenSSL::Digest] the openssl algorithm to use `OpenSSL::Digest::SHA256`, `OpenSSL::Digest::SHA1`.
22
- # @return [String] in the format of `"BF:ED:C5:F1:6C:AB:F5:B2:15:1F:BF:BD:7D:68:1A:F9:A5:4E:4C:19:30:BC:6D:25:B1:8E:98:D4:23:FD:B4:09"`
23
- def algorithm(algorithm)
24
- pretty_fingerprint(algorithm.new.hexdigest(x509.to_der))
25
- end
26
-
27
- def ==(other)
28
- self.to_s == other.to_s
29
- end
30
-
31
- def eql?(other)
32
- self == other
33
- end
34
-
35
- def hash
36
- to_s.hash
37
- end
38
-
39
- def to_s
40
- algorithm(OpenSSL::Digest::SHA256)
41
- end
42
-
43
- private
44
-
45
- def pretty_fingerprint(fingerprint)
46
- fingerprint.upcase.scan(/../).join(":")
47
- end
48
- end
49
- end
50
- end
@@ -1,14 +0,0 @@
1
- module Saml
2
- module Kit
3
- # This class is used primary for generating ID.
4
- #https://www.w3.org/2001/XMLSchema.xsd
5
- class Id
6
-
7
- # Generate an ID that conforms to the XML Schema.
8
- # https://www.w3.org/2001/XMLSchema.xsd
9
- def self.generate
10
- "_#{SecureRandom.uuid}"
11
- end
12
- end
13
- end
14
- end
@@ -1,29 +0,0 @@
1
- module Saml
2
- module Kit
3
- class KeyPair # :nodoc:
4
- attr_reader :certificate, :private_key, :use
5
-
6
- def initialize(certificate, private_key, passphrase, use)
7
- @use = use
8
- @certificate = Saml::Kit::Certificate.new(certificate, use: use)
9
- @private_key = OpenSSL::PKey::RSA.new(private_key, passphrase)
10
- end
11
-
12
- # Returns true if the key pair is the designated use.
13
- #
14
- # @param use [Symbol] Can be either `:signing` or `:encryption`.
15
- def for?(use)
16
- @use == use
17
- end
18
-
19
- # Returns a generated self signed certificate with private key.
20
- #
21
- # @param use [Symbol] Can be either `:signing` or `:encryption`.
22
- # @param passphrase [String] the passphrase to use to encrypt the private key.
23
- def self.generate(use:, passphrase: SecureRandom.uuid)
24
- certificate, private_key = SelfSignedCertificate.new(passphrase).create
25
- new(certificate, private_key, passphrase, use)
26
- end
27
- end
28
- end
29
- end
@@ -1,28 +0,0 @@
1
- module Saml
2
- module Kit
3
- class SelfSignedCertificate
4
- SUBJECT="/C=CA/ST=Alberta/L=Calgary/O=SamlKit/OU=SamlKit/CN=SamlKit"
5
-
6
- def initialize(passphrase)
7
- @passphrase = passphrase
8
- end
9
-
10
- def create
11
- rsa_key = OpenSSL::PKey::RSA.new(2048)
12
- public_key = rsa_key.public_key
13
- certificate = OpenSSL::X509::Certificate.new
14
- certificate.subject = certificate.issuer = OpenSSL::X509::Name.parse(SUBJECT)
15
- certificate.not_before = DateTime.now.beginning_of_day
16
- certificate.not_after = 30.days.from_now
17
- certificate.public_key = public_key
18
- certificate.serial = 0x0
19
- certificate.version = 2
20
- certificate.sign(rsa_key, OpenSSL::Digest::SHA256.new)
21
- [
22
- certificate.to_pem,
23
- rsa_key.to_pem(OpenSSL::Cipher.new('AES-256-CBC'), @passphrase)
24
- ]
25
- end
26
- end
27
- end
28
- end
@@ -1,57 +0,0 @@
1
- module Saml
2
- module Kit
3
- # @!visibility private
4
- class Signatures # :nodoc:
5
- # @!visibility private
6
- attr_reader :configuration
7
-
8
- # @!visibility private
9
- def initialize(configuration:)
10
- @configuration = configuration
11
- @key_pair = configuration.key_pairs(use: :signing).last
12
- end
13
-
14
- # @!visibility private
15
- def sign_with(key_pair)
16
- @key_pair = key_pair
17
- end
18
-
19
- # @!visibility private
20
- def build(reference_id)
21
- return nil unless configuration.sign?
22
- certificate = @key_pair.certificate
23
- Saml::Kit::Builders::XmlSignature.new(reference_id, configuration: configuration, certificate: certificate)
24
- end
25
-
26
- # @!visibility private
27
- def complete(raw_xml)
28
- return raw_xml unless configuration.sign?
29
- private_key = @key_pair.private_key
30
- Xmldsig::SignedDocument.new(raw_xml).sign(private_key)
31
- end
32
-
33
- # @!visibility private
34
- def self.sign(xml: ::Builder::XmlMarkup.new, configuration: Saml::Kit.configuration)
35
- signatures = Saml::Kit::Signatures.new(configuration: configuration)
36
- yield xml, XmlSignatureTemplate.new(xml, signatures)
37
- signatures.complete(xml.target!)
38
- end
39
-
40
- class XmlSignatureTemplate # :nodoc:
41
- # @!visibility private
42
- attr_reader :signatures, :xml
43
-
44
- # @!visibility private
45
- def initialize(xml, signatures)
46
- @signatures = signatures
47
- @xml = xml
48
- end
49
-
50
- # @!visibility private
51
- def template(reference_id)
52
- Template.new(signatures.build(reference_id)).to_xml(xml: xml)
53
- end
54
- end
55
- end
56
- end
57
- end
@@ -1,67 +0,0 @@
1
- module Saml
2
- module Kit
3
- module Templatable
4
- # Can be used to disable embeding a signature.
5
- # By default a signature will be embedded if a signing
6
- # certificate is available via the configuration.
7
- attr_accessor :embed_signature
8
-
9
- # @deprecated Use {#embed_signature=} instead of this method.
10
- def sign=(value)
11
- Saml::Kit.deprecate("sign= is deprecated. Use embed_signature= instead")
12
- self.embed_signature = value
13
- end
14
-
15
- # Returns the generated XML document with an XML Digital Signature and XML Encryption.
16
- def to_xml(xml: ::Builder::XmlMarkup.new)
17
- signatures.complete(render(self, xml: xml))
18
- end
19
-
20
- # @!visibility private
21
- def signature_for(reference_id:, xml:)
22
- return unless sign?
23
- render(signatures.build(reference_id), xml: xml)
24
- end
25
-
26
- # Allows you to specify which key pair to use for generating an XML digital signature.
27
- #
28
- # @param key_pair [Saml::Kit::KeyPair] the key pair to use for signing.
29
- def sign_with(key_pair)
30
- signatures.sign_with(key_pair)
31
- end
32
-
33
- # Returns true if an embedded signature is requested and at least one signing certificate is available via the configuration.
34
- def sign?
35
- embed_signature.nil? ? configuration.sign? : embed_signature && configuration.sign?
36
- end
37
-
38
- # @!visibility private
39
- def signatures
40
- @signatures ||= Saml::Kit::Signatures.new(configuration: configuration)
41
- end
42
-
43
- # @!visibility private
44
- def encryption_for(xml:)
45
- if encrypt?
46
- temp = ::Builder::XmlMarkup.new
47
- yield temp
48
- signed_xml = signatures.complete(temp.target!)
49
- xml_encryption = Saml::Kit::Builders::XmlEncryption.new(signed_xml, encryption_certificate.public_key)
50
- render(xml_encryption, xml: xml)
51
- else
52
- yield xml
53
- end
54
- end
55
-
56
- # @!visibility private
57
- def encrypt?
58
- encrypt && encryption_certificate
59
- end
60
-
61
- # @!visibility private
62
- def render(model, options)
63
- Saml::Kit::Template.new(model).to_xml(options)
64
- end
65
- end
66
- end
67
- end
@@ -1,33 +0,0 @@
1
- module Saml
2
- module Kit
3
- class Template
4
- attr_reader :target
5
-
6
- def initialize(target)
7
- @target = target
8
- end
9
-
10
- # Returns the compiled template as a [String].
11
- #
12
- # @param options [Hash] The options hash to pass to the template engine.
13
- def to_xml(options)
14
- template.render(target, options)
15
- end
16
-
17
- private
18
-
19
- def template_name
20
- "#{target.class.name.split("::").last.underscore}.builder"
21
- end
22
-
23
- def template_path
24
- root_path = File.expand_path(File.dirname(__FILE__))
25
- File.join(root_path, "builders/templates/", template_name)
26
- end
27
-
28
- def template
29
- Tilt.new(template_path)
30
- end
31
- end
32
- end
33
- end
@@ -1,80 +0,0 @@
1
- module Saml
2
- module Kit
3
- # {include:file:spec/saml/xml_spec.rb}
4
- class Xml # :nodoc:
5
- include ActiveModel::Validations
6
- NAMESPACES = {
7
- "NameFormat": Namespaces::ATTR_SPLAT,
8
- "ds": Namespaces::XMLDSIG,
9
- "md": Namespaces::METADATA,
10
- "saml": Namespaces::ASSERTION,
11
- "samlp": Namespaces::PROTOCOL,
12
- }.freeze
13
-
14
- validate :validate_signatures
15
- validate :validate_certificates
16
-
17
- def initialize(raw_xml)
18
- @raw_xml = raw_xml
19
- @document = Nokogiri::XML(raw_xml)
20
- end
21
-
22
- # Returns the first XML node found by searching the document with the provided XPath.
23
- #
24
- # @param xpath [String] the XPath to use to search the document
25
- def find_by(xpath)
26
- document.at_xpath(xpath, NAMESPACES)
27
- end
28
-
29
- # Returns all XML nodes found by searching the document with the provided XPath.
30
- #
31
- # @param xpath [String] the XPath to use to search the document
32
- def find_all(xpath)
33
- document.search(xpath, NAMESPACES)
34
- end
35
-
36
- # Return the XML document as a [String].
37
- #
38
- # @param pretty [Boolean] return the XML string in a human readable format if true.
39
- def to_xml(pretty: true)
40
- pretty ? document.to_xml(indent: 2) : raw_xml
41
- end
42
-
43
- private
44
-
45
- attr_reader :raw_xml, :document
46
-
47
- def validate_signatures
48
- invalid_signatures.flat_map(&:errors).uniq.each do |error|
49
- errors.add(error, "is invalid")
50
- end
51
- end
52
-
53
- def invalid_signatures
54
- signed_document = Xmldsig::SignedDocument.new(document, id_attr: 'ID=$uri or @Id')
55
- signed_document.signatures.find_all do |signature|
56
- x509_certificates.all? do |certificate|
57
- !signature.valid?(certificate)
58
- end
59
- end
60
- end
61
-
62
- def validate_certificates(now = Time.current)
63
- return if find_by('//ds:Signature').nil?
64
-
65
- x509_certificates.each do |certificate|
66
- inactive = now < certificate.not_before
67
- errors.add(:certificate, "Not valid before #{certificate.not_before}") if inactive
68
-
69
- expired = now > certificate.not_after
70
- errors.add(:certificate, "Not valid after #{certificate.not_after}") if expired
71
- end
72
- end
73
-
74
- def x509_certificates
75
- xpath = "//ds:KeyInfo/ds:X509Data/ds:X509Certificate"
76
- find_all(xpath).map { |item| Certificate.to_x509(item.text) }
77
- end
78
- end
79
- end
80
- end