-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsslcon.rb
276 lines (246 loc) · 7.44 KB
/
sslcon.rb
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
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
#!/usr/bin/env ruby -rubygems
# vim: ts=2:sw=2:expandtab
require 'rubygems'
require 'bundler/setup'
require 'haml'
require 'sinatra/base'
require 'json'
require 'sqlite3'
DB_FILENAME='sslconverter.sqlite3.db'
require 'openssl'
module OpenSSL::X509
class Name
def [](field)
firstfieldmatch = self.to_a.find {|i| i[0] == field}
return firstfieldmatch[1]
end
end
class Certificate
def uniq_name
return "#{self.subject['CN']}-----#{self.serial}"
end
end
end
class SSLConverter < Sinatra::Base
configure do
enable :sessions
set :static_cache_control, :public
set :public_folder, File.dirname(__FILE__) +'/public'
set :session_secret, '47d292ec431cae6bf26cb772a56ca82859f3766f'
end
helpers do
def init_db
logger.info "INITIALIZING DB!!"
db = SQLite3::Database.new(DB_FILENAME)
db.execute("CREATE TABLE certs(id INTEGER PRIMARY KEY AUTOINCREMENT, subject TEXT, not_before INTEGER, not_after INTEGER, cert_pem TEXT, key_pem TEXT, ca_pem TEXT, owner TEXT);")
end
def get_db
init_db unless File.exist?(DB_FILENAME)
db = SQLite3::Database.new(DB_FILENAME)
return db
end
def cert_type(filename)
return :x509 if filename.end_with? ".pem"
return :pkcs12 if filename.end_with? ".pfx"
return :pkcs12 if filename.end_with? ".pkcs12"
return :pkcs12 if filename.end_with? ".der"
raise "Choose a valid certificate file extension next time..."
end
def valid_session?
return 401 unless session[:session_id]
return true # TODO: Implement
end
def user_certificates(key)
ucerts = {}
dbc = get_db
dbc.execute("select cert_pem,key_pem from certs where owner == ?", session[:session_id]) do |row|
cert_pem = row[0]
cert = OpenSSL::X509::Certificate.new(cert_pem)
key_pem = row[1]
key = OpenSSL::PKey::RSA.new(key_pem)
ucerts[cert.uniq_name] = [cert,key]
end
dbc.close unless dbc.closed?
return ucerts
end
def get_certdata(cert_id)
return 401 unless valid_session?
dbc = get_db
dbc.execute("select cert_pem,key_pem from certs where owner == ?", session[:session_id]) do |row|
ucert = OpenSSL::X509::Certificate.new(row[0])
ukey = OpenSSL::PKey::RSA.new(row[1])
return [ucert,ukey] if(ucert.uniq_name == cert_id)
end
dbc.close unless dbc.closed?
return false
end
def labelled_input(nameid,label,placeholder="")
outp = haml "%label{:for=>'#{nameid}'}=\"#{label}: \""
outp += haml "%input{:name=>'#{nameid}',:id=>'#{nameid}',:type=>:text,:placeholder=>'#{placeholder}'}"
return outp
end
end
error do
status 500
@message = env['sinatra.error']
haml :error
end
before do
cache_control :private
@title = 'SSL Format Converter'
logger.info session
end
get /\/(index)?$/ do
haml :index, :format => :html5
end
get '/request' do
haml :request
end
post '/request' do
mandatory = %w(C ST L O CN)
optional = %w(OU)
newkey = OpenSSL::PKey::RSA.new(1024)
newreq = OpenSSL::X509::Request.new
newname = ""
(mandatory+optional).map do |f|
raise "Check value of #{f}!" unless params[f] != "" and mandatory.include? f
end
end
post '/upload' do
return 401 unless valid_session?
dbc = get_db
certfn = params[:certfile][:filename]
tempfile = Tempfile.new(certfn)
type = cert_type(certfn)
certok = false
begin
tempfile.write(params['certfile'][:tempfile].read)
tempcert = tempfile.path
case type
when :x509
tempfile.rewind
xcert = OpenSSL::X509::Certificate.new(tempfile.read) # TODO: Work out if the PEM is encoded (password)
tempfile.rewind
xkey = OpenSSL::PKey::RSA.new(tempfile.read)
dbc.execute("insert into certs (subject,not_before,not_after,cert_pem,key_pem,owner) values (?,?,?,?,?,?)",
xcert.subject,
xcert.not_before.to_i,
xcert.not_after.to_i,
xcert.to_pem,
xkey.to_pem,
session[:session_id])
certok = true
when :pkcs12
raise ArgumentError unless params[:certpass]
tempfile.rewind
xpkcs = OpenSSL::PKCS12.new(tempfile.read,params[:certpass])
xcert = xpkcs.certificate
xkey = xpkcs.key
dbc.execute("insert into certs (subject,not_before,not_after,cert_pem,key_pem,owner) values (?,?,?,?,?,?)",
xcert.subject,
xcert.not_before.to_i,
xcert.not_after.to_i,
xcert.to_pem,
xkey.to_pem,
session[:session_id])
certok = true
else
certok = false
# TODO: error?
end
ensure
dbc.close unless dbc.closed?
tempfile.close
end
redirect "/process" if certok
raise RuntimeError
end
get '/certinfo.:format/:certid' do
certka = get_certdata(params[:certid])
cert = certka[0]
key = certka[1]
@sdata = {
:subject => cert.subject,
:not_before => cert.not_before,
:not_after => cert.not_after,
:serial => cert.serial,
:issuer => cert.issuer
}
case params[:format]
when 'json'
set :content_type, "application/x-json"
return sdata.to_json
when 'html'
haml :ajax_certinfo, :layout => false
end
end
get '/pem/:certid' do
set :content_type, "text/plain"
certka = get_certdata(params[:certid])
@pem = certka[0].to_pem+certka[1].to_pem
haml :ajax_pem, :layout => false
end
get '/download.:format/:certid' do
case params[:format]
when 'pem'
certka = get_certdata(params[:certid])
cert = certka[0]
key = certka[1]
attachment "#{params[:certid]}.pem"
cert.to_pem + key.to_pem
when 'pfx'
certka = get_certdata(params[:certid])
cert = certka[0]
key = certka[1]
pfx = OpenSSL::PKCS12.create('Password99',
"Converted certificate #{params[:certid]}",
key,
cert) #TODO: Double-check the formatting for this...
attachment "#{params[:certid]}.der"
pfx.to_der
end
end
delete '/certificate/:certid' do
dbc = get_db
return 401 unless valid_session?
targetid = -1
dbc = get_db
rows = dbc.execute("select cert_pem,id from certs where owner == ?", session[:session_id])
p rows
rows.each do |row|
ucert = OpenSSL::X509::Certificate.new(row[0])
logger.info "UCERT ID is #{ucert.uniq_name}"
if(ucert.uniq_name == params[:certid])
targetid = row[1]
logger.info "FOUND CERTID AS #{targetid}"
break
end
end
logger.info "TARGETID is #{targetid}"
if targetid.to_i >= 0
res = dbc.execute("delete from certs where owner == ? and id == ?", session[:session_id],targetid)
dbc.close unless dbc.closed?
return "OK" if res.length > 0
status 404
"Certificate Not Deleted"
end
dbc.close unless dbc.closed?
status 404
"Certificate Not in DB"
end
get '/upload' do
status 405
"You must POST a certificate to this URL!"
end
get '/process' do
@key = session[:session_id]
@certs = user_certificates(@key)
redirect "/" unless valid_session?
haml :process
end
get '/logout' do
session.clear
redirect "index"
end
run! if app_file == $0
end