evil-proxy 0.0.2 → 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: f4c501bef2b64e58b8393c8cc73a6516739f8b87
4
- data.tar.gz: 9f8c54251a54e7f68cb47b04363ea746ed0956e1
3
+ metadata.gz: 53684305896f9e807ca7a800ce16365d1075047b
4
+ data.tar.gz: 9f3436b932cd284a97d572630b8eef0c82f8ccba
5
5
  SHA512:
6
- metadata.gz: dc6113ee6f3043cc8ddd8e70dc4423dc717281c5eb589cbc878531fa2019b280b09d1fbb8c9ae789d7b2c1ce8d93d4ad44f061a7c6ff8b67506838c44dc85b4c
7
- data.tar.gz: c598d95d33cdbaf4ec131697f3b95015f39f5a31dda3fe8043522d912d9ed9bbf0a0ad903fa51e251def4f3f7829facaa4af45aa4bc20f5873028408312cc8e1
6
+ metadata.gz: f4ab48b584aedfb81bdd292a29764fbcd17a2b55f91ad648ef1c07f572ccc5a8552bcea270a088c5766dc806bbf6f1243b6d77e0c34e4e9ed9fcc70503ff15f8
7
+ data.tar.gz: 6f4f16d5c257a680e7e13a6b8090ffa2bc561f324e75e7b29d302ee36f5dcd146ba761333942066042ffdd6af54900b2e34f2e30da7cfe4ead31d7fa9f3852fa
data/.gitignore CHANGED
@@ -10,6 +10,7 @@ coverage
10
10
  doc/
11
11
  lib/bundler/man
12
12
  pkg
13
+ certs
13
14
  rdoc
14
15
  spec/reports
15
16
  test/tmp
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # EvilProxy
2
2
 
3
- A ruby http proxy to do :imp: things.
3
+ A ruby http/https proxy, with SSL MITM support to do :imp: things.
4
4
 
5
5
  ## Installation
6
6
 
@@ -18,6 +18,45 @@ Or install it yourself as:
18
18
 
19
19
  ## Usage
20
20
 
21
+ #### MITMProxyServer
22
+ `MITMProxyServer` is a subclass of `HTTPProxyServer`, so it also has the callback & plugin system, this proxy will embed a mini CA, which generates certificates on the fly, so you may need to import the CA certificate (./certs/CA/cacert.pem) into your browser.
23
+
24
+ ```ruby
25
+ require 'evil-proxy'
26
+
27
+ proxy = EvilProxy::MITMProxyServer.new Port: 8080
28
+ proxy.start
29
+ ```
30
+
31
+ Without import the CA certificate
32
+ ```shell
33
+ $ https_proxy=http://localhost:8080 curl https://github.com
34
+ # =>
35
+ # curl: (60) SSL certificate problem: Invalid certificate chain
36
+ # More details here: http://curl.haxx.se/docs/sslcerts.html
37
+ #
38
+ # curl performs SSL certificate verification by default, using a "bundle"
39
+ # of Certificate Authority (CA) public keys (CA certs). If the default
40
+ # bundle file isn't adequate, you can specify an alternate file
41
+ # using the --cacert option.
42
+ # If this HTTPS server uses a certificate signed by a CA represented in
43
+ # the bundle, the certificate verification probably failed due to a
44
+ # problem with the certificate (it might be expired, or the name might
45
+ # not match the domain name in the URL).
46
+ # If you'd like to turn off curl's verification of the certificate, use
47
+ # the -k (or --insecure) option.
48
+ ```
49
+
50
+ ```shell
51
+ $ https_proxy=http://localhost:8080 curl https://github.com --insecure
52
+ # =>
53
+ # <!DOCTYPE html>
54
+ # <html lang="en" class="">
55
+ # ...
56
+ ```
57
+
58
+ So you can intercept and modify https traffic, ie: requests & responses.
59
+
21
60
  #### Basic usage: hooks
22
61
 
