You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

generate-test-material.rb 5.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  1. #!/usr/bin/env ruby
  2. $:.unshift File.expand_path File.join File.dirname(__FILE__), '../lib'
  3. require 'rubygems'
  4. require 'bundler/setup'
  5. require 'openssl'
  6. require 'socket'
  7. require 'cryptcheck'
  8. LOG = ::CryptCheck::Logger
  9. LOG.level = ENV['LOG'] || :info
  10. OpenSSL::PKey::EC.send :alias_method, :private?, :private_key?
  11. CA_KEY_USAGE = %w(keyCertSign cRLSign)
  12. CA_EXTENDED_KEY_USAGE = []
  13. CA_NETSCAPE_CERT_TYPE = %w(sslCA)
  14. RSA_KEY_USAGE = %w(digitalSignature)
  15. ECDSA_KEY_USAGE = %w(digitalSignature)
  16. CERT_EXTENDED_KEY_USAGE = %w(serverAuth)
  17. CERT_NETSCAPE_CERT_TYPE = %w(server)
  18. def certificate(key, subject, san: %w(DNS:localhost),
  19. from: Time.utc(2000, 1, 1), to: Time.utc(2001, 1, 1),
  20. issuer: nil, ca: false,
  21. key_usage: ECDSA_KEY_USAGE, extended_key_usage: CERT_EXTENDED_KEY_USAGE,
  22. netscape_cert_type: CERT_NETSCAPE_CERT_TYPE,
  23. hash: OpenSSL::Digest::SHA512, extensions: [])
  24. cert = OpenSSL::X509::Certificate.new
  25. cert.version = 3
  26. cert.serial = rand 2**63 .. 2**64
  27. cert.not_before = from if from
  28. cert.not_after = to if to
  29. cert.public_key = case key
  30. when OpenSSL::PKey::EC
  31. curve = key.group.curve_name
  32. public = OpenSSL::PKey::EC.new curve
  33. public.public_key = key.public_key
  34. public
  35. else
  36. key.public_key
  37. end
  38. name = OpenSSL::X509::Name.parse subject
  39. cert.subject = name
  40. issuer = { cert: cert, key: key } unless issuer
  41. cert.issuer = issuer[:cert].subject
  42. extension_factory = OpenSSL::X509::ExtensionFactory.new nil, cert
  43. extension_factory.subject_certificate = cert
  44. extension_factory.issuer_certificate = issuer[:cert]
  45. cert.add_extension extension_factory.create_extension 'basicConstraints', "CA:#{ca.to_s.upcase}", true
  46. cert.add_extension extension_factory.create_extension 'keyUsage', key_usage.uniq.join(','), true unless key_usage.empty?
  47. cert.add_extension extension_factory.create_extension 'extendedKeyUsage', extended_key_usage.uniq.join(','), true unless extended_key_usage.empty?
  48. cert.add_extension extension_factory.create_extension 'nsCertType', netscape_cert_type.uniq.join(',') unless netscape_cert_type.empty?
  49. cert.add_extension extension_factory.create_extension 'subjectKeyIdentifier', 'hash'
  50. cert.add_extension extension_factory.create_extension 'authorityKeyIdentifier', 'keyid:always'
  51. cert.add_extension extension_factory.create_extension 'subjectAltName', san.uniq.join(',') unless san.empty?
  52. extensions.each { |e| cert.add_extension e }
  53. cert.sign issuer[:key], hash.new
  54. cert
  55. end
  56. def generate_dh(size)
  57. filename = "spec/resources/dh-#{size}.pem"
  58. return if File.exist? filename
  59. LOG.info "Generate dh-#{size}"
  60. File.write filename, OpenSSL::PKey::DH.new(size).to_pem
  61. end
  62. def generate_material(name, key, cert)
  63. key_file = "spec/resources/#{name}.pem"
  64. key = if File.exists?(key_file)
  65. OpenSSL::PKey.read File.read key_file
  66. else
  67. LOG.info "Generate #{name} key"
  68. key = key.call
  69. File.write key_file, key.to_pem
  70. key
  71. end
  72. cert_file = "spec/resources/#{name}.crt"
  73. cert = if File.exist?(cert_file)
  74. OpenSSL::X509::Certificate.new File.read cert_file
  75. else
  76. LOG.info "Generate #{name} cert"
  77. cert = cert.call key
  78. File.write cert_file, cert.to_pem
  79. cert
  80. end
  81. [key, cert]
  82. end
  83. ca_key, ca_cert = generate_material 'ca',
  84. -> () { OpenSSL::PKey::EC.new('secp384r1').generate_key },
  85. -> (k) { certificate k, '/CN=ca', ca: true, key_usage: CA_KEY_USAGE, extended_key_usage: CA_EXTENDED_KEY_USAGE, netscape_cert_type: CA_NETSCAPE_CERT_TYPE }
  86. intermediate_key, intermediate_cert = generate_material 'intermediate',
  87. -> () { OpenSSL::PKey::EC.new('secp384r1').generate_key },
  88. -> (key) { certificate key, '/CN=intermediate', ca: true, issuer: { cert: ca_cert, key: ca_key }, key_usage: CA_KEY_USAGE, extended_key_usage: CA_EXTENDED_KEY_USAGE, netscape_cert_type: CA_NETSCAPE_CERT_TYPE }
  89. issuer = { cert: intermediate_cert, key: intermediate_key }
  90. [512, 768, 1024, 2048, 3072, 4096].each do |s|
  91. generate_material "rsa-#{s}",
  92. -> () { OpenSSL::PKey::RSA.new s },
  93. -> (key) { certificate key, "/CN=rsa-#{s}", issuer: issuer, key_usage: RSA_KEY_USAGE }
  94. generate_dh s
  95. end
  96. CryptCheck::Tls::Curve.each do |c|
  97. c = c.name
  98. generate_material "ecdsa-#{c}",
  99. -> () { OpenSSL::PKey::EC.new(c).generate_key },
  100. -> (key) { certificate key, "/CN=ecdsa-#{c}", issuer: issuer }
  101. end
  102. generate_material 'self-signed',
  103. -> { OpenSSL::PKey::EC.new('prime256v1').generate_key },
  104. -> (key) { certificate key, '/CN=self-signed', ca: true,
  105. key_usage: CA_KEY_USAGE+ECDSA_KEY_USAGE,
  106. extended_key_usage: CA_EXTENDED_KEY_USAGE + CERT_EXTENDED_KEY_USAGE,
  107. netscape_cert_type: CA_NETSCAPE_CERT_TYPE + CERT_NETSCAPE_CERT_TYPE }
  108. # Require patched OpenSSL to be able to issue MD5 certificates
  109. generate_material 'md5',
  110. -> { OpenSSL::PKey::EC.new('prime256v1').generate_key },
  111. -> (key) { certificate key, '/CN=md5', issuer: issuer, hash: OpenSSL::Digest::MD5 }
  112. # Require patched OpenSSL to be able to issue SHA1 certificates
  113. generate_material 'sha1',
  114. -> { OpenSSL::PKey::EC.new('prime256v1').generate_key },
  115. -> (key) { certificate key, '/CN=sha1', issuer: issuer, hash: OpenSSL::Digest::SHA1 }
  116. must_staple = OpenSSL::X509::Extension.new '1.3.6.1.5.5.7.1.24', '0', true
  117. generate_material 'must-staple',
  118. -> () { OpenSSL::PKey::EC.new('prime256v1').generate_key },
  119. -> (key) { certificate key, '/CN=must-staple', issuer: issuer, extensions: [must_staple] }