#!/usr/bin/env ruby ## ## update_trusted_ca_list.rb ## ## A simple script to download the latest list of trusted certificate ## authorities from Mozilla, parse it, and output a PEM file containing ## them. If configured correctly and if there is a valid PEM file to ## start with, the download will be done using SSL/TLS with site ## certificate verification. If there is no already-trusted PEM file, ## then the download will be attempted WITHOUT encryption/verification. ## ## Available online at: ## http://www.aarongifford.com/computers/update_trusted_ca_list.rb.txt ## ## ## Written by Aaron D. Gifford - http://www.aarongifford.com/ ## ## Copyright (c) 2010 Aaron D. Gifford ## ## Permission is hereby granted, free of charge, to any person obtaining ## a copy of this software and associated documentation files (the ## "Software"), to deal in the Software without restriction, including ## without limitation the rights to use, copy, modify, merge, publish, ## distribute, sublicense, and/or sell copies of the Software, and to ## permit persons to whom the Software is furnished to do so, subject to ## the following conditions: ## ## The above copyright notice and this permission notice shall be ## included in all copies or substantial portions of the Software. ## ## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, ## EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF ## MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. ## IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY ## CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, ## TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE ## SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ## ## ===== START OF CONFIGURATION ===== CA_URI = 'https://mxr.mozilla.org/mozilla/source/security/nss/lib/ckfw/builtins/certdata.txt?raw=1' CA_OUT = 'mozilla_trusted_certificate_authority_list.pem' ## ===== END OF CONFIGURATION ===== require 'uri' require 'net/http' require 'net/https' require 'digest/md5' ## Do some set-up: uri = URI.parse(CA_URI) filename = File.expand_path(CA_OUT, CA_OUT[0,1] == '/' ? '' : File.dirname(__FILE__)) puts "Downloading list of trusted certificate authorities from:" puts uri ## Download the latest list of trusted certificate authorities from Mozilla: net = Net::HTTP.new(uri.host, uri.port) if uri.scheme == 'https' net.use_ssl = true if File.exists?(filename) net.verify_mode = OpenSSL::SSL::VERIFY_PEER net.ca_file = filename net.verify_depth = 5 secure = "# NOTICE: Source data was downloaded using SSL/TLS using full peer\n" + "# verification of the site certificate." else secure = "# WARNING: Source data was downloaded using SSL/TLS, but with NO peer\n" + "# verification of the site certificate." net.verify_mode = OpenSSL::SSL::VERIFY_NONE end else secure = "# WARNING: Source data was downloaded WITHOUT using SSL/TLS encryption\n" + "# and with NO verification of the site." end response = net.get(uri.request_uri) puts secure puts "Parsing list of trusted certificate authorities..." ## Initialize parser: state = 0 line_no = 1 cert = nil label = '' ## Open output file: out = File.open(filename, 'w') out.puts "# ======================================================================" out.puts "# TRUSTED CERTIFICATE AUTHORITY LIST RETRIEVED FROM:" out.puts "# #{uri}" out.puts "# Downloaded at #{Time.now}" out.puts "#" out.puts secure out.puts "#" out.puts "# Downloaded using a Ruby script by Aaron D. Gifford available at:" out.puts "# http://www.aarongifford.com/computers/update_trusted_ca_list.rb.txt" out.puts "# ======================================================================" ## Parse file: response.body.each_line do |line| line.strip! line_no += 1 case state when 0 if /BEGIN LICENSE BLOCK/.match(line) out.puts line state = 1 end when 1 out.puts line if /END LICENSE BLOCK/.match(line) state = 2 end when 2 if /^CVS_ID "(.*)"/.match(line) state = 3 out.puts "# #{$1}" end when 3 if /^CKA_LABEL UTF8 "(.*)"/.match(line) label = $1 state = 4 end when 4 if /^CKA_VALUE MULTILINE_OCTAL/.match(line) cert = '' state = 5 end when 5 if /^END/.match(line) ## process certificate... cert = cert.split('\\').delete_if{|x| x==''}.map{|x| x.to_i(8).chr}.join fingerprint = 'MD5 Fingerprint=' + Digest::MD5.hexdigest(cert).scan(/../).join(':').upcase cert = [cert].pack('m') out.puts out.puts "# CERTIFICATE FOR: \"#{label}\"" out.puts "-----BEGIN CERTIFICATE-----" out.puts cert out.puts "-----END CERTIFICATE-----" out.puts fingerprint out.puts state = 3 else cert += line end else raise "Unhandled line #{line_no}: '#{line}'" end end out.close puts "Finished"