Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Dynamically sign certificates for each ssl request

Installing the root.cer allows your browser to implicitly trust
ephemeral response's ssl proxy
  • Loading branch information...
commit e0f31c53f62af73c4e7de06fbd977964c3f304c8 1 parent 0a65277
authored April 10, 2012 sandro committed April 10, 2012

Showing 1 changed file with 44 additions and 10 deletions. Show diff stats Hide diff stats

  1. 54  lib/ephemeral_response/proxy.rb
54  lib/ephemeral_response/proxy.rb
@@ -260,6 +260,7 @@ def initialize(port=nil)
260 260
       @ios = []
261 261
       @server_thread = []
262 262
       @mutex = Mutex.new
  263
+      @certs = {}
263 264
       self.port = port || 44567
264 265
     end
265 266
 
@@ -267,20 +268,55 @@ def dir
267 268
       File.expand_path(File.dirname(__FILE__))
268 269
     end
269 270
 
270  
-    def certificate_path
271  
-      File.join(dir, "certificate.pem")
  271
+    def root_ca
  272
+      @root_ca ||= begin
  273
+        path = File.join(dir, "root.cer")
  274
+        OpenSSL::X509::Certificate.new(File.open(path))
  275
+      end
272 276
     end
273 277
 
274  
-    def key_path
275  
-      File.join(dir, "key.pem")
  278
+    def root_key
  279
+      @root_key ||= begin
  280
+        path = File.join(dir, "rootkey.pem")
  281
+        OpenSSL::PKey::RSA.new(File.open(path))
  282
+      end
276 283
     end
277 284
 
278  
-    def ssl_sock(sock)
  285
+    def make_or_get_cert_for(host)
  286
+      @certs[host] ||= make_cert_for(host)
  287
+    end
  288
+
  289
+    def make_cert_for(host)
  290
+      puts "making cert for #{host}"
  291
+      key = OpenSSL::PKey::RSA.new 2048
  292
+      cert = OpenSSL::X509::Certificate.new
  293
+      cert.version = 2
  294
+      cert.serial = Integer(rand * 1_000_000)
  295
+      cert.subject = OpenSSL::X509::Name.parse "/DC=org/DC=ruby-lang/CN=#{host}"
  296
+      cert.issuer = root_ca.subject # root CA is the issuer
  297
+      cert.public_key = key.public_key
  298
+      cert.not_before = Time.now - 60
  299
+      cert.not_after = cert.not_before + 1 * 365 * 24 * 60 * 60 # 1 years validity
  300
+      ef = OpenSSL::X509::ExtensionFactory.new
  301
+      ef.subject_certificate = cert
  302
+      ef.issuer_certificate = root_ca
  303
+      cert.add_extension(ef.create_extension("subjectKeyIdentifier","hash",false))
  304
+      cert.add_extension(ef.create_extension("extendedKeyUsage", "serverAuth"))
  305
+      cert.add_extension(ef.create_extension("basicConstraints","CA:FALSE"))
  306
+      cert.add_extension(ef.create_extension("keyUsage", "keyEncipherment"))
  307
+      cert.sign(root_key, OpenSSL::Digest::SHA1.new)
  308
+      [cert, key]
  309
+    end
  310
+
  311
+    def ssl_sock(sock, host)
  312
+      cert, key = make_or_get_cert_for(host)
279 313
       context = OpenSSL::SSL::SSLContext.new
280  
-      context.cert = OpenSSL::X509::Certificate.new(File.open(certificate_path))
281  
-      context.key = OpenSSL::PKey::RSA.new(File.open(key_path))
  314
+      context.cert = cert
  315
+      context.key = key
282 316
       ssl = OpenSSL::SSL::SSLSocket.new(sock, context)
283 317
       ssl.sync_close = true
  318
+      ssl.extend SSLTunnel
  319
+      ssl.tunnel_host = host
284 320
       ssl
285 321
     end
286 322
 
@@ -335,9 +371,7 @@ def handle(socket_ready)
335 371
           s.print(str) unless s.closed?
336 372
         end
337 373
         if request.ssl_tunnel?
338  
-          ssl = ssl_sock(s)
339  
-          ssl.extend SSLTunnel
340  
-          ssl.tunnel_host = request.host
  374
+          ssl = ssl_sock(s, request.host)
341 375
           mutex.synchronize { ios << ssl }
342 376
         else
343 377
           s.close

0 notes on commit e0f31c5

Please sign in to comment.
Something went wrong with that request. Please try again.