class OmfCommon::Auth::Certificate
Constants
- BEGIN_CERT
- BEGIN_KEY
- DEF_DOMAIN_NAME
- DEF_DURATION
- END_CERT
- END_KEY
Attributes
Public Class Methods
# File lib/omf_common/auth/certificate.rb, line 152 def self._create_digest OpenSSL::Digest::SHA1.new end
Returns an array with a new RSA key and a SHA1 digest
# File lib/omf_common/auth/certificate.rb, line 148 def self._create_key(size = 2048) [OpenSSL::PKey::RSA.new(size), OpenSSL::Digest::SHA1.new] end
Create a X509 certificate
@param [String] address @param [String] subject @param [OpenSSL::PKey::RSA] key @return {cert, key}
# File lib/omf_common/auth/certificate.rb, line 163 def self._create_x509_cert(subject, key, digest = nil, issuer = nil, not_before = Time.now, duration = DEF_DURATION, addresses = []) if key.nil? key, digest = _create_key() else digest = _create_digest end cert = OpenSSL::X509::Certificate.new cert.version = 2 # TODO change serial to non-sequential secure random numbers for production use cert.serial = UUIDTools::UUID.random_create.to_i cert.subject = subject cert.public_key = key.public_key cert.not_before = not_before cert.not_after = not_before + duration.to_i #extensions << ["subjectAltName", "URI:http://foo.com/users/dc766130, URI:frcp:dc766130-c822-11e0-901e-000c29f89f7b@foo.com", false] issuer_cert = issuer ? issuer.to_x509 : cert ef = OpenSSL::X509::ExtensionFactory.new ef.subject_certificate = cert ef.issuer_certificate = issuer_cert unless addresses.empty? cert.add_extension(ef.create_extension("subjectAltName", addresses.join(','), false)) end if issuer cert.issuer = issuer.subject cert.sign(issuer.key, issuer.digest) else # self signed cert.issuer = subject # Not exactly sure if that's the right extensions to add. Copied from # http://www.ruby-doc.org/stdlib-1.9.3/libdoc/openssl/rdoc/OpenSSL/X509/Certificate.html cert.add_extension(ef.create_extension("basicConstraints", "CA:TRUE", true)) cert.add_extension(ef.create_extension("keyUsage", "keyCertSign, cRLSign", true)) cert.add_extension(ef.create_extension("subjectKeyIdentifier", "hash", false)) cert.add_extension(ef.create_extension("authorityKeyIdentifier", "keyid:always", false)) # Signing the cert should be ABSOLUTELY the last step cert.sign(key, digest) end { cert: cert, key: key } end
@param [String] resource_id
unique id of the resource entity @param [String] resource_type type of the resource entity @param [Certificate] Issuer @param [Hash] options @option [Time] :not_before Time the cert will be valid from [now] @option [int] :duration Time in seconds this cert is valid for [3600] @option [OpenSSL::PKey::RSA] :key Key
to encode in cert. If not given, will create a new one @option [String] :user_id ID (should be UUID) for user [UUID(email)] @option [String] :email Email to identify user. If not give, user ‘name’ and @@def_email_domain @option [String] :geni_uri @option [String] :frcp_uri @option [String] :frcp_domain @option [String] :http_uri @option [String] :http_prefix
# File lib/omf_common/auth/certificate.rb, line 56 def self.create_for_resource(resource_id, resource_type, issuer, opts = {}) xname = @@def_x509_name_prefix.dup xname << ['CN', opts[:cn] || resource_id] subject = OpenSSL::X509::Name.new(xname) if key = opts[:key] digest = _create_digest else key, digest = _create_key() end addresses = opts[:addresses] || [] addresses << "URI:uuid:#{opts[:resource_uuid]}" if opts[:resource_uuid] email_domain = opts[:email] ? opts[:email].split('@')[1] : @@def_email_domain addresses << (opts[:geni_uri] || "URI:urn:publicid:IDN+#{email_domain}+#{resource_type}+#{resource_id}") if frcp_uri = opts[:frcp_uri] unless frcp_uri.to_s.start_with? 'URI' frcp_uri = "URI:frcp:#{frcp_uri}" end addresses << frcp_uri end # opts[:frcp_uri] || "URI:frcp:#{user_id}@#{opts[:frcp_domain] || @@def_email_domain}", # opts[:http_uri] || "URI:http://#{opts[:http_prefix] || @@def_email_domain}/users/#{user_id}" not_before = opts[:not_before] || Time.now duration = opts[:duration] || 3600 c = _create_x509_cert(subject, key, digest, issuer, not_before, duration, addresses) c[:addresses] = addresses c[:resource_id] = resource_id c[:subject] = subject self.new c end
Return a newly create certificate with properties token from ‘pem’ encoded string.
@param [String] pem is the PEM encoded content of existing x509 cert @return [Certificate] Certificate
object
# File lib/omf_common/auth/certificate.rb, line 106 def self.create_from_pem(pem_s) state = :seeking cert_pem = [] key_pem = [] end_regexp = /^-*END/ pem_s.each_line do |line| state = :seeking if line.match(end_regexp) case state when :seeking case line when /^-*BEGIN CERTIFICATE/ state = :cert when /^-*BEGIN RSA PRIVATE KEY/ state = :key end when :cert cert_pem << line when :key key_pem << line else raise "BUG: Unknown state '#{state}'" end end # Some command list generated cert can use \r\n as newline char cert_pem = cert_pem.join() unless cert_pem =~ /^-----BEGIN CERTIFICATE-----/ cert_pem = "#{BEGIN_CERT}#{cert_pem.chomp}#{END_CERT}" end opts = {} opts[:cert] = OpenSSL::X509::Certificate.new(cert_pem) if key_pem.size > 0 key_pem = key_pem.join() unless key_pem =~ /^-----BEGIN RSA PRIVATE KEY-----/ key_pem = "#{BEGIN_KEY}#{key_pem.chomp}#{END_KEY}" end opts[:key] = OpenSSL::PKey::RSA.new(key_pem) end self.new(opts) end
# File lib/omf_common/auth/certificate.rb, line 88 def self.create_root(opts = {}) email = opts[:email] ||= "sa@#{@@def_email_domain}" opts = { addresses: [ "email:#{email}" ] }.merge(opts) cert = create_for_resource('sa', :authority, nil, opts) CertificateStore.instance.register_trusted(cert) cert end
# File lib/omf_common/auth/certificate.rb, line 27 def self.default_domain(country, state, organisation, org_unit) @@def_x509_name_prefix = [ ['C', c = country.upcase], ['ST', st = state.upcase], ['O', o = organisation], ['OU', ou = org_unit] ] "/C=#{c}/ST=#{st}/O=#{o}/OU=#{ou}" end
# File lib/omf_common/auth/certificate.rb, line 37 def self.default_email_domain(email_domain) @@def_email_domain = email_domain end
# File lib/omf_common/auth/certificate.rb, line 214 def initialize(opts) if @cert = opts[:cert] @subject = @cert.subject end @resource_id = opts[:resource_id] _extract_addresses(@cert) unless @subject ||= opts[:subject] name = opts[:name] type = opts[:type] domain = opts[:domain] @subject = _create_name(name, type, domain) end if key = opts[:key] @digest = opts[:digest] || self.class._create_digest end if @cert self.key = key if key # this verifies that key is the right one for this cert else #@cert ||= _create_x509_cert(@address, @subject, @key, @digest)[:cert] @cert = self.class._create_x509_cert(@subject, key, @digest)[:cert] @key = key end end
Public Instance Methods
# File lib/omf_common/auth/certificate.rb, line 351 def _extract_addresses(cert) addr = @addresses = {} return unless cert ext = cert.extensions.find { |ext| ext.oid == 'subjectAltName' } return unless ext @address_string = ext.value @addresses_raw = ext.value.split(',').compact @addresses_raw.each do |addr_s| parts = addr_s.split(':') #puts ">>>>>> #{parts}" case parts[0].strip when 'email' addr[:email] = parts[1] when 'URI' if parts[1] == 'urn' parts[2] == 'publicid' ? addr[:geni] = parts[3][4 .. -1] : addr[parts[2].to_sym] = parts[3] else addr[parts[1].to_sym] = parts[2] end else warn "Unknown address type '#{parts[0]}'" end end end
# File lib/omf_common/auth/certificate.rb, line 295 def can_sign? !cert_expired? && !@key.nil? && @key.private? end
# File lib/omf_common/auth/certificate.rb, line 243 def cert_expired? error "Certificate expired!" unless valid? !valid? end
# File lib/omf_common/auth/certificate.rb, line 255 def create_for_resource(resource_id, resource_type, opts = {}) unless valid? raise CertificateNoLongerValidException.new end resource_id ||= UUIDTools::UUID.random_create() unless opts[:resource_uuid] if resource_id.is_a? UUIDTools::UUID opts[:resource_uuid] = resource_id else opts[:resource_uuid] = UUIDTools::UUID.random_create() end end unless opts[:cn] opts[:cn] = "#{resource_id}/type=#{resource_type}" (opts[:cn] += "/uuid=#{opts[:resource_uuid]}") unless resource_id.is_a? UUIDTools::UUID end cert = self.class.create_for_resource(resource_id, resource_type, self, opts) CertificateStore.instance.register(cert) cert end
See create_for_resource
for documentation on ‘opts’
# File lib/omf_common/auth/certificate.rb, line 277 def create_for_user(name, opts = {}) unless valid? raise CertificateNoLongerValidException.new end email = opts[:email] || "#{name}@#{@@def_email_domain}" user_id = opts[:user_id] || UUIDTools::UUID.sha1_create(UUIDTools::UUID_URL_NAMESPACE, email) opts[:cn] = "#{user_id}/emailAddress=#{email}" opts[:addresses] = [ "email:#{email}", ] create_for_resource(user_id, :user, opts) end
Return a hash of some of the key properties of this cert. To get the full monty, use ‘openssl x509 -in xxx.pem -text’
# File lib/omf_common/auth/certificate.rb, line 326 def describe { subject: subject, issuer: @cert.issuer, addresses: addresses, can_sign: can_sign?, root_ca: root_ca?, valid: valid?, valid_period: [@cert.not_before, @cert.not_after] } #(@cert.methods - Object.new.methods).sort end
# File lib/omf_common/auth/certificate.rb, line 248 def key=(key) if @cert && !@cert.check_private_key(key) raise ArgumentError, "Private key provided could not match the public key of given certificate" end @key = key end
# File lib/omf_common/auth/certificate.rb, line 299 def root_ca? subject == @cert.issuer end
# File lib/omf_common/auth/certificate.rb, line 303 def to_pem to_x509.to_pem end
# File lib/omf_common/auth/certificate.rb, line 311 def to_pem_compact to_pem.lines.to_a[1 ... -1].join.strip end
# File lib/omf_common/auth/certificate.rb, line 307 def to_pem_with_key to_x509.to_pem + @key.to_pem end
Will return one of the following
:HS256, :HS384, :HS512, :RS256, :RS384, :RS512, :ES256, :ES384, :ES512
def key_algorithm
end
# File lib/omf_common/auth/certificate.rb, line 347 def to_s "#<#{self.class} subj=#{@subject} can-sign=#{@key != nil}>" end
Return the X509 certificate. If it hasn’t been passed in, return a self-signed one
# File lib/omf_common/auth/certificate.rb, line 291 def to_x509() @cert end
# File lib/omf_common/auth/certificate.rb, line 238 def valid? now = Time.new (@cert.not_before <= now && now <= @cert.not_after) end
# File lib/omf_common/auth/certificate.rb, line 315 def verify_cert if @cert.issuer == self.subject # self signed cert @cert.verify(@cert.public_key) else @cert.verify(CertificateStore.instance.cert_for(@cert.issuer).to_x509.public_key) end end