Module: SAML2::Signable

Included in:
Entity, Entity::Group, Message, Role
Defined in:
lib/saml2/signable.rb

Instance Method Summary collapse

Instance Method Details

#sign(x509_certificate, private_key, algorithm_name = :sha256) ⇒ self

Sign this object.



103
104
105
106
107
108
109
110
111
112
113
114
# File 'lib/saml2/signable.rb', line 103

def sign(x509_certificate, private_key, algorithm_name = :sha256)
  to_xml

  xml = @document.root
  xml.set_id_attribute('ID')
  xml.sign!(cert: x509_certificate, key: private_key, digest_alg: algorithm_name.to_s, signature_alg: "rsa-#{algorithm_name}", uri: "##{id}")
  # the Signature element must be the first element
  signature = xml.at_xpath("dsig:Signature", Namespaces::ALL)
  xml.children.first.add_previous_sibling(signature)

  self
end

#signatureNokogiri::XML::Element?



8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# File 'lib/saml2/signable.rb', line 8

def signature
  unless instance_variable_defined?(:@signature)
    @signature = xml.at_xpath('dsig:Signature', Namespaces::ALL)
    if @signature
      signed_node = @signature.at_xpath('dsig:SignedInfo/dsig:Reference', Namespaces::ALL)['URI']
      if signed_node == ''
        @signature = nil unless xml == xml.document.root
      elsif signed_node != "##{xml['ID']}"
        @signature = nil
      else
        # validating the schema will automatically add ID attributes, so check that first
        xml.set_id_attribute('ID') unless xml.document.get_id(xml['ID'])
      end
    end
  end
  @signature
end

#signed?Boolean



31
32
33
# File 'lib/saml2/signable.rb', line 31

def signed?
  !!signature
end

#signing_keyKeyInfo?



27
28
29
# File 'lib/saml2/signable.rb', line 27

def signing_key
  @signing_key ||= KeyInfo.from_xml(signature)
end

#valid_signature?(fingerprint: nil, cert: nil) ⇒ Boolean

Check if the signature on this object is valid.

Either fingerprint or cert must be provided.



90
91
92
# File 'lib/saml2/signable.rb', line 90

def valid_signature?(fingerprint: nil, cert: nil)
  validate_signature(fingerprint: fingerprint, cert: cert).empty?
end

#validate_signature(key: nil, fingerprint: nil, cert: nil) ⇒ Array<String>

Validate the signature on this object.

At least one of key, fingerprint or cert must be provided.



52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
# File 'lib/saml2/signable.rb', line 52

def validate_signature(key: nil,
                       fingerprint: nil,
                       cert: nil)
  return ["not signed"] unless signed?

  certs = Array(cert)
  certs = certs.dup if certs.equal?(cert)
  # see if any given fingerprints match the certificate embedded in the XML;
  # if so, extract the certificate, and add it to the allowed certificates list
  Array(fingerprint).each do |fp|
    certs << signing_key.certificate if signing_key&.fingerprint == SAML2::KeyInfo.format_fingerprint(fp)
  end
  certs = certs.uniq

  trusted_keys = certs.map do |cert|
    cert = cert.is_a?(String) ? OpenSSL::X509::Certificate.new(cert) : cert
    cert.public_key.to_s
  end
  if signing_key&.certificate && trusted_keys.include?(signing_key.certificate.public_key.to_s)
    key ||= signing_key.certificate.public_key.to_s
  end

  return ["no trusted signing key found"] if key.nil?

  begin
    result = signature.verify_with(key: key)
    result ? [] : ["signature is invalid"]
  rescue XMLSec::VerificationError => e
    [e.message]
  end
end