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) ⇒ Object



56
57
58
59
60
61
62
63
64
65
66
67
# File 'lib/saml2/signable.rb', line 56

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

#signatureObject



5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# File 'lib/saml2/signable.rb', line 5

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']}"
        # validating the schema will automatically add ID attributes, so check that first
        xml.set_id_attribute('ID') unless xml.document.get_id(xml['ID'])
        @signature = nil
      end
    end
  end
  @signature
end

#signed?Boolean

Returns:

  • (Boolean)


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

def signed?
  !!signature
end

#signing_keyObject



22
23
24
# File 'lib/saml2/signable.rb', line 22

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

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

Returns:

  • (Boolean)


52
53
54
# File 'lib/saml2/signable.rb', line 52

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

#validate_signature(fingerprint: nil, cert: nil, verification_time: nil) ⇒ Object



30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
# File 'lib/saml2/signable.rb', line 30

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

  certs = Array(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 == Key.format_fingerprint(fp)
  end
  certs = certs.uniq
  return ["no certificate found"] if certs.empty?

  begin
    # verify_certificates being false is hopefully a temporary thing, until I can figure
    # out how to get xmlsec to root a trust chain in a non-root certificate
    result = signature.verify_with(certs: certs, verification_time: verification_time, verify_certificates: false)
    result ? [] : ["signature does not match"]
  rescue XMLSec::VerificationError => e
    [e.message]
  end
end