23
62
  ```ruby
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'evil-proxy'
4
+
5
+ logger = WEBrick::Log.new(nil, 5)
6
+
7
+ proxy = EvilProxy::MITMProxyServer.new Port: 8080, Logger: logger
8
+ proxy.start
@@ -8,8 +8,8 @@ Gem::Specification.new do |spec|
8
8
  spec.version = EvilProxy::VERSION
9
9
  spec.authors = ["Theo"]
10
10
  spec.email = ["bbtfrr@gmail.com"]
11
- spec.summary = %q{A ruby http proxy to do EVIL things.}
12
- spec.description = %q{A ruby http proxy to do EVIL things.}
11
+ spec.summary = %q{A ruby http/https proxy to do EVIL things.}
12
+ spec.description = %q{A ruby http/https proxy, with SSL MITM support.}
13
13
  spec.homepage = "https://github.com/bbtfr/evil-proxy"
14
14
  spec.license = "MIT"
15
15
 
@@ -20,4 +20,5 @@ Gem::Specification.new do |spec|
20
20
 
21
21
  spec.add_development_dependency "bundler", "~> 1.6"
22
22
  spec.add_development_dependency "rake"
23
+ spec.add_development_dependency "pry-byebug"
23
24
  end
@@ -1,5 +1,6 @@
1
1
  require "evil-proxy/version"
2
2
  require "evil-proxy/httpproxy"
3
+ require "evil-proxy/mitmproxy"
3
4
  require "evil-proxy/httprequest"
4
5
 
5
6
  module EvilProxy
@@ -0,0 +1,54 @@
1
+ require 'webrick'
2
+ require 'webrick/https'
3
+ require 'webrick/httpproxy'
4
+ require 'openssl'
5
+
6
+ require 'pry-byebug'
7
+
8
+ class EvilProxy::AgentProxyServer < EvilProxy::HTTPProxyServer
9
+
10
+ def initialize_callbacks config
11
+ @mitm_server = config[:MITMProxyServer]
12
+ end
13
+
14
+ def fire key, *args
15
+ @mitm_server.fire key, *args, self
16
+ end
17
+
18
+ def perform_proxy_request(req, res)
19
+ uri = req.request_uri
20
+ path = uri.path.dup
21
+ path << "?" << uri.query if uri.query
22
+ header = Hash.new
23
+ choose_header(req, header)
24
+ response = nil
25
+
26
+ http = Net::HTTP.new(uri.host, uri.port)
27
+ http.use_ssl = true
28
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
29
+ http.start do
30
+ if @config[:ProxyTimeout]
31
+ ################################## these issues are
32
+ http.open_timeout = 30 # secs # necessary (maybe because
33
+ http.read_timeout = 60 # secs # Ruby's bug, but why?)
34
+ ##################################
35
+ end
36
+
37
+ response = yield(http, path, header)
38
+ end
39
+
40
+ # Persistent connection requirements are mysterious for me.
41
+ # So I will close the connection in every response.
42
+ res['proxy-connection'] = "close"
43
+ res['connection'] = "close"
44
+
45
+ # Convert Net::HTTP::HTTPResponse to WEBrick::HTTPResponse
46
+ res.status = response.code.to_i
47
+ choose_header(response, res)
48
+ set_cookie(response, res)
49
+ res.body = response.body
50
+ end
51
+
52
+ alias_method :service, :proxy_service
53
+
54
+ end
@@ -2,12 +2,14 @@ require 'webrick'
2
2
  require 'webrick/httpproxy'
3
3
 
4
4
  class EvilProxy::HTTPProxyServer < WEBrick::HTTPProxyServer
5
+ attr_reader :callbacks
6
+
5
7
  VALID_CALBACKS = Array.new
6
8
  DEFAULT_CALLBACKS = Hash.new
7
9
 
8
- def initialize *args
9
- initialize_callbacks
10
- fire :when_initialize, *args
10
+ def initialize config = {}, default = WEBrick::Config::HTTP
11
+ initialize_callbacks config
12
+ fire :when_initialize, config, default
11
13
  super
12
14
  end
13
15
 
@@ -27,7 +29,7 @@ class EvilProxy::HTTPProxyServer < WEBrick::HTTPProxyServer
27
29
  end
28
30
  end
29
31
 
30
- def proxy_service req, res
32
+ def service req, res
31
33
  fire :before_request, req
32
34
  super
33
35
  fire :before_response, req, res
@@ -64,7 +66,7 @@ class EvilProxy::HTTPProxyServer < WEBrick::HTTPProxyServer
64
66
  end
65
67
 
66
68
  private
67
- def initialize_callbacks
69
+ def initialize_callbacks config
68
70
  @callbacks = Hash.new
69
71
  DEFAULT_CALLBACKS.each do |key, callbacks|
70
72
  @callbacks[key] = callbacks.clone
@@ -1,11 +1,5 @@
1
- WEBrick::HTTPRequest.class_eval do
2
- alias_method :original_body, :body
3
- def body
4
- @evil_body || original_body
5
- end
6
-
7
- def body= body
8
- @evil_body = body
9
- end
1
+ require 'webrick/httprequest'
10
2
 
3
+ WEBrick::HTTPRequest.class_eval do
4
+ attr_writer :body, :unparsed_uri
11
5
  end
@@ -0,0 +1,116 @@
1
+ require 'evil-proxy/httpproxy'
2
+ require 'evil-proxy/agentproxy'
3
+ require 'evil-proxy/quickcert'
4
+
5
+ class EvilProxy::MITMProxyServer < EvilProxy::HTTPProxyServer
6
+
7
+ def initialize config
8
+ super
9
+ @mitm_servers = {}
10
+ @mitm_port = 4433
11
+ end
12
+
13
+ def ca
14
+ return @ca if @ca
15
+ logger.info "Create CA"
16
+
17
+ ca_config = {}
18
+ ca_config[:hostname] = 'ca'
19
+ ca_config[:domainname] = 'mitm.proxy'
20
+ ca_config[:password] = 'password'
21
+ ca_config[:CA_dir] ||= File.join(Dir.pwd, "certs/CA")
22
+
23
+ ca_config[:keypair_file] ||= File.join ca_config[:CA_dir], "private/cakeypair.pem"
24
+ ca_config[:cert_file] ||= File.join ca_config[:CA_dir], "cacert.pem"
25
+ ca_config[:serial_file] ||= File.join ca_config[:CA_dir], "serial"
26
+ ca_config[:new_certs_dir] ||= File.join ca_config[:CA_dir], "newcerts"
27
+ ca_config[:new_keypair_dir] ||= File.join ca_config[:CA_dir], "private/keypair_backup"
28
+ ca_config[:crl_dir] ||= File.join ca_config[:CA_dir], "crl"
29
+
30
+ ca_config[:ca_cert_days] ||= 5 * 365 # five years
31
+ ca_config[:ca_rsa_key_length] ||= 2048
32
+
33
+ ca_config[:cert_days] ||= 365 # one year
34
+ ca_config[:cert_key_length_min] ||= 1024
35
+ ca_config[:cert_key_length_max] ||= 2048
36
+
37
+ ca_config[:crl_file] ||= File.join ca_config[:crl_dir], "#{ca_config[:hostname]}.crl"
38
+ ca_config[:crl_pem_file] ||= File.join ca_config[:crl_dir], "#{ca_config[:hostname]}.pem"
39
+ ca_config[:crl_days] ||= 14
40
+
41
+ if ca_config[:name].nil?
42
+ ca_config[:name] = [
43
+ ['C', 'US', OpenSSL::ASN1::PRINTABLESTRING],
44
+ ['O', ca_config[:domainname], OpenSSL::ASN1::UTF8STRING],
45
+ ['OU', ca_config[:hostname], OpenSSL::ASN1::UTF8STRING],
46
+ ]
47
+ end
48
+
49
+ @ca = QuickCert.new ca_config
50
+ end
51
+
52
+ def create_self_signed_cert host
53
+ cn = [["C", "US"], ["O", host], ["CN", host]]
54
+ comment = "Generated by Ruby/OpenSSL/MITMProxyServer"
55
+ name = OpenSSL::X509::Name.new(cn)
56
+ hostname = name.to_s.scan(/CN=([\w.]+)/)[0][0]
57
+
58
+ logger.info "Create cert for #{hostname}"
59
+ cert_config = { type: 'server', hostname: hostname }
60
+ cert_file, cert, key = ca.create_cert(cert_config)
61
+
62
+ return cert, key
63
+ end
64
+
65
+ def retry_start_agent_server config
66
+ mitm_server = nil
67
+ 10.times do
68
+ begin
69
+ # XXX: ask system for an unused port
70
+ config = config.merge(Port: @mitm_port)
71
+ mitm_server = EvilProxy::AgentProxyServer.new config
72
+ rescue Errno::EADDRINUSE
73
+ ensure
74
+ @mitm_port += 1
75
+ return mitm_server if mitm_server
76
+ end
77
+ end
78
+ raise RuntimeError, "No avaliable port found, stop retrying"
79
+ end
80
+
81
+ def start_mitm_server unparsed_uri, host, port
82
+ if @mitm_servers[unparsed_uri]
83
+ return @mitm_servers[unparsed_uri].config[:Port]
84
+ else
85
+ cert, key = create_self_signed_cert host
86
+ agent_config = self.config.merge(
87
+ MITMProxyServer: self,
88
+ SSLEnable: true,
89
+ SSLVerifyClient: OpenSSL::SSL::VERIFY_NONE,
90
+ SSLCertificate: cert,
91
+ SSLPrivateKey: key,
92
+ )
93
+ mitm_server = retry_start_agent_server agent_config
94
+
95
+ @mitm_servers[unparsed_uri] = mitm_server
96
+
97
+ Thread.new do mitm_server.start end
98
+ return mitm_server.config[:Port]
99
+ end
100
+ end
101
+
102
+ def do_MITM req, res
103
+ unparsed_uri = req.unparsed_uri
104
+ host, port = unparsed_uri.split(":")
105
+ port ||= 443
106
+
107
+ mitm_port = start_mitm_server unparsed_uri, host, port
108
+ req.unparsed_uri = "127.0.0.1:#{mitm_port}"
109
+ end
110
+
111
+ def do_CONNECT req, res
112
+ do_MITM req, res
113
+ super
114
+ end
115
+
116
+ end
@@ -0,0 +1,349 @@
1
+ require 'openssl'
2
+ # QuickCert from http://segment7.net/projects/ruby/QuickCert/
3
+ # QuickCert allows you to quickly and easily create SSL
4
+ # certificates. It uses a simple configuration file to generate
5
+ # self-signed client and server certificates.
6
+ #
7
+ # QuickCert is a compilation of NAKAMURA Hiroshi's post to
8
+ # ruby-talk number 89917:
9
+ #
10
+ # http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/89917
11
+ #
12
+ # the example scripts referenced in the above post, and
13
+ # gen_csr.rb from Ruby's OpenSSL examples.
14
+ #
15
+ # A simple QuickCert configuration file looks like:
16
+ #
17
+ # full_hostname = `hostname`.strip
18
+ # domainname = full_hostname.split('.')[1..-1].join('.')
19
+ # hostname = full_hostname.split('.')[0]
20
+ #
21
+ # CA[:hostname] = hostname
22
+ # CA[:domainname] = domainname
23
+ # CA[:CA_dir] = File.join Dir.pwd, "CA"
24
+ # CA[:password] = '1234'
25
+ #
26
+ # CERTS << {
27
+ # :type => 'server',
28
+ # :hostname => 'uriel',
29
+ # :password => '5678',
30
+ # }
31
+ #
32
+ # CERTS << {
33
+ # :type => 'client',
34
+ # :user => 'drbrain',
35
+ # :email => 'drbrain@segment7.net',
36
+ # }
37
+ #
38
+ # This configuration will create a Certificate Authority in a
39
+ # 'CA' directory in the current directory, a server certificate
40
+ # with password '5678' for the server 'uriel' in a directory
41
+ # named 'uriel', and a client certificate for drbrain in the
42
+ # directory 'drbrain' with no password.
43
+ #
44
+ # There are additional SSL knobs you can tweak in the
45
+ # qc_defaults.rb file.
46
+ #
47
+ # To generate the certificates, simply create a qc_config file
48
+ # where you want the certificate directories to be created, then
49
+ # run QuickCert.
50
+ #
51
+ # QuickCert's homepage is:
52
+ # http://segment7.net/projects/ruby/QuickCert/
53
+
54
+ class QuickCert
55
+
56
+ ##
57
+ # QuickCert Version
58
+
59
+ VERSION = "1.0.2"
60
+ CERT_DIR = File.join(Dir.pwd, "certs")
61
+ Dir.mkdir(CERT_DIR) unless File.exists?(CERT_DIR)
62
+
63
+ ##
64
+ # Creates a new QuickCert instance using the Certificate
65
+ # Authority described in +ca_config+. If there is no CA at
66
+ # ca_config[:CA_dir], then QuickCert will initialize a new one.
67
+
68
+ def initialize(ca_config)
69
+ @ca_config = ca_config
70
+
71
+ create_ca
72
+ end
73
+
74
+ ##
75
+ # Creates a new certificate from +cert_config+ that is signed
76
+ # by the CA.
77
+
78
+ def create_cert(cert_config)
79
+ dest = cert_config[:hostname] || cert_config[:user]
80
+ key_file = "#{CERT_DIR}/#{dest}/#{dest}_keypair.pem"
81
+ cert_file = "#{CERT_DIR}/#{dest}/cert_#{dest}.pem"
82
+ if File.exists?(cert_file) && File.exists?(key_file)
83
+ key = OpenSSL::PKey::RSA.new(File.read(key_file))
84
+ cert = OpenSSL::X509::Certificate.new(File.read(cert_file))
85
+ else
86
+ cert_keypair, key = create_key(cert_config)
87
+ cert_csr = create_csr(cert_config, cert_keypair)
88
+ cert_file, cert = sign_cert(cert_config, cert_keypair, cert_csr)
89
+ end
90
+ return cert_file, cert, key
91
+ end
92
+
93
+ ##
94
+ # Creates a new Certificate Authority from @ca_config if it
95
+ # does not already exist at ca_config[:CA_dir].
96
+
97
+ def create_ca
98
+ return if File.exists? @ca_config[:CA_dir]
99
+
100
+ Dir.mkdir @ca_config[:CA_dir]
101
+
102
+ Dir.mkdir File.join(@ca_config[:CA_dir], 'private'), 0700
103
+ Dir.mkdir File.join(@ca_config[:CA_dir], 'newcerts')
104
+ Dir.mkdir File.join(@ca_config[:CA_dir], 'crl')
105
+
106
+ File.open @ca_config[:serial_file], 'w' do |f| f << "#{Time.now.to_i}" end
107
+
108
+ puts "Generating CA keypair" if $DEBUG
109
+ keypair = OpenSSL::PKey::RSA.new @ca_config[:ca_rsa_key_length]
110
+
111
+ cert = OpenSSL::X509::Certificate.new
112
+ name = @ca_config[:name].dup << ['CN', 'CA']
113
+ cert.subject = cert.issuer = OpenSSL::X509::Name.new(name)
114
+ cert.not_before = Time.now
115
+ cert.not_after = Time.now + @ca_config[:ca_cert_days] * 24 * 60 * 60
116
+ cert.public_key = keypair.public_key
117
+ cert.serial = 0x0
118
+ cert.version = 2 # X509v3
119
+
120
+ ef = OpenSSL::X509::ExtensionFactory.new
121
+ ef.subject_certificate = cert
122
+ ef.issuer_certificate = cert
123
+ cert.extensions = [
124
+ ef.create_extension("basicConstraints","CA:TRUE", true),
125
+ ef.create_extension("nsComment","Ruby/OpenSSL Generated Certificate"),
126
+ ef.create_extension("subjectKeyIdentifier", "hash"),
127
+ ef.create_extension("keyUsage", "cRLSign,keyCertSign", true),
128
+ ]
129
+ cert.add_extension ef.create_extension("authorityKeyIdentifier",
130
+ "keyid:always,issuer:always")
131
+ cert.sign(keypair, OpenSSL::Digest::SHA1.new)
132
+
133
+ keypair_export = keypair.export(OpenSSL::Cipher::DES.new(:EDE3, :CBC), @ca_config[:password])
134
+
135
+ puts "Writing keypair to #{@ca_config[:keypair_file]}" if $DEBUG
136
+ File.open @ca_config[:keypair_file], "w", 0400 do |fp|
137
+ fp << keypair_export
138
+ end
139
+
140
+ puts "Writing cert to #{@ca_config[:cert_file]}" if $DEBUG
141
+ File.open @ca_config[:cert_file], "w", 0644 do |f|
142
+ f << cert.to_pem
143
+ end
144
+
145
+ puts "Done generating certificate for #{cert.subject}" if $DEBUG
146
+ end
147
+
148
+ ##
149
+ # Creates a new RSA key from +cert_config+.
150
+
151
+ def create_key(cert_config)
152
+ dest = cert_config[:hostname] || cert_config[:user]
153
+ keypair_file = "#{CERT_DIR}/#{dest}/#{dest}_keypair.pem"
154
+ if File.exists?(keypair_file)
155
+ keypair = OpenSSL::PKey::RSA.new(File.read(keypair_file),
156
+ cert_config[:password])
157
+ return keypair_file, keypair
158
+ end
159
+ Dir.mkdir("#{CERT_DIR}/#{dest}", 0700) unless File.exists?("#{CERT_DIR}/#{dest}")
160
+
161
+ puts "Generating RSA keypair" if $DEBUG
162
+ keypair = OpenSSL::PKey::RSA.new 1024
163
+
164
+ if cert_config[:password].nil? then
165
+ File.open keypair_file, "w", 0400 do |f|
166
+ f << keypair.to_pem
167
+ end
168
+ else
169
+ keypair_export = keypair.export(OpenSSL::Cipher::DES.new(:EDE3, :CBC),
170
+ cert_config[:password])
171
+
172
+ puts "Writing keypair to #{keypair_file}" if $DEBUG
173
+ File.open keypair_file, "w", 0400 do |f|
174
+ f << keypair_export
175
+ end
176
+ end
177
+
178
+ return keypair_file, keypair
179
+ end
180
+
181
+ ##
182
+ # Creates a new Certificate Signing Request for the keypair in
183
+ # +keypair_file+, generating and saving new keypair if nil.
184
+
185
+ def create_csr(cert_config, keypair_file = nil)
186
+ keypair = nil
187
+ dest = cert_config[:hostname] || cert_config[:user]
188
+ csr_file = "#{CERT_DIR}/#{dest}/csr_#{dest}.pem"
189
+
190
+ name = @ca_config[:name].dup
191
+ case cert_config[:type]
192
+ when 'server' then
193
+ name << ['OU', 'CA']
194
+ name << ['CN', cert_config[:hostname]]
195
+ when 'client' then
196
+ name << ['CN', cert_config[:user]]
197
+ name << ['emailAddress', cert_config[:email]]
198
+ end
199
+ name = OpenSSL::X509::Name.new name
200
+
201
+ if File.exists? keypair_file then
202
+ keypair = OpenSSL::PKey::RSA.new(File.read(keypair_file),
203
+ cert_config[:password])
204
+ else
205
+ keypair = create_key cert_config
206
+ end
207
+
208
+ puts "Generating CSR for #{name}" if $DEBUG
209
+
210
+ req = OpenSSL::X509::Request.new
211
+ req.version = 0
212
+ req.subject = name
213
+ req.public_key = keypair.public_key
214
+ req.sign keypair, OpenSSL::Digest::MD5.new
215
+
216
+ puts "Writing CSR to #{csr_file}" if $DEBUG
217
+ File.open csr_file, "w" do |f|
218
+ f << req.to_pem
219
+ end
220
+
221
+ return csr_file
222
+ end
223
+
224
+ ##
225
+ # Signs the certificate described in +cert_config+ and
226
+ # +csr_file+, saving it to +cert_file+.
227
+
228
+ def sign_cert(cert_config, cert_file, csr_file)
229
+ csr = OpenSSL::X509::Request.new File.read(csr_file)
230
+
231
+ raise "CSR sign verification failed." unless csr.verify csr.public_key
232
+
233
+ if csr.public_key.n.num_bits < @ca_config[:cert_key_length_min] then
234
+ raise "Key length too short"
235
+ end
236
+
237
+ if csr.public_key.n.num_bits > @ca_config[:cert_key_length_max] then
238
+ raise "Key length too long"
239
+ end
240
+
241
+ if csr.subject.to_a[0, @ca_config[:name].size] != @ca_config[:name] then
242
+ raise "DN does not match"
243
+ end
244
+
245
+ # Only checks signature here. You must verify CSR according to your
246
+ # CP/CPS.
247
+
248
+ # CA setup
249
+
250
+ puts "Reading CA cert from #{@ca_config[:cert_file]}" if $DEBUG
251
+ ca = OpenSSL::X509::Certificate.new File.read(@ca_config[:cert_file])
252
+
253
+ puts "Reading CA keypair from #{@ca_config[:keypair_file]}" if $DEBUG
254
+ ca_keypair = OpenSSL::PKey::RSA.new File.read(@ca_config[:keypair_file]),
255
+ @ca_config[:password]
256
+
257
+ serial = File.read(@ca_config[:serial_file]).chomp.hex
258
+ File.open @ca_config[:serial_file], "w" do |f|
259
+ f << "%04X" % (serial + 1)
260
+ end
261
+
262
+ puts "Generating cert" if $DEBUG
263
+
264
+ cert = OpenSSL::X509::Certificate.new
265
+ from = Time.now
266
+ cert.subject = csr.subject
267
+ cert.issuer = ca.subject
268
+ cert.not_before = from
269
+ cert.not_after = from + @ca_config[:cert_days] * 24 * 60 * 60
270
+ cert.public_key = csr.public_key
271
+ cert.serial = serial
272
+ cert.version = 2 # X509v3
273
+
274
+ basic_constraint = nil
275
+ key_usage = []
276
+ ext_key_usage = []
277
+
278
+ case cert_config[:type]
279
+ when "ca" then
280
+ basic_constraint = "CA:TRUE"
281
+ key_usage << "cRLSign" << "keyCertSign"
282
+ when "terminalsubca" then
283
+ basic_constraint = "CA:TRUE,pathlen:0"
284
+ key_usage << "cRLSign" << "keyCertSign"
285
+ when "server" then
286
+ basic_constraint = "CA:FALSE"
287
+ key_usage << "digitalSignature" << "keyEncipherment"
288
+ ext_key_usage << "serverAuth"
289
+ when "ocsp" then
290
+ basic_constraint = "CA:FALSE"
291
+ key_usage << "nonRepudiation" << "digitalSignature"
292
+ ext_key_usage << "serverAuth" << "OCSPSigning"
293
+ when "client" then
294
+ basic_constraint = "CA:FALSE"
295
+ key_usage << "nonRepudiation" << "digitalSignature" << "keyEncipherment"
296
+ ext_key_usage << "clientAuth" << "emailProtection"
297
+ else
298
+ raise "unknonw cert type \"#{cert_config[:type]}\""
299
+ end
300
+
301
+ ef = OpenSSL::X509::ExtensionFactory.new
302
+ ef.subject_certificate = cert
303
+ ef.issuer_certificate = ca
304
+ ex = []
305
+ ex << ef.create_extension("basicConstraints", basic_constraint, true)
306
+ ex << ef.create_extension("nsComment",
307
+ "Ruby/OpenSSL Generated Certificate")
308
+ ex << ef.create_extension("subjectKeyIdentifier", "hash")
309
+ #ex << ef.create_extension("nsCertType", "client,email")
310
+ unless key_usage.empty? then
311
+ ex << ef.create_extension("keyUsage", key_usage.join(","))
312
+ end
313
+ #ex << ef.create_extension("authorityKeyIdentifier",
314
+ # "keyid:always,issuer:always")
315
+ #ex << ef.create_extension("authorityKeyIdentifier", "keyid:always")
316
+ unless ext_key_usage.empty? then
317
+ ex << ef.create_extension("extendedKeyUsage", ext_key_usage.join(","))
318
+ end
319
+
320
+ if @ca_config[:cdp_location] then
321
+ ex << ef.create_extension("crlDistributionPoints",
322
+ @ca_config[:cdp_location])
323
+ end
324
+
325
+ if @ca_config[:ocsp_location] then
326
+ ex << ef.create_extension("authorityInfoAccess",
327
+ "OCSP;" << @ca_config[:ocsp_location])
328
+ end
329
+ cert.extensions = ex
330
+ cert.sign(ca_keypair, OpenSSL::Digest::SHA1.new)
331
+
332
+ backup_cert_file = @ca_config[:new_certs_dir] + "/cert_#{cert.serial}.pem"
333
+ puts "Writing backup cert to #{backup_cert_file}" if $DEBUG
334
+ File.open backup_cert_file, "w", 0644 do |f|
335
+ f << cert.to_pem
336
+ end
337
+
338
+ # Write cert
339
+ dest = cert_config[:hostname] || cert_config[:user]
340
+ cert_file = "#{CERT_DIR}/#{dest}/cert_#{dest}.pem"
341
+ puts "Writing cert to #{cert_file}" if $DEBUG
342
+ File.open cert_file, "w", 0644 do |f|
343
+ f << cert.to_pem
344
+ end
345
+
346
+ return cert_file, cert
347
+ end
348
+
349
+ end # class QuickCert
@@ -1,3 +1,3 @@
1
1
  module EvilProxy
2
- VERSION = "0.0.2"
2
+ VERSION = "0.0.3"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: evil-proxy
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.0.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Theo
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-08-05 00:00:00.000000000 Z
11
+ date: 2015-11-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -38,10 +38,25 @@ dependencies:
38
38
  - - ">="
39
39
  - !ruby/object:Gem::Version
40
40
  version: '0'
41
- description: A ruby http proxy to do EVIL things.
41
+ - !ruby/object:Gem::Dependency
42
+ name: pry-byebug
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ description: A ruby http/https proxy, with SSL MITM support.
42
56
  email:
43
57
  - bbtfrr@gmail.com
44
- executables: []
58
+ executables:
59
+ - evil-proxy
45
60
  extensions: []
46
61
  extra_rdoc_files: []
47
62
  files:
@@ -50,11 +65,15 @@ files:
50
65
  - LICENSE.txt
51
66
  - README.md
52
67
  - Rakefile
68
+ - bin/evil-proxy
53
69
  - evil-proxy.gemspec
54
70
  - lib/evil-proxy.rb
71
+ - lib/evil-proxy/agentproxy.rb
55
72
  - lib/evil-proxy/async.rb
56
73
  - lib/evil-proxy/httpproxy.rb
57
74
  - lib/evil-proxy/httprequest.rb
75
+ - lib/evil-proxy/mitmproxy.rb
76
+ - lib/evil-proxy/quickcert.rb
58
77
  - lib/evil-proxy/selenium.rb
59
78
  - lib/evil-proxy/store.rb
60
79
  - lib/evil-proxy/version.rb
@@ -81,5 +100,5 @@ rubyforge_project:
81
100
  rubygems_version: 2.4.6
82
101
  signing_key:
83
102
  specification_version: 4
84
- summary: A ruby http proxy to do EVIL things.
103
+ summary: A ruby http/https proxy to do EVIL things.
85
104
  test_files: []