1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
|
#
# https.rb -- SSL/TLS enhancement for HTTPServer
#
# Author: IPR -- Internet Programming with Ruby -- writers
# Copyright (c) 2001 GOTOU Yuuzou
# Copyright (c) 2002 Internet Programming with Ruby writers. All rights
# reserved.
#
# $IPR: https.rb,v 1.15 2003/07/22 19:20:42 gotoyuzo Exp $
require 'webrick'
require 'openssl'
module WEBrick
module Config
HTTP.update(
:SSLEnable => true,
:SSLCertificate => nil,
:SSLPrivateKey => nil,
:SSLClientCA => nil,
:SSLCACertificateFile => nil,
:SSLCACertificatePath => nil,
:SSLCertificateStore => nil,
:SSLVerifyClient => ::OpenSSL::SSL::VERIFY_NONE,
:SSLVerifyDepth => nil,
:SSLVerifyCallback => nil, # custom verification
:SSLTimeout => nil,
:SSLOptions => nil,
# Must specify if you use auto generated certificate.
:SSLCertName => nil,
:SSLCertComment => "Generated by Ruby/OpenSSL"
)
osslv = ::OpenSSL::OPENSSL_VERSION.split[1]
HTTP[:ServerSoftware] << " OpenSSL/#{osslv}"
end
class HTTPRequest
attr_reader :cipher, :server_cert, :client_cert
alias orig_parse parse
def parse(socket=nil)
orig_parse(socket)
@cipher = socket.respond_to?(:cipher) ? socket.cipher : nil
@client_cert = socket.respond_to?(:peer_cert) ? socket.peer_cert : nil
@server_cert = @config[:SSLCertificate]
end
alias orig_parse_uri parse_uri
def parse_uri(str, scheme="https")
if @config[:SSLEnable]
return orig_parse_uri(str, scheme)
end
return orig_parse_uri(str)
end
alias orig_meta_vars meta_vars
def meta_vars
meta = orig_meta_vars
if @config[:SSLEnable]
meta["HTTPS"] = "on"
meta["SSL_CIPHER"] = @cipher ? @cipher[0] : ""
meta["SSL_CLIENT_CERT"] = @client_cert ? @client_cert.to_pem : ""
meta["SSL_SERVER_CERT"] = @server_cert ? @server_cert.to_pem : ""
end
meta
end
end
class HTTPServer
alias orig_init initialize
def initialize(*args)
orig_init(*args)
if @config[:SSLEnable]
unless @config[:SSLCertificate]
rsa = OpenSSL::PKey::RSA.new(512){|p, n|
case p
when 0; $stderr.putc "." # BN_generate_prime
when 1; $stderr.putc "+" # BN_generate_prime
when 2; $stderr.putc "*" # searching good prime,
# n = #of try,
# but also data from BN_generate_prime
when 3; $stderr.putc "\n" # found good prime, n==0 - p, n==1 - q,
# but also data from BN_generate_prime
else; $stderr.putc "*" # BN_generate_prime
end
}
cert = OpenSSL::X509::Certificate.new
cert.version = 3
cert.serial = 0
name = OpenSSL::X509::Name.new(@config[:SSLCertName])
cert.subject = name
cert.issuer = name
cert.not_before = Time.now
cert.not_after = Time.now + (365*24*60*60)
cert.public_key = rsa.public_key
ef = OpenSSL::X509::ExtensionFactory.new(nil,cert)
cert.extensions = [
ef.create_extension("basicConstraints","CA:FALSE"),
ef.create_extension("subjectKeyIdentifier", "hash"),
ef.create_extension("extendedKeyUsage", "serverAuth")
]
ef.issuer_certificate = cert
ext = ef.create_extension("authorityKeyIdentifier",
"keyid:always,issuer:always")
cert.add_extension(ext)
if comment = @config[:SSLCertComment]
cert.add_extension(ef.create_extension("nsComment", comment))
end
cert.sign(rsa, OpenSSL::Digest::SHA1.new)
@config[:SSLPrivateKey] = rsa
@config[:SSLCertificate] = cert
@logger.info cert.to_s
end
@ctx = OpenSSL::SSL::SSLContext.new
set_ssl_context(@ctx, @config)
end
end
alias orig_run run
def run(sock)
if @config[:SSLEnable]
ssl = OpenSSL::SSL::SSLSocket.new(sock, @ctx)
ssl.sync = true
ssl.accept
Thread.current[:WEBrickSocket] = ssl
orig_run(ssl)
Thread.current[:WEBrickSocket] = sock
ssl.close
else
orig_run(sock)
end
end
private
def set_ssl_context(ctx, config)
ctx.key = config[:SSLPrivateKey]
ctx.cert = config[:SSLCertificate]
ctx.client_ca = config[:SSLClientCA]
ctx.ca_file = config[:SSLCACertificateFile]
ctx.ca_path = config[:SSLCACertificatePath]
ctx.cert_store = config[:SSLCertificateStore]
ctx.verify_mode = config[:SSLVerifyClient]
ctx.verify_depth = config[:SSLVerifyDepth]
ctx.verify_callback = config[:SSLVerifyCallback]
ctx.timeout = config[:SSLTimeout]
ctx.options = config[:SSLOptions]
end
end
end
|