aboutsummaryrefslogtreecommitdiff
path: root/vendor/certificate_authority/lib
diff options
context:
space:
mode:
authorelijah <elijah@riseup.net>2012-11-16 14:30:20 -0800
committerelijah <elijah@riseup.net>2012-11-16 14:30:20 -0800
commit76a3a736cfb50cb1c6d926d1e3afb0f504818157 (patch)
tree95df178ce78ba5220eea267bdb21a04f2f975c75 /vendor/certificate_authority/lib
parentbeb6496309b3640d957428b52b4906a1279457ce (diff)
downloadleap_cli-76a3a736cfb50cb1c6d926d1e3afb0f504818157.tar.gz
leap_cli-76a3a736cfb50cb1c6d926d1e3afb0f504818157.tar.bz2
added CSR ability (and vendored certificate_authority gem, so we can get the unreleased fixes we need).
Diffstat (limited to 'vendor/certificate_authority/lib')
-rw-r--r--vendor/certificate_authority/lib/certificate_authority.rb21
-rw-r--r--vendor/certificate_authority/lib/certificate_authority/certificate.rb200
-rw-r--r--vendor/certificate_authority/lib/certificate_authority/certificate_revocation_list.rb77
-rw-r--r--vendor/certificate_authority/lib/certificate_authority/distinguished_name.rb97
-rw-r--r--vendor/certificate_authority/lib/certificate_authority/extensions.rb266
-rw-r--r--vendor/certificate_authority/lib/certificate_authority/key_material.rb148
-rw-r--r--vendor/certificate_authority/lib/certificate_authority/ocsp_handler.rb144
-rw-r--r--vendor/certificate_authority/lib/certificate_authority/pkcs11_key_material.rb65
-rw-r--r--vendor/certificate_authority/lib/certificate_authority/revocable.rb14
-rw-r--r--vendor/certificate_authority/lib/certificate_authority/serial_number.rb10
-rw-r--r--vendor/certificate_authority/lib/certificate_authority/signing_entity.rb16
-rw-r--r--vendor/certificate_authority/lib/certificate_authority/signing_request.rb56
-rw-r--r--vendor/certificate_authority/lib/tasks/certificate_authority.rake23
13 files changed, 1137 insertions, 0 deletions
diff --git a/vendor/certificate_authority/lib/certificate_authority.rb b/vendor/certificate_authority/lib/certificate_authority.rb
new file mode 100644
index 0000000..a697c1b
--- /dev/null
+++ b/vendor/certificate_authority/lib/certificate_authority.rb
@@ -0,0 +1,21 @@
+$:.unshift(File.dirname(__FILE__)) unless $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
+
+#Exterior requirements
+require 'openssl'
+require 'active_model'
+
+#Internal modules
+require 'certificate_authority/signing_entity'
+require 'certificate_authority/revocable'
+require 'certificate_authority/distinguished_name'
+require 'certificate_authority/serial_number'
+require 'certificate_authority/key_material'
+require 'certificate_authority/pkcs11_key_material'
+require 'certificate_authority/extensions'
+require 'certificate_authority/certificate'
+require 'certificate_authority/certificate_revocation_list'
+require 'certificate_authority/ocsp_handler'
+require 'certificate_authority/signing_request'
+
+module CertificateAuthority
+end
diff --git a/vendor/certificate_authority/lib/certificate_authority/certificate.rb b/vendor/certificate_authority/lib/certificate_authority/certificate.rb
new file mode 100644
index 0000000..ca8bc7c
--- /dev/null
+++ b/vendor/certificate_authority/lib/certificate_authority/certificate.rb
@@ -0,0 +1,200 @@
+module CertificateAuthority
+ class Certificate
+ include ActiveModel::Validations
+ include Revocable
+
+ attr_accessor :distinguished_name
+ attr_accessor :serial_number
+ attr_accessor :key_material
+ attr_accessor :not_before
+ attr_accessor :not_after
+ attr_accessor :extensions
+ attr_accessor :openssl_body
+
+ alias :subject :distinguished_name #Same thing as the DN
+
+ attr_accessor :parent
+
+ validate do |certificate|
+ errors.add :base, "Distinguished name must be valid" unless distinguished_name.valid?
+ errors.add :base, "Key material must be valid" unless key_material.valid?
+ errors.add :base, "Serial number must be valid" unless serial_number.valid?
+ errors.add :base, "Extensions must be valid" unless extensions.each do |item|
+ unless item.respond_to?(:valid?)
+ true
+ else
+ item.valid?
+ end
+ end
+ end
+
+ def initialize
+ self.distinguished_name = DistinguishedName.new
+ self.serial_number = SerialNumber.new
+ self.key_material = MemoryKeyMaterial.new
+ self.not_before = Time.now
+ self.not_after = Time.now + 60 * 60 * 24 * 365 #One year
+ self.parent = self
+ self.extensions = load_extensions()
+
+ self.signing_entity = false
+
+ end
+
+ def sign!(signing_profile={})
+ raise "Invalid certificate #{self.errors.full_messages}" unless valid?
+ merge_profile_with_extensions(signing_profile)
+
+ openssl_cert = OpenSSL::X509::Certificate.new
+ openssl_cert.version = 2
+ openssl_cert.not_before = self.not_before
+ openssl_cert.not_after = self.not_after
+ openssl_cert.public_key = self.key_material.public_key
+
+ openssl_cert.serial = self.serial_number.number
+
+ openssl_cert.subject = self.distinguished_name.to_x509_name
+ openssl_cert.issuer = parent.distinguished_name.to_x509_name
+
+ require 'tempfile'
+ t = Tempfile.new("bullshit_conf")
+ # t = File.new("/tmp/openssl.cnf")
+ ## The config requires a file even though we won't use it
+ openssl_config = OpenSSL::Config.new(t.path)
+
+ factory = OpenSSL::X509::ExtensionFactory.new
+ factory.subject_certificate = openssl_cert
+
+ #NB: If the parent doesn't have an SSL body we're making this a self-signed cert
+ if parent.openssl_body.nil?
+ factory.issuer_certificate = openssl_cert
+ else
+ factory.issuer_certificate = parent.openssl_body
+ end
+
+ self.extensions.keys.each do |k|
+ config_extensions = extensions[k].config_extensions
+ openssl_config = merge_options(openssl_config,config_extensions)
+ end
+
+ # p openssl_config.sections
+
+ factory.config = openssl_config
+
+ # Order matters: e.g. for self-signed, subjectKeyIdentifier must come before authorityKeyIdentifier
+ self.extensions.keys.sort{|a,b| b<=>a}.each do |k|
+ e = extensions[k]
+ next if e.to_s.nil? or e.to_s == "" ## If the extension returns an empty string we won't include it
+ ext = factory.create_ext(e.openssl_identifier, e.to_s)
+ openssl_cert.add_extension(ext)
+ end
+
+ if signing_profile["digest"].nil?
+ digest = OpenSSL::Digest::Digest.new("SHA512")
+ else
+ digest = OpenSSL::Digest::Digest.new(signing_profile["digest"])
+ end
+ self.openssl_body = openssl_cert.sign(parent.key_material.private_key,digest)
+ t.close! if t.is_a?(Tempfile)# We can get rid of the ridiculous temp file
+ self.openssl_body
+ end
+
+ def is_signing_entity?
+ self.extensions["basicConstraints"].ca
+ end
+
+ def signing_entity=(signing)
+ self.extensions["basicConstraints"].ca = signing
+ end
+
+ def revoked?
+ !self.revoked_at.nil?
+ end
+
+ def to_pem
+ raise "Certificate has no signed body" if self.openssl_body.nil?
+ self.openssl_body.to_pem
+ end
+
+ def is_root_entity?
+ self.parent == self && is_signing_entity?
+ end
+
+ def is_intermediate_entity?
+ (self.parent != self) && is_signing_entity?
+ end
+
+ private
+
+ def merge_profile_with_extensions(signing_profile={})
+ return self.extensions if signing_profile["extensions"].nil?
+ signing_config = signing_profile["extensions"]
+ signing_config.keys.each do |k|
+ extension = self.extensions[k]
+ items = signing_config[k]
+ items.keys.each do |profile_item_key|
+ if extension.respond_to?("#{profile_item_key}=".to_sym)
+ extension.send("#{profile_item_key}=".to_sym, items[profile_item_key] )
+ else
+ p "Tried applying '#{profile_item_key}' to #{extension.class} but it doesn't respond!"
+ end
+ end
+ end
+ end
+
+ def load_extensions
+ extension_hash = {}
+
+ temp_extensions = []
+ basic_constraints = CertificateAuthority::Extensions::BasicConstraints.new
+ temp_extensions << basic_constraints
+ crl_distribution_points = CertificateAuthority::Extensions::CrlDistributionPoints.new
+ temp_extensions << crl_distribution_points
+ subject_key_identifier = CertificateAuthority::Extensions::SubjectKeyIdentifier.new
+ temp_extensions << subject_key_identifier
+ authority_key_identifier = CertificateAuthority::Extensions::AuthorityKeyIdentifier.new
+ temp_extensions << authority_key_identifier
+ authority_info_access = CertificateAuthority::Extensions::AuthorityInfoAccess.new
+ temp_extensions << authority_info_access
+ key_usage = CertificateAuthority::Extensions::KeyUsage.new
+ temp_extensions << key_usage
+ extended_key_usage = CertificateAuthority::Extensions::ExtendedKeyUsage.new
+ temp_extensions << extended_key_usage
+ subject_alternative_name = CertificateAuthority::Extensions::SubjectAlternativeName.new
+ temp_extensions << subject_alternative_name
+ certificate_policies = CertificateAuthority::Extensions::CertificatePolicies.new
+ temp_extensions << certificate_policies
+
+ temp_extensions.each do |extension|
+ extension_hash[extension.openssl_identifier] = extension
+ end
+
+ extension_hash
+ end
+
+ def merge_options(config,hash)
+ hash.keys.each do |k|
+ config[k] = hash[k]
+ end
+ config
+ end
+
+ def self.from_openssl openssl_cert
+ unless openssl_cert.is_a? OpenSSL::X509::Certificate
+ raise "Can only construct from an OpenSSL::X509::Certificate"
+ end
+
+ certificate = Certificate.new
+ # Only subject, key_material, and body are used for signing
+ certificate.distinguished_name = DistinguishedName.from_openssl openssl_cert.subject
+ certificate.key_material.public_key = openssl_cert.public_key
+ certificate.openssl_body = openssl_cert
+ certificate.serial_number.number = openssl_cert.serial.to_i
+ certificate.not_before = openssl_cert.not_before
+ certificate.not_after = openssl_cert.not_after
+ # TODO extensions
+ certificate
+ end
+
+ end
+end
diff --git a/vendor/certificate_authority/lib/certificate_authority/certificate_revocation_list.rb b/vendor/certificate_authority/lib/certificate_authority/certificate_revocation_list.rb
new file mode 100644
index 0000000..e222e26
--- /dev/null
+++ b/vendor/certificate_authority/lib/certificate_authority/certificate_revocation_list.rb
@@ -0,0 +1,77 @@
+module CertificateAuthority
+ class CertificateRevocationList
+ include ActiveModel::Validations
+
+ attr_accessor :certificates
+ attr_accessor :parent
+ attr_accessor :crl_body
+ attr_accessor :next_update
+
+ validate do |crl|
+ errors.add :next_update, "Next update must be a positive value" if crl.next_update < 0
+ errors.add :parent, "A parent entity must be set" if crl.parent.nil?
+ end
+
+ def initialize
+ self.certificates = []
+ self.next_update = 60 * 60 * 4 # 4 hour default
+ end
+
+ def <<(revocable)
+ case revocable
+ when Revocable
+ raise "Only revoked entities can be added to a CRL" unless revocable.revoked?
+ self.certificates << revocable
+ when OpenSSL::X509::Certificate
+ raise "Not implemented yet"
+ else
+ raise "#{revocable.class} cannot be included in a CRL"
+ end
+ end
+
+ def sign!(signing_profile={})
+ raise "No parent entity has been set!" if self.parent.nil?
+ raise "Invalid CRL" unless self.valid?
+
+ revocations = self.certificates.collect do |revocable|
+ revocation = OpenSSL::X509::Revoked.new
+
+ ## We really just need a serial number, now we have to dig it out
+ case revocable
+ when Certificate
+ x509_cert = OpenSSL::X509::Certificate.new(revocable.to_pem)
+ revocation.serial = x509_cert.serial
+ when SerialNumber
+ revocation.serial = revocable.number
+ end
+ revocation.time = revocable.revoked_at
+ revocation
+ end
+
+ crl = OpenSSL::X509::CRL.new
+ revocations.each do |revocation|
+ crl.add_revoked(revocation)
+ end
+
+ crl.version = 1
+ crl.last_update = Time.now
+ crl.next_update = Time.now + self.next_update
+
+ signing_cert = OpenSSL::X509::Certificate.new(self.parent.to_pem)
+ if signing_profile["digest"].nil?
+ digest = OpenSSL::Digest::Digest.new("SHA512")
+ else
+ digest = OpenSSL::Digest::Digest.new(signing_profile["digest"])
+ end
+ crl.issuer = signing_cert.subject
+ self.crl_body = crl.sign(self.parent.key_material.private_key, digest)
+
+ self.crl_body
+ end
+
+ def to_pem
+ raise "No signed CRL body" if self.crl_body.nil?
+ self.crl_body.to_pem
+ end
+ end#CertificateRevocationList
+end
diff --git a/vendor/certificate_authority/lib/certificate_authority/distinguished_name.rb b/vendor/certificate_authority/lib/certificate_authority/distinguished_name.rb
new file mode 100644
index 0000000..165fe29
--- /dev/null
+++ b/vendor/certificate_authority/lib/certificate_authority/distinguished_name.rb
@@ -0,0 +1,97 @@
+module CertificateAuthority
+ class DistinguishedName
+ include ActiveModel::Validations
+
+ validates_presence_of :common_name
+
+ attr_accessor :common_name
+ alias :cn :common_name
+ alias :cn= :common_name=
+
+ attr_accessor :locality
+ alias :l :locality
+ alias :l= :locality=
+
+ attr_accessor :state
+ alias :s :state
+ alias :st= :state=
+
+ attr_accessor :country
+ alias :c :country
+ alias :c= :country=
+
+ attr_accessor :organization
+ alias :o :organization
+ alias :o= :organization=
+
+ attr_accessor :organizational_unit
+ alias :ou :organizational_unit
+ alias :ou= :organizational_unit=
+
+ attr_accessor :email_address
+ alias :emailAddress :email_address
+ alias :emailAddress= :email_address=
+
+ def to_x509_name
+ raise "Invalid Distinguished Name" unless valid?
+
+ # NB: the capitalization in the strings counts
+ name = OpenSSL::X509::Name.new
+ name.add_entry("C", country) unless country.blank?
+ name.add_entry("ST", state) unless state.blank?
+ name.add_entry("L", locality) unless locality.blank?
+ name.add_entry("O", organization) unless organization.blank?
+ name.add_entry("OU", organizational_unit) unless organizational_unit.blank?
+ name.add_entry("CN", common_name)
+ name.add_entry("emailAddress", email_address) unless email_address.blank?
+ name
+ end
+
+ def ==(other)
+ # Use the established OpenSSL comparison
+ self.to_x509_name() == other.to_x509_name()
+ end
+
+ def self.from_openssl openssl_name
+ unless openssl_name.is_a? OpenSSL::X509::Name
+ raise "Argument must be a OpenSSL::X509::Name"
+ end
+
+ WrappedDistinguishedName.new(openssl_name)
+ end
+ end
+
+ ## This is a significantly more complicated case. It's possible that
+ ## generically handled certificates will include custom OIDs in the
+ ## subject.
+ class WrappedDistinguishedName < DistinguishedName
+ attr_accessor :x509_name
+
+ def initialize(x509_name)
+ @x509_name = x509_name
+
+ subject = @x509_name.to_a
+ subject.each do |element|
+ field = element[0].downcase
+ value = element[1]
+ #type = element[2] ## -not used
+ method_sym = "#{field}=".to_sym
+ if self.respond_to?(method_sym)
+ self.send("#{field}=",value)
+ else
+ ## Custom OID
+ @custom_oids = true
+ end
+ end
+
+ end
+
+ def to_x509_name
+ @x509_name
+ end
+
+ def custom_oids?
+ @custom_oids
+ end
+ end
+end
diff --git a/vendor/certificate_authority/lib/certificate_authority/extensions.rb b/vendor/certificate_authority/lib/certificate_authority/extensions.rb
new file mode 100644
index 0000000..e5a8e85
--- /dev/null
+++ b/vendor/certificate_authority/lib/certificate_authority/extensions.rb
@@ -0,0 +1,266 @@
+module CertificateAuthority
+ module Extensions
+ module ExtensionAPI
+ def to_s
+ raise "Implementation required"
+ end
+
+ def config_extensions
+ {}
+ end
+
+ def openssl_identifier
+ raise "Implementation required"
+ end
+ end
+
+ class BasicConstraints
+ include ExtensionAPI
+ include ActiveModel::Validations
+ attr_accessor :ca
+ attr_accessor :path_len
+ validates :ca, :inclusion => [true,false]
+
+ def initialize
+ self.ca = false
+ end
+
+ def is_ca?
+ self.ca
+ end
+
+ def path_len=(value)
+ raise "path_len must be a non-negative integer" if value < 0 or !value.is_a?(Fixnum)
+ @path_len = value
+ end
+
+ def openssl_identifier
+ "basicConstraints"
+ end
+
+ def to_s
+ result = ""
+ result += "CA:#{self.ca}"
+ result += ",pathlen:#{self.path_len}" unless self.path_len.nil?
+ result
+ end
+ end
+
+ class CrlDistributionPoints
+ include ExtensionAPI
+
+ attr_accessor :uri
+
+ def initialize
+ # self.uri = "http://moo.crlendPoint.example.com/something.crl"
+ end
+
+ def openssl_identifier
+ "crlDistributionPoints"
+ end
+
+ ## NB: At this time it seems OpenSSL's extension handlers don't support
+ ## any of the config options the docs claim to support... everything comes back
+ ## "missing value" on GENERAL NAME. Even if copied verbatim
+ def config_extensions
+ {
+ # "custom_crl_fields" => {"fullname" => "URI:#{fullname}"},
+ # "issuer_sect" => {"CN" => "crlissuer.com", "C" => "US", "O" => "shudder"}
+ }
+ end
+
+ def to_s
+ return "" if self.uri.nil?
+ "URI:#{self.uri}"
+ end
+ end
+
+ class SubjectKeyIdentifier
+ include ExtensionAPI
+ def openssl_identifier
+ "subjectKeyIdentifier"
+ end
+
+ def to_s
+ "hash"
+ end
+ end
+
+ class AuthorityKeyIdentifier
+ include ExtensionAPI
+
+ def openssl_identifier
+ "authorityKeyIdentifier"
+ end
+
+ def to_s
+ "keyid,issuer"
+ end
+ end
+
+ class AuthorityInfoAccess
+ include ExtensionAPI
+
+ attr_accessor :ocsp
+
+ def initialize
+ self.ocsp = []
+ end
+
+ def openssl_identifier
+ "authorityInfoAccess"
+ end
+
+ def to_s
+ return "" if self.ocsp.empty?
+ "OCSP;URI:#{self.ocsp}"
+ end
+ end
+
+ class KeyUsage
+ include ExtensionAPI
+
+ attr_accessor :usage
+
+ def initialize
+ self.usage = ["digitalSignature", "nonRepudiation"]
+ end
+
+ def openssl_identifier
+ "keyUsage"
+ end
+
+ def to_s
+ "#{self.usage.join(',')}"
+ end
+ end
+
+ class ExtendedKeyUsage
+ include ExtensionAPI
+
+ attr_accessor :usage
+
+ def initialize
+ self.usage = ["serverAuth","clientAuth"]
+ end
+
+ def openssl_identifier
+ "extendedKeyUsage"
+ end
+
+ def to_s
+ "#{self.usage.join(',')}"
+ end
+ end
+
+ class SubjectAlternativeName
+ include ExtensionAPI
+
+ attr_accessor :uris, :dns_names, :ips
+
+ def initialize
+ self.uris = []
+ self.dns_names = []
+ self.ips = []
+ end
+
+ def uris=(value)
+ raise "URIs must be an array" unless value.is_a?(Array)
+ @uris = value
+ end
+
+ def dns_names=(value)
+ raise "DNS names must be an array" unless value.is_a?(Array)
+ @dns_names = value
+ end
+
+ def ips=(value)
+ raise "IPs must be an array" unless value.is_a?(Array)
+ @ips = value
+ end
+
+ def openssl_identifier
+ "subjectAltName"
+ end
+
+ def to_s
+ res = self.uris.map {|u| "URI:#{u}" }
+ res += self.dns_names.map {|d| "DNS:#{d}" }
+ res += self.ips.map {|i| "IP:#{i}" }
+
+ return res.join(',')
+ end
+ end
+
+ class CertificatePolicies
+ include ExtensionAPI
+
+ attr_accessor :policy_identifier
+ attr_accessor :cps_uris
+ ##User notice
+ attr_accessor :explicit_text
+ attr_accessor :organization
+ attr_accessor :notice_numbers
+
+ def initialize
+ @contains_data = false
+ end
+
+
+ def openssl_identifier
+ "certificatePolicies"
+ end
+
+ def user_notice=(value={})
+ value.keys.each do |key|
+ self.send("#{key}=".to_sym, value[key])
+ end
+ end
+
+ def config_extensions
+ config_extension = {}
+ custom_policies = {}
+ notice = {}
+ unless self.policy_identifier.nil?
+ custom_policies["policyIdentifier"] = self.policy_identifier
+ end
+
+ if !self.cps_uris.nil? and self.cps_uris.is_a?(Array)
+ self.cps_uris.each_with_index do |cps_uri,i|
+ custom_policies["CPS.#{i}"] = cps_uri
+ end
+ end
+
+ unless self.explicit_text.nil?
+ notice["explicitText"] = self.explicit_text
+ end
+
+ unless self.organization.nil?
+ notice["organization"] = self.organization
+ end
+
+ unless self.notice_numbers.nil?
+ notice["noticeNumbers"] = self.notice_numbers
+ end
+
+ if notice.keys.size > 0
+ custom_policies["userNotice.1"] = "@notice"
+ config_extension["notice"] = notice
+ end
+
+ if custom_policies.keys.size > 0
+ config_extension["custom_policies"] = custom_policies
+ @contains_data = true
+ end
+
+ config_extension
+ end
+
+ def to_s
+ return "" unless @contains_data
+ "ia5org,@custom_policies"
+ end
+ end
+
+ end
+end
diff --git a/vendor/certificate_authority/lib/certificate_authority/key_material.rb b/vendor/certificate_authority/lib/certificate_authority/key_material.rb
new file mode 100644
index 0000000..75ec62e
--- /dev/null
+++ b/vendor/certificate_authority/lib/certificate_authority/key_material.rb
@@ -0,0 +1,148 @@
+module CertificateAuthority
+ module KeyMaterial
+ def public_key
+ raise "Required implementation"
+ end
+
+ def private_key
+ raise "Required implementation"
+ end
+
+ def is_in_hardware?
+ raise "Required implementation"
+ end
+
+ def is_in_memory?
+ raise "Required implementation"
+ end
+
+ def self.from_x509_key_pair(pair,password=nil)
+ if password.nil?
+ key = OpenSSL::PKey::RSA.new(pair)
+ else
+ key = OpenSSL::PKey::RSA.new(pair,password)
+ end
+ mem_key = MemoryKeyMaterial.new
+ mem_key.public_key = key.public_key
+ mem_key.private_key = key
+ mem_key
+ end
+
+ def self.from_x509_public_key(public_key_pem)
+ key = OpenSSL::PKey::RSA.new(public_key_pem)
+ signing_request_key = SigningRequestKeyMaterial.new
+ signing_request_key.public_key = key.public_key
+ signing_request_key
+ end
+ end
+
+ class MemoryKeyMaterial
+ include KeyMaterial
+ include ActiveModel::Validations
+
+ attr_accessor :keypair
+ attr_accessor :private_key
+ attr_accessor :public_key
+
+ def initialize
+ end
+
+ validates_each :private_key do |record, attr, value|
+ record.errors.add :private_key, "cannot be blank" if record.private_key.nil?
+ end
+ validates_each :public_key do |record, attr, value|
+ record.errors.add :public_key, "cannot be blank" if record.public_key.nil?
+ end
+
+ def is_in_hardware?
+ false
+ end
+
+ def is_in_memory?
+ true
+ end
+
+ def generate_key(modulus_bits=2048)
+ self.keypair = OpenSSL::PKey::RSA.new(modulus_bits)
+ self.private_key = keypair
+ self.public_key = keypair.public_key
+ self.keypair
+ end
+
+ def private_key
+ @private_key
+ end
+
+ def public_key
+ @public_key
+ end
+ end
+
+ class SigningRequestKeyMaterial
+ include KeyMaterial
+ include ActiveModel::Validations
+
+ validates_each :public_key do |record, attr, value|
+ record.errors.add :public_key, "cannot be blank" if record.public_key.nil?
+ end
+
+ attr_accessor :public_key
+
+ def initialize(request=nil)
+ if request.is_a? OpenSSL::X509::Request
+ raise "Invalid certificate signing request" unless request.verify request.public_key
+ self.public_key = request.public_key
+ end
+ end
+
+ def is_in_hardware?
+ false
+ end
+
+ def is_in_memory?
+ true
+ end
+
+ def private_key
+ nil
+ end
+
+ def public_key
+ @public_key
+ end
+ end
+
+ class SigningRequestKeyMaterial
+ include KeyMaterial
+ include ActiveModel::Validations
+
+ validates_each :public_key do |record, attr, value|
+ record.errors.add :public_key, "cannot be blank" if record.public_key.nil?
+ end
+
+ attr_accessor :public_key
+
+ def initialize(request=nil)
+ if request.is_a? OpenSSL::X509::Request
+ raise "Invalid certificate signing request" unless request.verify request.public_key
+ self.public_key = request.public_key
+ end
+ end
+
+ def is_in_hardware?
+ false
+ end
+
+ def is_in_memory?
+ true
+ end
+
+ def private_key
+ nil
+ end
+
+ def public_key
+ @public_key
+ end
+ end
+end
diff --git a/vendor/certificate_authority/lib/certificate_authority/ocsp_handler.rb b/vendor/certificate_authority/lib/certificate_authority/ocsp_handler.rb
new file mode 100644
index 0000000..e101f98
--- /dev/null
+++ b/vendor/certificate_authority/lib/certificate_authority/ocsp_handler.rb
@@ -0,0 +1,144 @@
+module CertificateAuthority
+ class OCSPResponseBuilder
+ attr_accessor :ocsp_response
+ attr_accessor :verification_mechanism
+ attr_accessor :ocsp_request_reader
+ attr_accessor :parent
+ attr_accessor :next_update
+
+ GOOD = OpenSSL::OCSP::V_CERTSTATUS_GOOD
+ REVOKED = OpenSSL::OCSP::V_CERTSTATUS_REVOKED
+
+ NO_REASON=0
+ KEY_COMPROMISED=OpenSSL::OCSP::REVOKED_STATUS_KEYCOMPROMISE
+ UNSPECIFIED=OpenSSL::OCSP::REVOKED_STATUS_UNSPECIFIED
+
+ def build_response()
+ raise "Requires a parent for signing" if @parent.nil?
+ if @verification_mechanism.nil?
+ ## If no verification callback is provided we're marking it GOOD
+ @verification_mechanism = lambda {|cert_id| [GOOD,NO_REASON] }
+ end
+
+ @ocsp_request_reader.ocsp_request.certid.each do |cert_id|
+ result,reason = verification_mechanism.call(cert_id.serial)
+
+ ## cert_id, status, reason, rev_time, this update, next update, ext
+ ## - unit of time is seconds
+ ## - rev_time is currently set to "now"
+ @ocsp_response.add_status(cert_id,
+ result, reason,
+ 0, 0, @next_update, nil)
+ end
+
+ @ocsp_response.sign(OpenSSL::X509::Certificate.new(@parent.to_pem), @parent.key_material.private_key, nil, nil)
+ OpenSSL::OCSP::Response.create(OpenSSL::OCSP::RESPONSE_STATUS_SUCCESSFUL, @ocsp_response)
+ end
+
+ def self.from_request_reader(request_reader,verification_mechanism=nil)
+ response_builder = OCSPResponseBuilder.new
+ response_builder.ocsp_request_reader = request_reader
+
+ ocsp_response = OpenSSL::OCSP::BasicResponse.new
+ ocsp_response.copy_nonce(request_reader.ocsp_request)
+ response_builder.ocsp_response = ocsp_response
+ response_builder.next_update = 60*15 #Default of 15 minutes
+ response_builder
+ end
+ end
+
+ class OCSPRequestReader
+ attr_accessor :raw_ocsp_request
+ attr_accessor :ocsp_request
+
+ def serial_numbers
+ @ocsp_request.certid.collect do |cert_id|
+ cert_id.serial
+ end
+ end
+
+ def self.from_der(request_body)
+ reader = OCSPRequestReader.new
+ reader.raw_ocsp_request = request_body
+ reader.ocsp_request = OpenSSL::OCSP::Request.new(request_body)
+
+ reader
+ end
+ end
+
+ ## DEPRECATED
+ class OCSPHandler
+ include ActiveModel::Validations
+
+ attr_accessor :ocsp_request
+ attr_accessor :certificate_ids
+
+ attr_accessor :certificates
+ attr_accessor :parent
+
+ attr_accessor :ocsp_response_body
+
+ validate do |crl|
+ errors.add :parent, "A parent entity must be set" if parent.nil?
+ end
+ validate :all_certificates_available
+
+ def initialize
+ self.certificates = {}
+ end
+
+ def <<(cert)
+ self.certificates[cert.serial_number.number.to_s] = cert
+ end
+
+ def extract_certificate_serials
+ openssl_request = OpenSSL::OCSP::Request.new(@ocsp_request)
+
+ self.certificate_ids = openssl_request.certid.collect do |cert_id|
+ cert_id.serial
+ end
+
+ self.certificate_ids
+ end
+
+
+ def response
+ raise "Invalid response" unless valid?
+
+ openssl_ocsp_response = OpenSSL::OCSP::BasicResponse.new
+ openssl_ocsp_request = OpenSSL::OCSP::Request.new(self.ocsp_request)
+ openssl_ocsp_response.copy_nonce(openssl_ocsp_request)
+
+ openssl_ocsp_request.certid.each do |cert_id|
+ certificate = self.certificates[cert_id.serial.to_s]
+
+ openssl_ocsp_response.add_status(cert_id,
+ OpenSSL::OCSP::V_CERTSTATUS_GOOD, 0,
+ 0, 0, 30, nil)
+ end
+
+
+ openssl_ocsp_response.sign(OpenSSL::X509::Certificate.new(self.parent.to_pem), self.parent.key_material.private_key, nil, nil)
+ final_response = OpenSSL::OCSP::Response.create(OpenSSL::OCSP::RESPONSE_STATUS_SUCCESSFUL, openssl_ocsp_response)
+ self.ocsp_response_body = final_response
+ self.ocsp_response_body
+ end
+
+ def to_der
+ raise "No signed OCSP response body available" if self.ocsp_response_body.nil?
+ self.ocsp_response_body.to_der
+ end
+
+ private
+
+ def all_certificates_available
+ openssl_ocsp_request = OpenSSL::OCSP::Request.new(self.ocsp_request)
+
+ openssl_ocsp_request.certid.each do |cert_id|
+ certificate = self.certificates[cert_id.serial.to_s]
+ errors.add(:base, "Certificate #{cert_id.serial} has not been added yet") if certificate.nil?
+ end
+ end
+
+ end
+end
diff --git a/vendor/certificate_authority/lib/certificate_authority/pkcs11_key_material.rb b/vendor/certificate_authority/lib/certificate_authority/pkcs11_key_material.rb
new file mode 100644
index 0000000..d4ebc47
--- /dev/null
+++ b/vendor/certificate_authority/lib/certificate_authority/pkcs11_key_material.rb
@@ -0,0 +1,65 @@
+module CertificateAuthority
+ class Pkcs11KeyMaterial
+ include KeyMaterial
+ include ActiveModel::Validations
+ include ActiveModel::Serialization
+
+ attr_accessor :engine
+ attr_accessor :token_id
+ attr_accessor :pkcs11_lib
+ attr_accessor :openssl_pkcs11_engine_lib
+ attr_accessor :pin
+
+ def initialize(attributes = {})
+ @attributes = attributes
+ initialize_engine
+ end
+
+ def is_in_hardware?
+ true
+ end
+
+ def is_in_memory?
+ false
+ end
+
+ def generate_key(modulus_bits=1024)
+ puts "Key generation is not currently supported in hardware"
+ nil
+ end
+
+ def private_key
+ initialize_engine
+ self.engine.load_private_key(self.token_id)
+ end
+
+ def public_key
+ initialize_engine
+ self.engine.load_public_key(self.token_id)
+ end
+
+ private
+
+ def initialize_engine
+ ## We're going to return early and try again later if params weren't passed in
+ ## at initialization. Any attempt at getting a public/private key will try
+ ## again.
+ return false if self.openssl_pkcs11_engine_lib.nil? or self.pkcs11_lib.nil?
+ return self.engine unless self.engine.nil?
+ OpenSSL::Engine.load
+
+ pkcs11 = OpenSSL::Engine.by_id("dynamic") do |e|
+ e.ctrl_cmd("SO_PATH",self.openssl_pkcs11_engine_lib)
+ e.ctrl_cmd("ID","pkcs11")
+ e.ctrl_cmd("LIST_ADD","1")
+ e.ctrl_cmd("LOAD")
+ e.ctrl_cmd("PIN",self.pin) unless self.pin.nil? or self.pin == ""
+ e.ctrl_cmd("MODULE_PATH",self.pkcs11_lib)
+ end
+
+ self.engine = pkcs11
+ pkcs11
+ end
+
+ end
+end
diff --git a/vendor/certificate_authority/lib/certificate_authority/revocable.rb b/vendor/certificate_authority/lib/certificate_authority/revocable.rb
new file mode 100644
index 0000000..eba5d98
--- /dev/null
+++ b/vendor/certificate_authority/lib/certificate_authority/revocable.rb
@@ -0,0 +1,14 @@
+module CertificateAuthority
+ module Revocable
+ attr_accessor :revoked_at
+
+ def revoke!(time=Time.now)
+ @revoked_at = time
+ end
+
+ def revoked?
+ # If we have a time, then we're revoked
+ !@revoked_at.nil?
+ end
+ end
+end
diff --git a/vendor/certificate_authority/lib/certificate_authority/serial_number.rb b/vendor/certificate_authority/lib/certificate_authority/serial_number.rb
new file mode 100644
index 0000000..ec0b836
--- /dev/null
+++ b/vendor/certificate_authority/lib/certificate_authority/serial_number.rb
@@ -0,0 +1,10 @@
+module CertificateAuthority
+ class SerialNumber
+ include ActiveModel::Validations
+ include Revocable
+
+ attr_accessor :number
+
+ validates :number, :presence => true, :numericality => {:greater_than => 0}
+ end
+end
diff --git a/vendor/certificate_authority/lib/certificate_authority/signing_entity.rb b/vendor/certificate_authority/lib/certificate_authority/signing_entity.rb
new file mode 100644
index 0000000..748350b
--- /dev/null
+++ b/vendor/certificate_authority/lib/certificate_authority/signing_entity.rb
@@ -0,0 +1,16 @@
+module CertificateAuthority
+ module SigningEntity
+
+ def self.included(mod)
+ mod.class_eval do
+ attr_accessor :signing_entity
+ end
+ end
+
+ def signing_entity=(val)
+ raise "invalid param" unless [true,false].include?(val)
+ @signing_entity = val
+ end
+
+ end
+end
diff --git a/vendor/certificate_authority/lib/certificate_authority/signing_request.rb b/vendor/certificate_authority/lib/certificate_authority/signing_request.rb
new file mode 100644
index 0000000..590d5be
--- /dev/null
+++ b/vendor/certificate_authority/lib/certificate_authority/signing_request.rb
@@ -0,0 +1,56 @@
+module CertificateAuthority
+ class SigningRequest
+ attr_accessor :distinguished_name
+ attr_accessor :key_material
+ attr_accessor :raw_body
+ attr_accessor :openssl_csr
+ attr_accessor :digest
+
+ def to_cert
+ cert = Certificate.new
+ if !@distinguished_name.nil?
+ cert.distinguished_name = @distinguished_name
+ end
+ cert.key_material = @key_material
+ cert
+ end
+
+ def to_pem
+ to_x509_csr.to_pem
+ end
+
+ def to_x509_csr
+ raise "Must specify a DN/subject on csr" if @distinguished_name.nil?
+ raise "Invalid DN in request" unless @distinguished_name.valid?
+ raise "CSR must have key material" if @key_material.nil?
+ raise "CSR must include a public key on key material" if @key_material.public_key.nil?
+
+ opensslcsr = OpenSSL::X509::Request.new
+ opensslcsr.subject = @distinguished_name.to_x509_name
+ opensslcsr.public_key = @key_material.public_key
+ opensslcsr.sign @key_material.private_key, OpenSSL::Digest::Digest.new(@digest || "SHA512")
+ opensslcsr
+ end
+
+ def self.from_x509_csr(raw_csr)
+ csr = SigningRequest.new
+ openssl_csr = OpenSSL::X509::Request.new(raw_csr)
+ csr.distinguished_name = DistinguishedName.from_openssl openssl_csr.subject
+ csr.raw_body = raw_csr
+ csr.openssl_csr = openssl_csr
+ key_material = SigningRequestKeyMaterial.new
+ key_material.public_key = openssl_csr.public_key
+ csr.key_material = key_material
+ csr
+ end
+
+ def self.from_netscape_spkac(raw_spkac)
+ openssl_spkac = OpenSSL::Netscape::SPKI.new raw_spkac
+ csr = SigningRequest.new
+ csr.raw_body = raw_spkac
+ key_material = SigningRequestKeyMaterial.new
+ key_material.public_key = openssl_spkac.public_key
+ csr
+ end
+ end
+end \ No newline at end of file
diff --git a/vendor/certificate_authority/lib/tasks/certificate_authority.rake b/vendor/certificate_authority/lib/tasks/certificate_authority.rake
new file mode 100644
index 0000000..e7d5bf9
--- /dev/null
+++ b/vendor/certificate_authority/lib/tasks/certificate_authority.rake
@@ -0,0 +1,23 @@
+require 'certificate_authority'
+
+namespace :certificate_authority do
+ desc "Generate a quick self-signed cert"
+ task :self_signed do
+
+ cn = "http://localhost"
+ cn = ENV['DOMAIN'] unless ENV['DOMAIN'].nil?
+
+ root = CertificateAuthority::Certificate.new
+ root.subject.common_name= cn
+ root.key_material.generate_key
+ root.signing_entity = true
+ root.valid?
+ root.sign!
+
+ print "Your cert for #{cn}\n"
+ print root.to_pem
+
+ print "Your private key\n"
+ print root.key_material.private_key.to_pem
+ end
+end