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

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