Guest User

Untitled

a guest
Apr 25th, 2018
92
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 12.75 KB | None | 0 0
  1.  
  2. require 'openssl'
  3.  
  4.  
  5.  
  6. class QuickCert
  7.  
  8. VERSION = "1.0.2"
  9.  
  10. ##
  11. # Creates a new QuickCert instance using the Certificate
  12. # Authority described in +cfg+. If there is no CA at
  13. # cfg[:certificate_authority_dir], then QuickCert will initialize a new one.
  14.  
  15. def initialize(new_certificate_opts_authority)
  16. @new_certificate_opts_authority = new_certificate_opts_authority
  17.  
  18. create_certificate_authority
  19. end
  20.  
  21.  
  22. ##
  23. # Creates a new Certificate Authority from @new_certificate_opts_authority if it
  24. # does not already exist at new_certificate_opts_authority[:certificate_authority_dir].
  25.  
  26. def create_certificate_authority
  27. return if File.exists? @new_certificate_opts_authority[:certificate_authority_dir]
  28.  
  29. Dir.mkdir @new_certificate_opts_authority[:certificate_authority_dir]
  30.  
  31. Dir.mkdir(File.join(@new_certificate_opts_authority[:certificate_authority_dir], 'private'), 0700)
  32. @new_certificate_opts_authority[:new_certificates_dir] = File.join(@new_certificate_opts_authority[:certificate_authority_dir], 'newcertificates')
  33. Dir.mkdir(@new_certificate_opts_authority[:new_certificates_dir])
  34. Dir.mkdir(File.join(@new_certificate_opts_authority[:certificate_authority_dir], 'certificate_revokation_list'))
  35.  
  36. File.open @new_certificate_opts_authority[:serial_file], 'w' do |f| f << '1' end
  37.  
  38. puts "Generating CA keypair" if $DEBUG
  39.  
  40. keypair = OpenSSL::PKey::RSA.new @new_certificate_opts_authority[:ca_rsa_key_length]
  41.  
  42. certificate = OpenSSL::X509::Certificate.new
  43.  
  44. name = @new_certificate_opts_authority[:name].dup << ['CN', 'CA']
  45.  
  46. certificate.subject = certificate.issuer = OpenSSL::X509::Name.new(name)
  47.  
  48. certificate.not_before = Time.now
  49.  
  50. certificate.not_after = Time.now + @new_certificate_opts_authority[:ca_cert_days] * 24 * 60 * 60
  51.  
  52. certificate.public_key = keypair.public_key
  53.  
  54. certificate.serial = 12345
  55.  
  56. certificate.version = 2 # X509v3
  57.  
  58. ef = OpenSSL::X509::ExtensionFactory.new
  59.  
  60. ef.subject_certificate = certificate
  61.  
  62. ef.issuer_certificate = certificate
  63.  
  64. certificate.extensions = [
  65. ef.create_extension("basicConstraints","CA:TRUE", true),
  66. ef.create_extension("nsComment","Ruby/OpenSSL Generated Certificate"),
  67. ef.create_extension("subjectKeyIdentifier", "hash"),
  68. ef.create_extension("keyUsage", "cRLSign,keyCertSign", true),
  69. ef.create_extension("authorityKeyIdentifier", "keyid:always, issuer:always")
  70. ]
  71.  
  72. # certificate.extensions.each {|e| puts e}
  73. # abort
  74. # cert.addExtension(ext)
  75.  
  76. # certificate.addExtension ef.create_extension("authorityKeyIdentifier", "keyid:always, issuer:always")
  77.  
  78. certificate.sign keypair, OpenSSL::Digest::SHA1.new
  79.  
  80. cb = proc do @new_certificate_opts_authority[:password] end
  81.  
  82. keypair_export = (keypair.export OpenSSL::Cipher::DES.new(:EDE3, :CBC), &cb)
  83.  
  84. puts "Writing keypair to #{@new_certificate_opts_authority[:keypair_file]}" if $DEBUG
  85.  
  86. File.open @new_certificate_opts_authority[:keypair_file], "w", 0400 do |fp|
  87. fp << keypair_export
  88. end
  89.  
  90. puts "Writing certificate to #{@new_certificate_opts_authority[:cert_file]}" if $DEBUG
  91.  
  92. File.open @new_certificate_opts_authority[:cert_file], "w", 0644 do |f|
  93. f << certificate.to_pem
  94. end
  95.  
  96. puts "Done generating certificate for #{certificate.subject}" if $DEBUG
  97. end
  98.  
  99. ##
  100. # Creates a new certificate from +new_certificate_opts+ that is signed
  101. # by the CA.
  102.  
  103. def create_certificate(new_certificate_opts)
  104.  
  105. @dest = new_certificate_opts[:hostname] || new_certificate_opts[:user]
  106. new_certificate_opts[:new_dir] = File.join(@new_certificate_opts_authority[:new_certificates_dir], @dest)
  107. Dir.mkdir(new_certificate_opts[:new_dir], 0700) # 0700 = 000 111 000 000 I think owner read/write/execute
  108.  
  109. @keypair_file = File.join(new_certificate_opts[:new_dir], (@dest + "_keypair.pem"))
  110.  
  111. certificate_keypair = create_key new_certificate_opts
  112.  
  113. certificate_csr = create_certificate_signing_request new_certificate_opts, certificate_keypair
  114.  
  115. sign_certificate new_certificate_opts, certificate_keypair, certificate_csr
  116. end
  117.  
  118.  
  119.  
  120. ##
  121. # Creates a new RSA key from +new_certificate_opts+.
  122.  
  123. def create_key(new_certificate_opts)
  124.  
  125. passwd_cb = nil
  126.  
  127. # @dest = new_certificate_opts[:hostname] || new_certificate_opts[:user]
  128.  
  129. # keypair_file = File.join(ldest, (dest + "_keypair.pem"))
  130.  
  131. # new_dir = File.join(@new_certificate_opts_authority[:new_certificates_dir], dest)
  132. # Dir.mkdir new_dir, 0700
  133.  
  134. puts "Generating RSA keypair" if $DEBUG
  135.  
  136. keypair = OpenSSL::PKey::RSA.new(1024)
  137.  
  138. if new_certificate_opts[:password].nil? then
  139. File.open @keypair_file, "w", 0400 do |f| # 0400 = 000 100 000 000 owner read only ??
  140. f << keypair.to_pem
  141. end
  142. else
  143. passwd_cb = proc do
  144. new_certificate_opts[:password]
  145. end
  146.  
  147. cipher = OpenSSL::Cipher::DES.new(:EDE3, :CBC)
  148.  
  149. keypair_export = keypair.export(cipher, new_certificate_opts[:password])
  150.  
  151. puts "Writing keypair to #{@keypair_file}" if $DEBUG
  152.  
  153. File.open @keypair_file, "w", 0400 do |f|
  154. f << keypair_export
  155. end
  156.  
  157. end
  158.  
  159. return @keypair_file
  160. end
  161.  
  162.  
  163. ##
  164. # Creates a new Certificate Signing Request for the keypair in
  165. # +keypair_file+, generating and saving new keypair if nil.
  166.  
  167. def create_certificate_signing_request(new_certificate_opts, keypair_file = nil)
  168.  
  169. keypair = nil
  170.  
  171. # dest = new_certificate_opts[:hostname] || new_certificate_opts[:user]
  172.  
  173. csr_file = File.join new_certificate_opts[:new_dir], "csr_#{@dest}.pem"
  174.  
  175. name = @new_certificate_opts_authority[:name].dup
  176.  
  177. case new_certificate_opts[:type]
  178. when 'server' then
  179. name << ['OU', 'CA']
  180. name << ['CN', new_certificate_opts[:hostname]]
  181. when 'client' then
  182. name << ['CN', new_certificate_opts[:user]]
  183. name << ['emailAddress', new_certificate_opts[:email]]
  184. end
  185.  
  186. name = OpenSSL::X509::Name.new(name)
  187.  
  188. if File.exists? keypair_file then
  189. keypair = OpenSSL::PKey::RSA.new(File.read(keypair_file), new_certificate_opts[:password])
  190. else
  191. keypair = create_key(new_certificate_opts)
  192. end
  193.  
  194. puts "Generating CSR for #{name}" if $DEBUG
  195.  
  196. req = OpenSSL::X509::Request.new
  197. req.version = 0
  198. req.subject = name
  199. req.public_key = keypair.public_key
  200. req.sign keypair, OpenSSL::Digest::MD5.new
  201.  
  202. puts "Writing CSR to #{csr_file}" if $DEBUG
  203.  
  204. File.open csr_file, "w" do |f|
  205. f << req.to_pem
  206. end
  207.  
  208. return csr_file
  209. end
  210.  
  211. ##
  212. # Signs the certificate described in +new_certificate_opts+ and
  213. # +csr_file+, saving it to +cert_file+.
  214.  
  215. def sign_certificate(new_certificate_opts, cert_file, csr_file)
  216.  
  217. csr = OpenSSL::X509::Request.new File.read(csr_file)
  218.  
  219. raise "CSR sign verification failed." unless csr.verify csr.public_key
  220.  
  221. if csr.public_key.n.num_bits < @new_certificate_opts_authority[:cert_key_length_min] then
  222. raise "Key length too short"
  223. end
  224.  
  225. if csr.public_key.n.num_bits > @new_certificate_opts_authority[:cert_key_length_max] then
  226. raise "Key length too long"
  227. end
  228.  
  229. if csr.subject.to_a[0, @new_certificate_opts_authority[:name].size] != @new_certificate_opts_authority[:name] then
  230. raise "DN does not match"
  231. end
  232.  
  233. # Only checks signature here. You must verify CSR according to your
  234. # CP/CPS.
  235.  
  236. # CA setup
  237.  
  238. puts "Reading CA certificate from #{@new_certificate_opts_authority[:cert_file]}" if $DEBUG
  239.  
  240. ca = OpenSSL::X509::Certificate.new(File.read(@new_certificate_opts_authority[:cert_file]))
  241.  
  242. puts "Reading CA keypair from #{@new_certificate_opts_authority[:keypair_file]}" if $DEBUG
  243.  
  244. ca_keypair = OpenSSL::PKey::RSA.new(File.read(@new_certificate_opts_authority[:keypair_file]), @new_certificate_opts_authority[:password])
  245.  
  246. serial = File.read(@new_certificate_opts_authority[:serial_file]).chomp.hex
  247.  
  248. File.open @new_certificate_opts_authority[:serial_file], "w" do |f|
  249. f << "%04X" % (serial + 1)
  250. end
  251.  
  252. puts "Generating certificate" if $DEBUG
  253.  
  254. certificate = OpenSSL::X509::Certificate.new
  255. from = Time.now
  256. certificate.subject = csr.subject
  257. certificate.issuer = ca.subject
  258. certificate.not_before = from
  259. certificate.not_after = from + @new_certificate_opts_authority[:cert_days] * 24 * 60 * 60
  260. certificate.public_key = csr.public_key
  261. certificate.serial = serial
  262. certificate.version = 2 # X509v3
  263.  
  264. basic_constraint = nil
  265. key_usage = []
  266. ext_key_usage = []
  267.  
  268. case new_certificate_opts[:type]
  269. when "ca" then
  270. basic_constraint = "CA:TRUE"
  271. key_usage << "cRLSign" << "keyCertSign"
  272. when "terminalsubca" then
  273. basic_constraint = "CA:TRUE,pathlen:0"
  274. key_usage << "cRLSign" << "keyCertSign"
  275. when "server" then
  276. basic_constraint = "CA:FALSE"
  277. key_usage << "digitalSignature" << "keyEncipherment"
  278. ext_key_usage << "serverAuth"
  279. when "ocsp" then
  280. basic_constraint = "CA:FALSE"
  281. key_usage << "nonRepudiation" << "digitalSignature"
  282. ext_key_usage << "serverAuth" << "OCSPSigning"
  283. when "client" then
  284. basic_constraint = "CA:FALSE"
  285. key_usage << "nonRepudiation" << "digitalSignature" << "keyEncipherment"
  286. ext_key_usage << "clientAuth" << "emailProtection"
  287. else
  288. raise "unknonw certificate type \"#{new_certificate_opts[:type]}\""
  289. end
  290.  
  291. ef = OpenSSL::X509::ExtensionFactory.new
  292. ef.subject_certificate = certificate
  293. ef.issuer_certificate = ca
  294. ex = []
  295. ex << ef.create_extension("basicConstraints", basic_constraint, true)
  296. ex << ef.create_extension("nsComment",
  297. "Ruby/OpenSSL Generated Certificate")
  298. ex << ef.create_extension("subjectKeyIdentifier", "hash")
  299. #ex << ef.create_extension("nsCertType", "client,email")
  300. unless key_usage.empty? then
  301. ex << ef.create_extension("keyUsage", key_usage.join(","))
  302. end
  303. #ex << ef.create_extension("authorityKeyIdentifier",
  304. # "keyid:always,issuer:always")
  305. #ex << ef.create_extension("authorityKeyIdentifier", "keyid:always")
  306. unless ext_key_usage.empty? then
  307. ex << ef.create_extension("extendedKeyUsage", ext_key_usage.join(","))
  308. end
  309.  
  310. if @new_certificate_opts_authority[:certificate_revokation_list_distribution_point_location] then
  311. ex << ef.create_extension("crlDistributionPoints",
  312. @new_certificate_opts_authority[:certificate_revokation_list_distribution_point_location])
  313. end
  314.  
  315. if @new_certificate_opts_authority[:online_certificate_status_protocol_utility_location] then
  316. ex << ef.create_extension("authorityInfoAccess",
  317. "OCSP;" << @new_certificate_opts_authority[:online_certificate_status_protocol_utility_location])
  318. end
  319.  
  320. certificate.extensions = ex
  321.  
  322. certificate.sign(ca_keypair, OpenSSL::Digest::SHA1.new)
  323.  
  324. backup_certificate_file = @new_certificate_opts_authority[:new_certs_dir] + "/cert_#{certificate.serial}.pem"
  325.  
  326. puts "Writing backup certificate to #{backup_certificate_file}" if $DEBUG
  327.  
  328. File.open backup_certificate_file, "w", 0644 do |f|
  329. f << certificate.to_pem
  330. end
  331.  
  332. # Write certificate
  333. dest = new_certificate_opts[:hostname] || new_certificate_opts[:user]
  334.  
  335. certificate_file = File.join dest, "certificate_#{dest}.pem"
  336.  
  337. puts "Writing certificate to #{certificate_file}" if $DEBUG
  338.  
  339. File.open certificate_file, "w", 0644 do |f|
  340. f << certificate.to_pem
  341. end
  342.  
  343. return certificate_file
  344. end
  345. end # class QuickCert
  346.  
  347. #if __FILE__ == $0 then
  348.  
  349. $DEBUG = true
  350. CA = {}
  351. CERTS = []
  352. #load ARGV.shift || 'qc_config'
  353.  
  354. #CA[:certificate_authority_dir] = File.join($info[:program_directory], 'certificates')
  355.  
  356. CA[:certificate_authority_dir] = File.join(File.dirname(__FILE__) , 'certificates')
  357.  
  358. CA[:keypair_file] ||= File.join CA[:certificate_authority_dir], "private/cakeypair.pem"
  359. CA[:cert_file] ||= File.join CA[:certificate_authority_dir], "cacert.pem"
  360. CA[:serial_file] ||= File.join CA[:certificate_authority_dir], "serial"
  361. CA[:new_certs_dir] ||= File.join CA[:certificate_authority_dir], "newcerts"
  362. CA[:new_keypair_dir] ||= File.join CA[:certificate_authority_dir], "private/keypair_backup"
  363. CA[:crl_dir] ||= File.join CA[:certificate_authority_dir], "crl"
  364.  
  365. CA[:ca_cert_days] ||= 5 * 365 # five years
  366. CA[:ca_rsa_key_length] ||= 2048
  367.  
  368. CA[:cert_days] ||= 365 # one year
  369. CA[:cert_key_length_min] ||= 1024
  370. CA[:cert_key_length_max] ||= 2048
  371.  
  372. CA[:crl_file] ||= File.join CA[:crl_dir], "#{CA[:hostname]}.crl"
  373. CA[:crl_pem_file] ||= File.join CA[:crl_dir], "#{CA[:hostname]}.pem"
  374. CA[:crl_days] ||= 14
  375.  
  376. if CA[:name].nil?
  377. CA[:name] = [
  378. ['C', 'US', OpenSSL::ASN1::PRINTABLESTRING],
  379. ['O', CA[:domainname], OpenSSL::ASN1::UTF8STRING],
  380. ['OU', CA[:hostname], OpenSSL::ASN1::UTF8STRING],
  381. ]
  382. end
  383.  
  384. CERTS << {
  385. :type => 'server',
  386. :hostname => 'uriel',
  387. :password => '5678',
  388. }
  389.  
  390. CERTS << {
  391. :type => 'client',
  392. :user => 'drbrain',
  393. :email => 'drbrain@segment7.net',
  394. }
  395.  
  396.  
  397.  
  398. qc = QuickCert.new(CA)
  399. CERTS.each do |new_certificate_opts|
  400. qc.create_certificate(new_certificate_opts)
  401. end
  402. #end
Add Comment
Please, Sign In to add comment