class OmniAuth::Strategies::WSFed::XMLSecurity::SignedDocument

Constants

DSIG

Attributes

settings[RW]
signed_element_id[RW]

Public Class Methods

new(response, settings = {}) click to toggle source
Calls superclass method
# File lib/omniauth/strategies/wsfed/xml_security.rb, line 44
def initialize(response, settings = {})
  super(response)
  extract_signed_element_id

  self.settings = settings
end

Public Instance Methods

validate(idp_cert_fingerprint, soft = true) click to toggle source
# File lib/omniauth/strategies/wsfed/xml_security.rb, line 51
def validate(idp_cert_fingerprint, soft = true)
  # get cert from response
  base64_cert = self.elements["//ds:X509Certificate"].text
  cert_text   = Base64.decode64(base64_cert)
  cert        = OpenSSL::X509::Certificate.new(cert_text)

  # check cert matches registered idp cert
  fingerprint = Digest::SHA1.hexdigest(cert.to_der)

  if fingerprint != idp_cert_fingerprint.gsub(/[^a-zA-Z0-9]/,"").downcase
    return soft ? false : (raise OmniAuth::Strategies::WSFed::ValidationError.new("Fingerprint mismatch"))
  end

  validate_doc(base64_cert, soft)
end
validate_doc(base64_cert, soft = true) click to toggle source
# File lib/omniauth/strategies/wsfed/xml_security.rb, line 67
def validate_doc(base64_cert, soft = true)
  # validate references

  # check for inclusive namespaces

  inclusive_namespaces            = []
  inclusive_namespace_element     = REXML::XPath.first(self, "//ec:InclusiveNamespaces")

  if inclusive_namespace_element
    prefix_list                   = inclusive_namespace_element.attributes.get_attribute('PrefixList').value
    inclusive_namespaces          = prefix_list.split(" ")
  end

  # remove signature node
  sig_element = REXML::XPath.first(self, "//ds:Signature", {"ds"=>DSIG})
  sig_element.remove

  # check digests
  saml_version = settings[:saml_version]
  REXML::XPath.each(sig_element, "//ds:Reference", {"ds"=>DSIG}) do |ref|
    uri                           = ref.attributes.get_attribute("URI").value
    hashed_element                = REXML::XPath.first(self, "//*[@ID='#{uri[1,uri.size]}']") ||
                                    REXML::XPath.first(self, "//*[@AssertionID='#{uri[1,uri.size]}']")
    canoner                       = XML::Util::XmlCanonicalizer.new(false, true)
    canoner.inclusive_namespaces  = inclusive_namespaces if canoner.respond_to?(:inclusive_namespaces) && !inclusive_namespaces.empty?
    canon_hashed_element          = canoner.canonicalize(hashed_element)
          digest_algorithm              = algorithm(REXML::XPath.first(ref, "//ds:DigestMethod"))
    hash                          = Base64.encode64(digest_algorithm.digest(canon_hashed_element)).chomp
    digest_value                  = REXML::XPath.first(ref, "//ds:DigestValue", {"ds"=>"http://www.w3.org/2000/09/xmldsig#"}).text

    unless digests_match?(hash, digest_value)
      return soft ? false : (raise OmniAuth::Strategies::WSFed::ValidationError.new("Digest mismatch"))
    end
  end

  # verify signature
  canoner                 = XML::Util::XmlCanonicalizer.new(false, true)
  signed_info_element     = REXML::XPath.first(sig_element, "//ds:SignedInfo", {"ds"=>DSIG})
  canon_string            = canoner.canonicalize(signed_info_element)

  base64_signature        = REXML::XPath.first(sig_element, "//ds:SignatureValue", {"ds"=>DSIG}).text
  signature               = Base64.decode64(base64_signature)

  # get certificate object
  cert_text               = Base64.decode64(base64_cert)
  cert                    = OpenSSL::X509::Certificate.new(cert_text)

  # signature method
  signature_algorithm     = algorithm(REXML::XPath.first(signed_info_element, "//ds:SignatureMethod", {"ds"=>DSIG}))

  unless cert.public_key.verify(signature_algorithm.new, signature, canon_string)
    return soft ? false : (raise OmniAuth::Strategies::WSFed::ValidationError.new("Key validation error"))
  end

  return true
end

Private Instance Methods

algorithm(element) click to toggle source
# File lib/omniauth/strategies/wsfed/xml_security.rb, line 135
def algorithm(element)
  algorithm = element.attribute("Algorithm").value if element
  algorithm = algorithm && algorithm =~ /sha(.*?)$/i && $1.to_i
  case algorithm
  when 256 then OpenSSL::Digest::SHA256
  when 384 then OpenSSL::Digest::SHA384
  when 512 then OpenSSL::Digest::SHA512
  else
    OpenSSL::Digest::SHA1
  end
end
digests_match?(hash, digest_value) click to toggle source
# File lib/omniauth/strategies/wsfed/xml_security.rb, line 126
def digests_match?(hash, digest_value)
  hash == digest_value
end
extract_signed_element_id() click to toggle source
# File lib/omniauth/strategies/wsfed/xml_security.rb, line 130
def extract_signed_element_id
  reference_element       = REXML::XPath.first(self, "//ds:Signature/ds:SignedInfo/ds:Reference", {"ds"=>DSIG})
  self.signed_element_id  = reference_element.attribute("URI").value unless reference_element.nil?
end