/
update_bundled_ca_certificates.rb
137 lines (108 loc) · 2.87 KB
/
update_bundled_ca_certificates.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
# 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