PageRenderTime 47ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/util/update_bundled_ca_certificates.rb

https://github.com/technicalpickles/rubygems
Ruby | 136 lines | 105 code | 29 blank | 2 comment | 7 complexity | 40b2a4c89ba7adc813696ebd4fa44f95 MD5 | raw file
  1. # frozen_string_literal: true
  2. require 'net/http'
  3. require 'openssl'
  4. require 'fileutils'
  5. URIS = [
  6. URI('https://rubygems.org'),
  7. URI('https://www.rubygems.org'),
  8. URI('https://index.rubygems.org'),
  9. URI('https://staging.rubygems.org'),
  10. ].freeze
  11. HOSTNAMES_TO_MAP = [
  12. 'rubygems.org',
  13. ].freeze
  14. def connect_to(uri, store)
  15. # None of the URIs are IPv6, so URI::Generic#hostname(ruby 1.9.3+) isn't needed
  16. http = Net::HTTP.new uri.host, uri.port
  17. http.use_ssl = uri.scheme.downcase == 'https'
  18. http.ssl_version = :TLSv1_2
  19. http.verify_mode = OpenSSL::SSL::VERIFY_PEER
  20. http.cert_store = store
  21. http.get '/'
  22. true
  23. rescue OpenSSL::SSL::SSLError
  24. false
  25. end
  26. def load_certificates(io)
  27. cert_texts =
  28. io.read.scan(/^-{5}BEGIN CERTIFICATE-{5}.*?^-{5}END CERTIFICATE-{5}/m)
  29. cert_texts.map do |cert_text|
  30. OpenSSL::X509::Certificate.new cert_text
  31. end
  32. end
  33. def show_certificates(certificates)
  34. certificates.each do |certificate|
  35. p certificate.subject.to_a
  36. end
  37. end
  38. def store_for(certificates)
  39. store = OpenSSL::X509::Store.new
  40. certificates.each do |certificate|
  41. store.add_cert certificate
  42. end
  43. store
  44. end
  45. def test_certificates(certificates, uri)
  46. 1.upto certificates.length do |n|
  47. puts "combinations of #{n} certificates"
  48. certificates.combination(n).each do |combination|
  49. match = test_uri uri, combination
  50. if match
  51. $needed_combinations << match
  52. puts
  53. puts match.map {|certificate| certificate.subject }
  54. return
  55. else
  56. print '.'
  57. end
  58. end
  59. puts
  60. end
  61. end
  62. def test_uri(uri, certificates)
  63. store = store_for certificates
  64. verified = connect_to uri, store
  65. return certificates if verified
  66. nil
  67. end
  68. def hostname_certificate_mapping(certificates)
  69. mapping = {}
  70. HOSTNAMES_TO_MAP.each do |hostname|
  71. uri = URI("https://#{hostname}")
  72. certificates.each do |cert|
  73. match = test_uri uri, [cert]
  74. mapping[hostname] = cert if match && !mapping.values.include?(cert)
  75. end
  76. end
  77. mapping
  78. end
  79. def write_certificates(certificates)
  80. mapping = hostname_certificate_mapping(certificates)
  81. mapping.each do |hostname, certificate|
  82. subject = certificate.subject.to_a
  83. name = (subject.assoc('CN') || subject.assoc('OU'))[1]
  84. name = name.delete ' .-'
  85. FileUtils.mkdir_p("lib/rubygems/ssl_certs/#{hostname}")
  86. destination = "lib/rubygems/ssl_certs/#{hostname}/#{name}.pem"
  87. warn "overwriting certificate #{name}" if File.exist? destination
  88. File.open destination, 'w' do |io|
  89. io.write certificate.to_pem
  90. end
  91. end
  92. end
  93. io =
  94. if ARGV.empty?
  95. File.open OpenSSL::X509::DEFAULT_CERT_FILE
  96. else
  97. ARGF
  98. end
  99. certificates = load_certificates io
  100. puts "loaded #{certificates.length} certificates"
  101. $needed_combinations = []
  102. URIS.each do |uri|
  103. puts uri
  104. test_certificates certificates, uri
  105. end
  106. needed = $needed_combinations.flatten.uniq
  107. write_certificates needed