Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- require 'openssl'
- class QuickCert
- VERSION = "1.0.2"
- ##
- # Creates a new QuickCert instance using the Certificate
- # Authority described in +cfg+. If there is no CA at
- # cfg[:certificate_authority_dir], then QuickCert will initialize a new one.
- def initialize(new_certificate_opts_authority)
- @new_certificate_opts_authority = new_certificate_opts_authority
- create_certificate_authority
- end
- ##
- # Creates a new Certificate Authority from @new_certificate_opts_authority if it
- # does not already exist at new_certificate_opts_authority[:certificate_authority_dir].
- def create_certificate_authority
- return if File.exists? @new_certificate_opts_authority[:certificate_authority_dir]
- Dir.mkdir @new_certificate_opts_authority[:certificate_authority_dir]
- Dir.mkdir(File.join(@new_certificate_opts_authority[:certificate_authority_dir], 'private'), 0700)
- @new_certificate_opts_authority[:new_certificates_dir] = File.join(@new_certificate_opts_authority[:certificate_authority_dir], 'newcertificates')
- Dir.mkdir(@new_certificate_opts_authority[:new_certificates_dir])
- Dir.mkdir(File.join(@new_certificate_opts_authority[:certificate_authority_dir], 'certificate_revokation_list'))
- File.open @new_certificate_opts_authority[:serial_file], 'w' do |f| f << '1' end
- puts "Generating CA keypair" if $DEBUG
- keypair = OpenSSL::PKey::RSA.new @new_certificate_opts_authority[:ca_rsa_key_length]
- certificate = OpenSSL::X509::Certificate.new
- name = @new_certificate_opts_authority[:name].dup << ['CN', 'CA']
- certificate.subject = certificate.issuer = OpenSSL::X509::Name.new(name)
- certificate.not_before = Time.now
- certificate.not_after = Time.now + @new_certificate_opts_authority[:ca_cert_days] * 24 * 60 * 60
- certificate.public_key = keypair.public_key
- certificate.serial = 12345
- certificate.version = 2 # X509v3
- ef = OpenSSL::X509::ExtensionFactory.new
- ef.subject_certificate = certificate
- ef.issuer_certificate = certificate
- certificate.extensions = [
- ef.create_extension("basicConstraints","CA:TRUE", true),
- ef.create_extension("nsComment","Ruby/OpenSSL Generated Certificate"),
- ef.create_extension("subjectKeyIdentifier", "hash"),
- ef.create_extension("keyUsage", "cRLSign,keyCertSign", true),
- ef.create_extension("authorityKeyIdentifier", "keyid:always, issuer:always")
- ]
- # certificate.extensions.each {|e| puts e}
- # abort
- # cert.addExtension(ext)
- # certificate.addExtension ef.create_extension("authorityKeyIdentifier", "keyid:always, issuer:always")
- certificate.sign keypair, OpenSSL::Digest::SHA1.new
- cb = proc do @new_certificate_opts_authority[:password] end
- keypair_export = (keypair.export OpenSSL::Cipher::DES.new(:EDE3, :CBC), &cb)
- puts "Writing keypair to #{@new_certificate_opts_authority[:keypair_file]}" if $DEBUG
- File.open @new_certificate_opts_authority[:keypair_file], "w", 0400 do |fp|
- fp << keypair_export
- end
- puts "Writing certificate to #{@new_certificate_opts_authority[:cert_file]}" if $DEBUG
- File.open @new_certificate_opts_authority[:cert_file], "w", 0644 do |f|
- f << certificate.to_pem
- end
- puts "Done generating certificate for #{certificate.subject}" if $DEBUG
- end
- ##
- # Creates a new certificate from +new_certificate_opts+ that is signed
- # by the CA.
- def create_certificate(new_certificate_opts)
- @dest = new_certificate_opts[:hostname] || new_certificate_opts[:user]
- new_certificate_opts[:new_dir] = File.join(@new_certificate_opts_authority[:new_certificates_dir], @dest)
- Dir.mkdir(new_certificate_opts[:new_dir], 0700) # 0700 = 000 111 000 000 I think owner read/write/execute
- @keypair_file = File.join(new_certificate_opts[:new_dir], (@dest + "_keypair.pem"))
- certificate_keypair = create_key new_certificate_opts
- certificate_csr = create_certificate_signing_request new_certificate_opts, certificate_keypair
- sign_certificate new_certificate_opts, certificate_keypair, certificate_csr
- end
- ##
- # Creates a new RSA key from +new_certificate_opts+.
- def create_key(new_certificate_opts)
- passwd_cb = nil
- # @dest = new_certificate_opts[:hostname] || new_certificate_opts[:user]
- # keypair_file = File.join(ldest, (dest + "_keypair.pem"))
- # new_dir = File.join(@new_certificate_opts_authority[:new_certificates_dir], dest)
- # Dir.mkdir new_dir, 0700
- puts "Generating RSA keypair" if $DEBUG
- keypair = OpenSSL::PKey::RSA.new(1024)
- if new_certificate_opts[:password].nil? then
- File.open @keypair_file, "w", 0400 do |f| # 0400 = 000 100 000 000 owner read only ??
- f << keypair.to_pem
- end
- else
- passwd_cb = proc do
- new_certificate_opts[:password]
- end
- cipher = OpenSSL::Cipher::DES.new(:EDE3, :CBC)
- keypair_export = keypair.export(cipher, new_certificate_opts[:password])
- puts "Writing keypair to #{@keypair_file}" if $DEBUG
- File.open @keypair_file, "w", 0400 do |f|
- f << keypair_export
- end
- end
- return @keypair_file
- end
- ##
- # Creates a new Certificate Signing Request for the keypair in
- # +keypair_file+, generating and saving new keypair if nil.
- def create_certificate_signing_request(new_certificate_opts, keypair_file = nil)
- keypair = nil
- # dest = new_certificate_opts[:hostname] || new_certificate_opts[:user]
- csr_file = File.join new_certificate_opts[:new_dir], "csr_#{@dest}.pem"
- name = @new_certificate_opts_authority[:name].dup
- case new_certificate_opts[:type]
- when 'server' then
- name << ['OU', 'CA']
- name << ['CN', new_certificate_opts[:hostname]]
- when 'client' then
- name << ['CN', new_certificate_opts[:user]]
- name << ['emailAddress', new_certificate_opts[:email]]
- end
- name = OpenSSL::X509::Name.new(name)
- if File.exists? keypair_file then
- keypair = OpenSSL::PKey::RSA.new(File.read(keypair_file), new_certificate_opts[:password])
- else
- keypair = create_key(new_certificate_opts)
- end
- puts "Generating CSR for #{name}" if $DEBUG
- req = OpenSSL::X509::Request.new
- req.version = 0
- req.subject = name
- req.public_key = keypair.public_key
- req.sign keypair, OpenSSL::Digest::MD5.new
- puts "Writing CSR to #{csr_file}" if $DEBUG
- File.open csr_file, "w" do |f|
- f << req.to_pem
- end
- return csr_file
- end
- ##
- # Signs the certificate described in +new_certificate_opts+ and
- # +csr_file+, saving it to +cert_file+.
- def sign_certificate(new_certificate_opts, cert_file, csr_file)
- csr = OpenSSL::X509::Request.new File.read(csr_file)
- raise "CSR sign verification failed." unless csr.verify csr.public_key
- if csr.public_key.n.num_bits < @new_certificate_opts_authority[:cert_key_length_min] then
- raise "Key length too short"
- end
- if csr.public_key.n.num_bits > @new_certificate_opts_authority[:cert_key_length_max] then
- raise "Key length too long"
- end
- if csr.subject.to_a[0, @new_certificate_opts_authority[:name].size] != @new_certificate_opts_authority[:name] then
- raise "DN does not match"
- end
- # Only checks signature here. You must verify CSR according to your
- # CP/CPS.
- # CA setup
- puts "Reading CA certificate from #{@new_certificate_opts_authority[:cert_file]}" if $DEBUG
- ca = OpenSSL::X509::Certificate.new(File.read(@new_certificate_opts_authority[:cert_file]))
- puts "Reading CA keypair from #{@new_certificate_opts_authority[:keypair_file]}" if $DEBUG
- ca_keypair = OpenSSL::PKey::RSA.new(File.read(@new_certificate_opts_authority[:keypair_file]), @new_certificate_opts_authority[:password])
- serial = File.read(@new_certificate_opts_authority[:serial_file]).chomp.hex
- File.open @new_certificate_opts_authority[:serial_file], "w" do |f|
- f << "%04X" % (serial + 1)
- end
- puts "Generating certificate" if $DEBUG
- certificate = OpenSSL::X509::Certificate.new
- from = Time.now
- certificate.subject = csr.subject
- certificate.issuer = ca.subject
- certificate.not_before = from
- certificate.not_after = from + @new_certificate_opts_authority[:cert_days] * 24 * 60 * 60
- certificate.public_key = csr.public_key
- certificate.serial = serial
- certificate.version = 2 # X509v3
- basic_constraint = nil
- key_usage = []
- ext_key_usage = []
- case new_certificate_opts[:type]
- when "ca" then
- basic_constraint = "CA:TRUE"
- key_usage << "cRLSign" << "keyCertSign"
- when "terminalsubca" then
- basic_constraint = "CA:TRUE,pathlen:0"
- key_usage << "cRLSign" << "keyCertSign"
- when "server" then
- basic_constraint = "CA:FALSE"
- key_usage << "digitalSignature" << "keyEncipherment"
- ext_key_usage << "serverAuth"
- when "ocsp" then
- basic_constraint = "CA:FALSE"
- key_usage << "nonRepudiation" << "digitalSignature"
- ext_key_usage << "serverAuth" << "OCSPSigning"
- when "client" then
- basic_constraint = "CA:FALSE"
- key_usage << "nonRepudiation" << "digitalSignature" << "keyEncipherment"
- ext_key_usage << "clientAuth" << "emailProtection"
- else
- raise "unknonw certificate type \"#{new_certificate_opts[:type]}\""
- end
- ef = OpenSSL::X509::ExtensionFactory.new
- ef.subject_certificate = certificate
- ef.issuer_certificate = ca
- ex = []
- ex << ef.create_extension("basicConstraints", basic_constraint, true)
- ex << ef.create_extension("nsComment",
- "Ruby/OpenSSL Generated Certificate")
- ex << ef.create_extension("subjectKeyIdentifier", "hash")
- #ex << ef.create_extension("nsCertType", "client,email")
- unless key_usage.empty? then
- ex << ef.create_extension("keyUsage", key_usage.join(","))
- end
- #ex << ef.create_extension("authorityKeyIdentifier",
- # "keyid:always,issuer:always")
- #ex << ef.create_extension("authorityKeyIdentifier", "keyid:always")
- unless ext_key_usage.empty? then
- ex << ef.create_extension("extendedKeyUsage", ext_key_usage.join(","))
- end
- if @new_certificate_opts_authority[:certificate_revokation_list_distribution_point_location] then
- ex << ef.create_extension("crlDistributionPoints",
- @new_certificate_opts_authority[:certificate_revokation_list_distribution_point_location])
- end
- if @new_certificate_opts_authority[:online_certificate_status_protocol_utility_location] then
- ex << ef.create_extension("authorityInfoAccess",
- "OCSP;" << @new_certificate_opts_authority[:online_certificate_status_protocol_utility_location])
- end
- certificate.extensions = ex
- certificate.sign(ca_keypair, OpenSSL::Digest::SHA1.new)
- backup_certificate_file = @new_certificate_opts_authority[:new_certs_dir] + "/cert_#{certificate.serial}.pem"
- puts "Writing backup certificate to #{backup_certificate_file}" if $DEBUG
- File.open backup_certificate_file, "w", 0644 do |f|
- f << certificate.to_pem
- end
- # Write certificate
- dest = new_certificate_opts[:hostname] || new_certificate_opts[:user]
- certificate_file = File.join dest, "certificate_#{dest}.pem"
- puts "Writing certificate to #{certificate_file}" if $DEBUG
- File.open certificate_file, "w", 0644 do |f|
- f << certificate.to_pem
- end
- return certificate_file
- end
- end # class QuickCert
- #if __FILE__ == $0 then
- $DEBUG = true
- CA = {}
- CERTS = []
- #load ARGV.shift || 'qc_config'
- #CA[:certificate_authority_dir] = File.join($info[:program_directory], 'certificates')
- CA[:certificate_authority_dir] = File.join(File.dirname(__FILE__) , 'certificates')
- CA[:keypair_file] ||= File.join CA[:certificate_authority_dir], "private/cakeypair.pem"
- CA[:cert_file] ||= File.join CA[:certificate_authority_dir], "cacert.pem"
- CA[:serial_file] ||= File.join CA[:certificate_authority_dir], "serial"
- CA[:new_certs_dir] ||= File.join CA[:certificate_authority_dir], "newcerts"
- CA[:new_keypair_dir] ||= File.join CA[:certificate_authority_dir], "private/keypair_backup"
- CA[:crl_dir] ||= File.join CA[:certificate_authority_dir], "crl"
- CA[:ca_cert_days] ||= 5 * 365 # five years
- CA[:ca_rsa_key_length] ||= 2048
- CA[:cert_days] ||= 365 # one year
- CA[:cert_key_length_min] ||= 1024
- CA[:cert_key_length_max] ||= 2048
- CA[:crl_file] ||= File.join CA[:crl_dir], "#{CA[:hostname]}.crl"
- CA[:crl_pem_file] ||= File.join CA[:crl_dir], "#{CA[:hostname]}.pem"
- CA[:crl_days] ||= 14
- if CA[:name].nil?
- CA[:name] = [
- ['C', 'US', OpenSSL::ASN1::PRINTABLESTRING],
- ['O', CA[:domainname], OpenSSL::ASN1::UTF8STRING],
- ['OU', CA[:hostname], OpenSSL::ASN1::UTF8STRING],
- ]
- end
- CERTS << {
- :type => 'server',
- :hostname => 'uriel',
- :password => '5678',
- }
- CERTS << {
- :type => 'client',
- :user => 'drbrain',
- :email => 'drbrain@segment7.net',
- }
- qc = QuickCert.new(CA)
- CERTS.each do |new_certificate_opts|
- qc.create_certificate(new_certificate_opts)
- end
- #end
Add Comment
Please, Sign In to add comment