class XMLSecurity::SignedDocument
Attributes
errors[RW]
signed_element_id[RW]
Public Class Methods
new(response, errors = [])
click to toggle source
Calls superclass method
# File lib/xml_security.rb, line 162 def initialize(response, errors = []) super(response) @errors = errors extract_signed_element_id end
Public Instance Methods
validate_document(idp_cert_fingerprint, soft = true)
click to toggle source
# File lib/xml_security.rb, line 168 def validate_document(idp_cert_fingerprint, soft = true) # get cert from response cert_element = REXML::XPath.first(self, "//ds:X509Certificate", { "ds"=>DSIG }) unless cert_element if soft return true # temp if no x509Ceritificate then whitelist this. else raise Samlsso::ValidationError.new("Certificate element missing in response (ds:X509Certificate)") end end base64_cert = cert_element.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 @errors << "Fingerprint mismatch" return soft ? false : (raise Samlsso::ValidationError.new("Fingerprint mismatch")) end validate_signature(base64_cert, soft) end
validate_signature(base64_cert, soft = true)
click to toggle source
# File lib/xml_security.rb, line 193 def validate_signature(base64_cert, soft = true) # validate references # check for inclusive namespaces inclusive_namespaces = extract_inclusive_namespaces document = Nokogiri.parse(self.to_s) # create a working copy so we don't modify the original @working_copy ||= REXML::Document.new(self.to_s).root # store and remove signature node @sig_element ||= begin element = REXML::XPath.first(@working_copy, "//ds:Signature", {"ds"=>DSIG}) element.remove end # verify signature signed_info_element = REXML::XPath.first(@sig_element, "//ds:SignedInfo", {"ds"=>DSIG}) signed_info_element = REXML::XPath.first(@sig_element, "//ds:SignedInfo") unless signed_info_element noko_sig_element = document.at_xpath('//ds:Signature', 'ds' => DSIG) noko_signed_info_element = noko_sig_element.at_xpath('./ds:SignedInfo', 'ds' => DSIG) canon_algorithm = canon_algorithm REXML::XPath.first(@sig_element, '//ds:CanonicalizationMethod', 'ds' => DSIG) canon_string = noko_signed_info_element.canonicalize(canon_algorithm) noko_sig_element.remove # check digests if REXML::XPath.first(@sig_element, "//ds:Reference", {"ds"=>DSIG}) REXML::XPath.each(@sig_element, "//ds:Reference", {"ds"=>DSIG}) do |ref| uri = ref.attributes.get_attribute("URI").value hashed_element = document.at_xpath("//*[@ID='#{uri[1..-1]}']") canon_algorithm = canon_algorithm REXML::XPath.first(ref, '//ds:CanonicalizationMethod', 'ds' => DSIG) canon_hashed_element = hashed_element.canonicalize(canon_algorithm, inclusive_namespaces) digest_algorithm_str = REXML::XPath.first(ref, "//ds:DigestMethod", 'ds' => DSIG) digest_algorithm_str = REXML::XPath.first(ref, "//ds:DigestMethod") unless digest_algorithm_str digest_algorithm = algorithm(digest_algorithm_str) hash = digest_algorithm.digest(canon_hashed_element) base64_digest = REXML::XPath.first(ref, "//ds:DigestValue", {"ds"=>DSIG}) base64_digest = REXML::XPath.first(ref, "//ds:DigestValue") unless base64_digest digest_value = Base64.decode64(base64_digest.text) unless digests_match?(hash, digest_value) @errors << "Digest mismatch" return soft ? false : (raise Samlsso::ValidationError.new("Digest mismatch")) end end else REXML::XPath.each(@sig_element, "//ds:Reference") do |ref| uri = ref.attributes.get_attribute("URI").value hashed_element = document.at_xpath("//*[@ID='#{uri[1..-1]}']") canon_algorithm = canon_algorithm REXML::XPath.first(ref, '//ds:CanonicalizationMethod', 'ds' => DSIG) canon_hashed_element = hashed_element.canonicalize(canon_algorithm, inclusive_namespaces) digest_algorithm_str = REXML::XPath.first(ref, "//ds:DigestMethod", 'ds' => DSIG) digest_algorithm_str = REXML::XPath.first(ref, "//ds:DigestMethod") unless digest_algorithm_str digest_algorithm = algorithm(digest_algorithm_str) hash = digest_algorithm.digest(canon_hashed_element) base64_digest = REXML::XPath.first(ref, "//ds:DigestValue", {"ds"=>DSIG}) base64_digest = REXML::XPath.first(ref, "//ds:DigestValue") unless base64_digest digest_value = Base64.decode64(base64_digest.text) unless digests_match?(hash, digest_value) @errors << "Digest mismatch" return soft ? false : (raise Samlsso::ValidationError.new("Digest mismatch")) end end end base64_signature = REXML::XPath.first(@sig_element, "//ds:SignatureValue", {"ds"=>DSIG}) base64_signature = REXML::XPath.first(@sig_element, "//ds:SignatureValue") unless base64_signature signature = Base64.decode64(base64_signature.text) # get certificate object cert_text = Base64.decode64(base64_cert) cert = OpenSSL::X509::Certificate.new(cert_text) # signature method signature_method = REXML::XPath.first(signed_info_element, "//ds:SignatureMethod", {"ds"=>DSIG}) signature_method = REXML::XPath.first(signed_info_element, "//ds:SignatureMethod") unless signature_method signature_algorithm = algorithm(signature_method) unless cert.public_key.verify(signature_algorithm.new, signature, canon_string) @errors << "Key validation error" return soft ? false : (raise Samlsso::ValidationError.new("Key validation error")) end return true end
Private Instance Methods
digests_match?(hash, digest_value)
click to toggle source
# File lib/xml_security.rb, line 291 def digests_match?(hash, digest_value) hash == digest_value end
extract_inclusive_namespaces()
click to toggle source
# File lib/xml_security.rb, line 300 def extract_inclusive_namespaces if element = REXML::XPath.first(self, "//ec:InclusiveNamespaces", { "ec" => C14N }) prefix_list = element.attributes.get_attribute("PrefixList").value prefix_list.split(" ") else [] end end
extract_signed_element_id()
click to toggle source
# File lib/xml_security.rb, line 295 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[1..-1] unless reference_element.nil? end