saml-kit 0.3.0 → 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
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