-
-
Notifications
You must be signed in to change notification settings - Fork 1.7k
/
update_bundled_ca_certificates.rb
138 lines (108 loc) · 2.86 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'),
]
HOSTNAMES_TO_MAP = [
'rubygems.org',
'index.rubygems.org'
]
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 then
$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? then
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