0

I'm having some trouble navigating the confusing world of Ruby OpenSSL libraries.

The desired end result would be dynamically creating SSL server certs and keys for use with WEBrick::HTTPProxyServer, each with a CN corresponding to the requested domain and all sharing the same CA.

The reason for this is to allow for an HTTP proxy to intercept and monitor HTTPS traffic of websites that have the HSTS header enabled, by first loading the CACert into the browser, thus making my self-signed certificates look legit for each given site.

If anyone's got experience with this sort of thing code examples would be greatly appreciated.

Thanks in advance.

PS. Nothing sinister going on here, just improving a feature of a penetration testing software.

jww
  • 97,681
  • 90
  • 411
  • 885
Tasos Laskos
  • 345
  • 1
  • 7
  • *"... each with a CN corresponding to the requested domain..."* - browsers will reject a certificate issued like that. They require server names in the *Subject Alternate Name* (SAN), not the *Common Name* (CN). Related, see [How to create a self-signed certificate with openssl?](http://stackoverflow.com/a/27931596/608639). It explains the rules to make most user agents happy. – jww Jun 15 '15 at 10:52

1 Answers1

1

Figured it out. :)

ca     = OpenSSL::X509::Certificate.new( File.read( INTERCEPTOR_CA_CERTIFICATE ) )
ca_key = OpenSSL::PKey::RSA.new( File.read( INTERCEPTOR_CA_KEY ) )

keypair = OpenSSL::PKey::RSA.new( 4096 )

req            = OpenSSL::X509::Request.new
req.version    = 0
req.subject    = OpenSSL::X509::Name.parse(
    "CN=www.origin-server.com/O=Arachni/OU=Proxy/L=Athens/ST=Attika/C=GR"
)
req.public_key = keypair.public_key
req.sign( keypair, OpenSSL::Digest::SHA1.new )

cert            = OpenSSL::X509::Certificate.new
cert.version    = 2
cert.serial     = rand( 999999 )
cert.not_before = Time.new
cert.not_after  = cert.not_before + (60 * 60 * 24 * 365)
cert.public_key = req.public_key
cert.subject    = req.subject
cert.issuer     = ca.subject

ef = OpenSSL::X509::ExtensionFactory.new
ef.subject_certificate = cert
ef.issuer_certificate  = ca

cert.extensions = [
    ef.create_extension( 'basicConstraints', 'CA:FALSE', true ),
    ef.create_extension( 'extendedKeyUsage', 'serverAuth', false ),
    ef.create_extension( 'subjectKeyIdentifier', 'hash' ),
    ef.create_extension( 'authorityKeyIdentifier', 'keyid:always,issuer:always' ),
    ef.create_extension( 'keyUsage',
        %w(nonRepudiation digitalSignature
        keyEncipherment dataEncipherment).join(","),
        true
    )
]
cert.sign( ca_key, OpenSSL::Digest::SHA1.new )

Helpful resources:

  1. http://users.nccs.gov/~fwang2/ruby/ruby_ssl.html
  2. http://acidx.net/wordpress/2012/09/creating-a-certification-authority-and-a-server-certificate-on-ubuntu/
Tasos Laskos
  • 345
  • 1
  • 7
  • If you put the sever names in the *Subject Alternate Name (SAN)* (and not the *Common Name (CN)*), then I would upvote this answer. As is, the code above relies on deprecated behavior by both the IETF and CA/Browser forums. it will not work in modern browsers, but I'm not sure if your interception proxy targets or accommodates browsers. – jww Jun 15 '15 at 13:24
  • @jww So "CN=#{host}/subjectAltName=#{host}" should keep most UAs happy? – Tasos Laskos Jun 16 '15 at 01:43