class OpenID::SimpleSign

Basic implementation of the XML Simple Sign algorithm. Currently only supports RSA-SHA1

Constants

C14N_RAW_OCTETS
NAMESPACES
SIGN_RSA_SHA1

Public Class Methods

parse_certificates(doc) click to toggle source

Extracts the signer’s certificates from the XML

# File lib/gapps_openid.rb, line 264
def self.parse_certificates(doc) 
  certs = []
  REXML::XPath.each(doc, "//ds:Signature/ds:KeyInfo/ds:X509Data/ds:X509Certificate", NAMESPACES ) { | encoded |
    encoded = encoded.text.strip.scan(%r.{1,64}/).join("\n")
    encoded = "-----BEGIN CERTIFICATE-----\n#{encoded}\n-----END CERTIFICATE-----\n"
    cert = OpenSSL::X509::Certificate.new(encoded)
    certs << cert
  }
  return certs
end
store() click to toggle source

Initialize the store

# File lib/gapps_openid.rb, line 252
def self.store
  if @@store.nil?
    OpenID.logger.info("Initializing CA bundle") unless OpenID.logger.nil?        
    ca_bundle_path = File.join(File.dirname(__FILE__), 'ca-bundle.crt')
    @@store = OpenSSL::X509::Store.new
    @@store.set_default_paths
    @@store.add_file(ca_bundle_path)        
  end
  return @@store
end
valid_chain?(chain) click to toggle source

Verifies the chain of trust for the signing certificates

# File lib/gapps_openid.rb, line 276
def self.valid_chain?(chain)
  if chain.nil? or chain.empty?
    return false
  end
  cert = chain.shift
  if self.store.verify(cert)
    return true
  end
  if chain.empty? or not cert.verify(chain.first.public_key)
    return false
  end
  return self.valid_chain?(chain)
end
verify(xml, signature_value) click to toggle source

Verifies the signature of the doc, returning the CN of the signer if valid

# File lib/gapps_openid.rb, line 291
def self.verify(xml, signature_value) 
  doc = REXML::Document.new(xml)

  return nil if REXML::XPath.first(doc, "//ds:Signature").nil? and signature_value.nil?    

  decoded_sig = Base64.decode64(signature_value)
  certs = self.parse_certificates(doc)
  raise "No signature in document" if certs.nil? or certs.empty?
  raise "Missing signature value" if signature_value.nil?


  signing_certificate = certs.first
  raise "Invalid signature" if !signing_certificate.public_key.verify(OpenSSL::Digest::SHA1.new, decoded_sig, xml)
  raise "Certificate chain not valid" if !self.valid_chain?(certs)

  # Signature is valid, return CN of the subject
  subject = signing_certificate.subject.to_a
  signed_by = subject.last[1]
  return signed_by
end