Permalink
Browse files

Added CA example scripts

  • Loading branch information...
1 parent 78bb722 commit 767b851bf5c8d4b0319651119099a43dbe7bebfa @nahi committed Sep 1, 2011
Showing with 426 additions and 0 deletions.
  1. +37 −0 CA.rb/ca_config.rb
  2. +127 −0 CA.rb/gen_cert.rb
  3. +62 −0 CA.rb/gen_crl.rb
  4. +50 −0 CA.rb/gen_csr.rb
  5. +31 −0 CA.rb/gen_rsakey.rb
  6. +66 −0 CA.rb/init_ca.rb
  7. +53 −0 CA.rb/init_sub_ca.rb
View
@@ -0,0 +1,37 @@
+class CAConfig
+ BASE_DIR = File.dirname(__FILE__)
+ KEYPAIR_FILE = "#{BASE_DIR}/private/cakeypair.pem"
+ CERT_FILE = "#{BASE_DIR}/cacert.pem"
+ SERIAL_FILE = "#{BASE_DIR}/serial"
+ NEW_CERTS_DIR = "#{BASE_DIR}/newcerts"
+ NEW_KEYPAIR_DIR = "#{BASE_DIR}/private/keypair_backup"
+ CRL_DIR = "#{BASE_DIR}/crl"
+
+ NAME = [['C', 'JP'], ['O', 'JIN.GR.JP'], ['OU', 'RRR']]
+ CA_CERT_DAYS = 50 * 365
+ CA_RSA_KEY_LENGTH = 2048
+
+ CERT_DAYS = 31 * 365
+ CERT_KEY_LENGTH_MIN = 1024
+ CERT_KEY_LENGTH_MAX = 2048
+ CDP_LOCATION = nil
+ OCSP_LOCATION = nil
+
+ CRL_FILE = "#{CRL_DIR}/rrr.crl"
+ CRL_PEM_FILE = "#{CRL_DIR}/rrr.pem"
+ CRL_DAYS = 14
+
+ PASSWD_CB = Proc.new { |flag|
+ print "Enter password: "
+ pass = $stdin.gets.chop!
+ # when the flag is true, this passphrase
+ # will be used to perform encryption; otherwise it will
+ # be used to perform decryption.
+ if flag
+ print "Verify password: "
+ pass2 = $stdin.gets.chop!
+ raise "verify failed." if pass != pass2
+ end
+ pass
+ }
+end
View
@@ -0,0 +1,127 @@
+#!/usr/bin/env ruby
+
+require 'openssl'
+require 'ca_config'
+require 'fileutils'
+require 'getopts'
+
+include OpenSSL
+
+def usage
+ myname = File::basename($0)
+ $stderr.puts "Usage: #{myname} [--type (client|server|ca|ocsp)] [--out certfile] csr_file"
+ exit
+end
+
+getopts nil, 'type:client', 'out:', 'force'
+
+cert_type = $OPT_type
+out_file = $OPT_out || 'cert.pem'
+csr_file = ARGV.shift or usage
+ARGV.empty? or usage
+
+csr = X509::Request.new(File.open(csr_file).read)
+unless csr.verify(csr.public_key)
+ raise "CSR sign verification failed."
+end
+p csr.public_key
+if csr.public_key.n.num_bits < CAConfig::CERT_KEY_LENGTH_MIN
+ raise "Key length too short"
+end
+if csr.public_key.n.num_bits > CAConfig::CERT_KEY_LENGTH_MAX
+ raise "Key length too long"
+end
+if csr.subject.to_a[0, CAConfig::NAME.size] != CAConfig::NAME
+ unless $OPT_force
+ p csr.subject.to_a
+ p CAConfig::NAME
+ raise "DN does not match"
+ end
+end
+
+# Only checks signature here. You must verify CSR according to your CP/CPS.
+
+$stdout.sync = true
+
+# CA setup
+
+ca_file = CAConfig::CERT_FILE
+puts "Reading CA cert (from #{ca_file})"
+ca = X509::Certificate.new(File.read(ca_file))
+
+ca_keypair_file = CAConfig::KEYPAIR_FILE
+puts "Reading CA keypair (from #{ca_keypair_file})"
+ca_keypair = PKey::RSA.new(File.read(ca_keypair_file), &CAConfig::PASSWD_CB)
+
+serial = File.open(CAConfig::SERIAL_FILE, "r").read.chomp.hex
+File.open(CAConfig::SERIAL_FILE, "w") do |f|
+ f << sprintf("%04X", serial + 1)
+end
+
+# Generate new cert
+
+cert = X509::Certificate.new
+from = Time.now # + 30 * 60 # Wait 30 minutes.
+cert.subject = csr.subject
+cert.issuer = ca.subject
+cert.not_before = from
+cert.not_after = from + CAConfig::CERT_DAYS * 24 * 60 * 60
+cert.public_key = csr.public_key
+cert.serial = serial
+cert.version = 2 # X509v3
+
+basic_constraint = nil
+key_usage = []
+ext_key_usage = []
+case cert_type
+when "ca"
+ basic_constraint = "CA:TRUE"
+ key_usage << "cRLSign" << "keyCertSign"
+when "terminalsubca"
+ basic_constraint = "CA:TRUE,pathlen:0"
+ key_usage << "cRLSign" << "keyCertSign"
+when "server"
+ basic_constraint = "CA:FALSE"
+ key_usage << "digitalSignature" << "keyEncipherment"
+ ext_key_usage << "serverAuth"
+when "ocsp"
+ basic_constraint = "CA:FALSE"
+ key_usage << "nonRepudiation" << "digitalSignature"
+ ext_key_usage << "serverAuth" << "OCSPSigning"
+when "client"
+ basic_constraint = "CA:FALSE"
+ key_usage << "nonRepudiation" << "digitalSignature" << "keyEncipherment"
+ ext_key_usage << "clientAuth" << "emailProtection"
+else
+ raise "unknonw cert type \"#{cert_type}\" is specified."
+end
+
+ef = X509::ExtensionFactory.new
+ef.subject_certificate = cert
+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")
+ex << ef.create_extension("keyUsage", key_usage.join(",")) unless key_usage.empty?
+#ex << ef.create_extension("authorityKeyIdentifier", "keyid:always,issuer:always")
+#ex << ef.create_extension("authorityKeyIdentifier", "keyid:always")
+ex << ef.create_extension("extendedKeyUsage", ext_key_usage.join(",")) unless ext_key_usage.empty?
+
+ex << ef.create_extension("crlDistributionPoints", CAConfig::CDP_LOCATION) if CAConfig::CDP_LOCATION
+ex << ef.create_extension("authorityInfoAccess", "OCSP;" << CAConfig::OCSP_LOCATION) if CAConfig::OCSP_LOCATION
+cert.extensions = ex
+cert.sign(ca_keypair, OpenSSL::Digest::SHA1.new)
+
+# For backup
+
+cert_file = CAConfig::NEW_CERTS_DIR + "/#{cert.serial}_cert.pem"
+File.open(cert_file, "w", 0644) do |f|
+ f << cert.to_pem
+end
+
+puts "Writing cert.pem..."
+FileUtils.copy(cert_file, out_file)
+
+puts "DONE. (Generated certificate for '#{cert.subject}')"
View
@@ -0,0 +1,62 @@
+#!/usr/bin/env ruby
+
+require 'openssl'
+require 'ca_config'
+require 'getopts'
+
+include OpenSSL
+
+def usage
+ myname = File::basename($0)
+ $stderr.puts
+ $stderr.puts "Warning: You're publishing empty CRL."
+ $stderr.puts "For revoking certificates use it like this:"
+ $stderr.puts "\t$ #{myname} Cert_to_revoke1.pem*"
+ $stderr.puts
+end
+
+ARGV.empty? && usage()
+
+# CA setup
+
+ca_file = CAConfig::CERT_FILE
+puts "Reading CA cert (from #{ca_file})"
+ca = X509::Certificate.new(File.read(ca_file))
+
+ca_keypair_file = CAConfig::KEYPAIR_FILE
+puts "Reading CA keypair (from #{ca_keypair_file})"
+ca_keypair = PKey::RSA.new(File.read(ca_keypair_file), &CAConfig::PASSWD_CB)
+
+# CRL setting
+
+crl = if FileTest.exist?(CAConfig::CRL_FILE)
+ X509::CRL.new(File.read(CAConfig::CRL_FILE))
+ else
+ X509::CRL.new
+ end
+
+now = Time.now
+crl.issuer = ca.subject
+crl.last_update = now
+crl.next_update = now + CAConfig::CRL_DAYS * 24 * 60 * 60
+
+ARGV.each do |file|
+ cert = X509::Certificate.new(File.read(file))
+ re = X509::Revoked.new
+ re.serial = cert.serial
+ re.time = now
+ crl.add_revoked(re)
+ puts "+ Serial ##{re.serial} - revoked at #{re.time}"
+end
+
+crl.sign(ca_keypair, Digest::SHA1.new)
+
+puts "Writing #{CAConfig::CRL_FILE}."
+File.open(CAConfig::CRL_FILE, "w") do |f|
+ f << crl.to_der
+end
+File.open(CAConfig::CRL_PEM_FILE, "w") do |f|
+ f << crl.to_pem
+end
+
+puts "DONE. (Generated CRL for '#{ca.subject}')"
View
@@ -0,0 +1,50 @@
+#!/usr/bin/env ruby
+
+require 'getopts'
+require 'openssl'
+
+include OpenSSL
+
+def usage
+ myname = File::basename($0)
+ $stderr.puts <<EOS
+Usage: #{myname} [--key keypair_file] name
+ name ... ex. /C=JP/O=RRR/OU=CA/CN=NaHi/emailAddress=nahi@example.org
+EOS
+ exit
+end
+
+getopts nil, "key:", "csrout:", "keyout:"
+keypair_file = $OPT_key
+csrout = $OPT_csrout || "csr.pem"
+keyout = $OPT_keyout || "keypair.pem"
+
+$stdout.sync = true
+name_str = ARGV.shift or usage()
+p name_str
+name = X509::Name.parse(name_str)
+
+keypair = nil
+if keypair_file
+ keypair = PKey::RSA.new(File.open(keypair_file).read)
+else
+ keypair = PKey::RSA.new(1024) { putc "." }
+ puts
+ puts "Writing #{keyout}..."
+ File.open(keyout, "w", 0400) do |f|
+ f << keypair.to_pem
+ end
+end
+
+puts "Generating CSR for #{name_str}"
+
+req = X509::Request.new
+req.version = 0
+req.subject = name
+req.public_key = keypair.public_key
+req.sign(keypair, OpenSSL::Digest::MD5.new)
+
+puts "Writing #{csrout}..."
+File.open(csrout, "w") do |f|
+ f << req.to_pem
+end
View
@@ -0,0 +1,31 @@
+#!/usr/bin/env ruby
+
+require 'openssl'
+
+include OpenSSL
+
+$stdout.sync = true
+
+keypair_file = ARGV.shift || 'keypair.pem'
+
+PASSWD_CB = Proc.new { |flag|
+ print "Enter password: "
+ pass = $stdin.gets.chop!
+ # when the flag is true, this passphrase
+ # will be used to perform encryption; otherwise it will
+ # be used to perform decryption.
+ if flag
+ print "Verify password: "
+ pass2 = $stdin.gets.chop!
+ raise "verify failed." if pass != pass2
+ end
+ pass
+}
+
+print "Generating CA keypair: "
+keypair = PKey::RSA.new(2048) { putc "." }
+putc "\n"
+puts "Writing keypair."
+File.open(keypair_file, "w", 0400) do |f|
+ f << keypair.export(Cipher::DES.new(:EDE3, :CBC), &PASSWD_CB)
+end
View
@@ -0,0 +1,66 @@
+#!/usr/bin/env ruby
+
+require 'openssl'
+require 'ca_config'
+
+include OpenSSL
+
+$stdout.sync = true
+
+cn = ARGV.shift || 'CA'
+
+unless FileTest.exist?('private')
+ Dir.mkdir('private', 0700)
+end
+unless FileTest.exist?('newcerts')
+ Dir.mkdir('newcerts')
+end
+unless FileTest.exist?('crl')
+ Dir.mkdir('crl')
+end
+unless FileTest.exist?('serial')
+ File.open('serial', 'w') do |f|
+ f << '2'
+ end
+end
+
+print "Generating CA keypair: "
+keypair = PKey::RSA.new(CAConfig::CA_RSA_KEY_LENGTH) { putc "." }
+putc "\n"
+
+now = Time.now
+cert = X509::Certificate.new
+name = CAConfig::NAME.dup << ['CN', cn]
+cert.subject = cert.issuer = X509::Name.new(name)
+cert.not_before = now
+cert.not_after = now + CAConfig::CA_CERT_DAYS * 24 * 60 * 60
+cert.public_key = keypair.public_key
+cert.serial = 0x1
+cert.version = 2 # X509v3
+
+key_usage = ["cRLSign", "keyCertSign"]
+ef = X509::ExtensionFactory.new
+ef.subject_certificate = cert
+ef.issuer_certificate = cert # we needed subjectKeyInfo inside, now we have it
+ext1 = ef.create_extension("basicConstraints","CA:TRUE", true)
+ext2 = ef.create_extension("nsComment","Ruby/OpenSSL Generated Certificate")
+ext3 = ef.create_extension("subjectKeyIdentifier", "hash")
+ext4 = ef.create_extension("keyUsage", key_usage.join(","), true)
+cert.extensions = [ext1, ext2, ext3, ext4]
+ext0 = ef.create_extension("authorityKeyIdentifier", "keyid:always,issuer:always")
+cert.add_extension(ext0)
+cert.sign(keypair, OpenSSL::Digest::SHA1.new)
+
+keypair_file = CAConfig::KEYPAIR_FILE
+puts "Writing keypair."
+File.open(keypair_file, "w", 0400) do |f|
+ f << keypair.export(Cipher::DES.new(:EDE3, :CBC), &CAConfig::PASSWD_CB)
+end
+
+cert_file = CAConfig::CERT_FILE
+puts "Writing #{cert_file}."
+File.open(cert_file, "w", 0644) do |f|
+ f << cert.to_pem
+end
+
+puts "DONE. (Generated certificate for '#{cert.subject}')"
Oops, something went wrong.

0 comments on commit 767b851

Please sign in to comment.