File: //usr/local/rvm/src/rubygems-3.0.9/util/update_bundled_ca_certificates.rb
# frozen_string_literal: true
require 'net/http'
require 'openssl'
require 'fileutils'
URIS = [
URI('https://rubygems.org'),
URI('https://www.rubygems.org'),
URI('https://index.rubygems.org'),
URI('https://staging.rubygems.org'),
].freeze
HOSTNAMES_TO_MAP = [
'rubygems.org',
'index.rubygems.org'
].freeze
def connect_to(uri, store)
# None of the URIs are IPv6, so URI::Generic#hostname(ruby 1.9.3+) isn't needed
http = Net::HTTP.new uri.host, uri.port
http.use_ssl = uri.scheme.downcase == 'https'
http.ssl_version = :TLSv1_2
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
http.cert_store = store
http.get '/'
true
rescue OpenSSL::SSL::SSLError
false
end
def load_certificates(io)
cert_texts =
io.read.scan(/^-{5}BEGIN CERTIFICATE-{5}.*?^-{5}END CERTIFICATE-{5}/m)
cert_texts.map do |cert_text|
OpenSSL::X509::Certificate.new cert_text
end
end
def show_certificates(certificates)
certificates.each do |certificate|
p certificate.subject.to_a
end
end
def store_for(certificates)
store = OpenSSL::X509::Store.new
certificates.each do |certificate|
store.add_cert certificate
end
store
end
def test_certificates(certificates, uri)
1.upto certificates.length do |n|
puts "combinations of #{n} certificates"
certificates.combination(n).each do |combination|
match = test_uri uri, combination
if match
$needed_combinations << match
puts
puts match.map { |certificate| certificate.subject }
return
else
print '.'
end
end
puts
end
end
def test_uri(uri, certificates)
store = store_for certificates
verified = connect_to uri, store
return certificates if verified
nil
end
def hostname_certificate_mapping(certificates)
mapping = {}
HOSTNAMES_TO_MAP.each do |hostname|
uri = URI("https://#{hostname}")
certificates.each do |cert|
match = test_uri uri, [cert]
mapping[hostname] = cert if match && !mapping.values.include?(cert)
end
end
mapping
end
def write_certificates(certificates)
mapping = hostname_certificate_mapping(certificates)
mapping.each do |hostname, certificate|
subject = certificate.subject.to_a
name = (subject.assoc('CN') || subject.assoc('OU'))[1]
name = name.delete ' .-'
FileUtils.mkdir_p("lib/rubygems/ssl_certs/#{hostname}")
destination = "lib/rubygems/ssl_certs/#{hostname}/#{name}.pem"
warn "overwriting certificate #{name}" if File.exist? destination
open destination, 'w' do |io|
io.write certificate.to_pem
end
end
end
io =
if ARGV.empty?
open OpenSSL::X509::DEFAULT_CERT_FILE
else
ARGF
end
certificates = load_certificates io
puts "loaded #{certificates.length} certificates"
$needed_combinations = []
URIS.each do |uri|
puts uri
test_certificates certificates, uri
end
needed = $needed_combinations.flatten.uniq
write_certificates needed