r509-ca-http 0.1 → 0.2
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.
- data/Rakefile +18 -18
- data/doc/R509/CertificateAuthority/Http/Factory/CsrFactory.html +11 -11
- data/doc/R509/CertificateAuthority/Http/Factory/SpkiFactory.html +11 -11
- data/doc/R509/CertificateAuthority/Http/Factory.html +9 -9
- data/doc/R509/CertificateAuthority/Http/Server.html +9 -9
- data/doc/R509/CertificateAuthority/Http/SubjectParser.html +22 -22
- data/doc/R509/CertificateAuthority/Http/ValidityPeriodConverter.html +19 -19
- data/doc/R509/CertificateAuthority/Http.html +11 -11
- data/doc/R509/CertificateAuthority.html +6 -6
- data/doc/R509.html +5 -5
- data/doc/_index.html +21 -21
- data/doc/class_list.html +2 -2
- data/doc/css/style.css +10 -0
- data/doc/file.README.html +7 -7
- data/doc/file_list.html +1 -1
- data/doc/frames.html +1 -1
- data/doc/index.html +7 -7
- data/doc/js/full_list.js +6 -1
- data/doc/method_list.html +10 -20
- data/doc/top-level-namespace.html +5 -5
- data/lib/r509/certificateauthority/http/factory.rb +12 -12
- data/lib/r509/certificateauthority/http/server.rb +219 -223
- data/lib/r509/certificateauthority/http/subjectparser.rb +27 -27
- data/lib/r509/certificateauthority/http/validityperiodconverter.rb +14 -14
- data/lib/r509/certificateauthority/http/version.rb +4 -4
- data/lib/r509/certificateauthority/http/views/test_issue.erb +73 -73
- data/lib/r509/certificateauthority/http/views/test_revoke.erb +19 -19
- data/lib/r509/certificateauthority/http/views/test_unrevoke.erb +14 -14
- data/spec/fixtures/test_config.yaml +14 -15
- data/spec/http_spec.rb +235 -227
- data/spec/spec_helper.rb +1 -1
- data/spec/subject_parser_spec.rb +2 -2
- data/spec/validity_period_converter_spec.rb +2 -2
- metadata +20 -20
@@ -1,4 +1,3 @@
|
|
1
|
-
require 'rubygems' if RUBY_VERSION < "1.9"
|
2
1
|
require 'sinatra/base'
|
3
2
|
require 'r509'
|
4
3
|
require "#{File.dirname(__FILE__)}/subjectparser"
|
@@ -10,228 +9,225 @@ require 'logger'
|
|
10
9
|
require 'dependo'
|
11
10
|
|
12
11
|
module R509
|
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
|
-
end
|
38
|
-
|
39
|
-
before do
|
40
|
-
content_type :text
|
41
|
-
end
|
42
|
-
|
43
|
-
helpers do
|
44
|
-
def crl(name)
|
45
|
-
settings.crls[name]
|
46
|
-
end
|
47
|
-
def ca(name)
|
48
|
-
settings.certificate_authorities[name]
|
49
|
-
end
|
50
|
-
def subject_parser
|
51
|
-
settings.subject_parser
|
52
|
-
end
|
53
|
-
def validity_period_converter
|
54
|
-
settings.validity_period_converter
|
55
|
-
end
|
56
|
-
def csr_factory
|
57
|
-
settings.csr_factory
|
58
|
-
end
|
59
|
-
def spki_factory
|
60
|
-
settings.spki_factory
|
61
|
-
end
|
62
|
-
end
|
63
|
-
|
64
|
-
error do
|
65
|
-
log.error env["sinatra.error"].inspect
|
66
|
-
log.error env["sinatra.error"].backtrace.join("\n")
|
67
|
-
"Something is amiss with our CA. You should ... wait?"
|
68
|
-
end
|
69
|
-
|
70
|
-
error StandardError do
|
71
|
-
log.error env["sinatra.error"].inspect
|
72
|
-
log.error env["sinatra.error"].backtrace.join("\n")
|
73
|
-
env["sinatra.error"].inspect
|
74
|
-
end
|
75
|
-
|
76
|
-
get '/favicon.ico' do
|
77
|
-
log.debug "go away. no children."
|
78
|
-
"go away. no children"
|
79
|
-
end
|
80
|
-
|
81
|
-
get '/1/crl/:ca/get/?' do
|
82
|
-
log.info "Get CRL for #{params[:ca]}"
|
83
|
-
|
84
|
-
if not crl(params[:ca])
|
85
|
-
raise ArgumentError, "CA not found"
|
86
|
-
end
|
87
|
-
|
88
|
-
crl(params[:ca]).to_pem
|
89
|
-
end
|
90
|
-
|
91
|
-
get '/1/crl/:ca/generate/?' do
|
92
|
-
log.info "Generate CRL for #{params[:ca]}"
|
93
|
-
|
94
|
-
if not crl(params[:ca])
|
95
|
-
raise ArgumentError, "CA not found"
|
96
|
-
end
|
97
|
-
|
98
|
-
crl(params[:ca]).generate_crl
|
99
|
-
end
|
100
|
-
|
101
|
-
post '/1/certificate/issue/?' do
|
102
|
-
log.info "Issue Certificate"
|
103
|
-
raw = request.env["rack.input"].read
|
104
|
-
env["rack.input"].rewind
|
105
|
-
log.info raw
|
106
|
-
|
107
|
-
log.info params.inspect
|
108
|
-
|
109
|
-
if not params.has_key?("ca")
|
110
|
-
raise ArgumentError, "Must provide a CA"
|
111
|
-
end
|
112
|
-
if not ca(params["ca"])
|
113
|
-
raise ArgumentError, "CA not found"
|
114
|
-
end
|
115
|
-
if not params.has_key?("profile")
|
116
|
-
raise ArgumentError, "Must provide a CA profile"
|
117
|
-
end
|
118
|
-
if not params.has_key?("validityPeriod")
|
119
|
-
raise ArgumentError, "Must provide a validity period"
|
120
|
-
end
|
121
|
-
if not params.has_key?("csr") and not params.has_key?("spki")
|
122
|
-
raise ArgumentError, "Must provide a CSR or SPKI"
|
123
|
-
end
|
124
|
-
|
125
|
-
subject = subject_parser.parse(raw, "subject")
|
126
|
-
log.info subject.inspect
|
127
|
-
log.info subject.to_s
|
128
|
-
if subject.empty?
|
129
|
-
raise ArgumentError, "Must provide a subject"
|
130
|
-
end
|
131
|
-
|
132
|
-
if params.has_key?("extensions") and params["extensions"].has_key?("subjectAlternativeName")
|
133
|
-
san_names = params["extensions"]["subjectAlternativeName"].select { |name| not name.empty? }
|
134
|
-
else
|
135
|
-
san_names = []
|
136
|
-
end
|
137
|
-
|
138
|
-
data_hash = {
|
139
|
-
:subject => subject,
|
140
|
-
:san_names => san_names
|
141
|
-
}
|
142
|
-
|
143
|
-
validity_period = validity_period_converter.convert(params["validityPeriod"])
|
144
|
-
|
145
|
-
if params.has_key?("csr")
|
146
|
-
csr = csr_factory.build(:csr => params["csr"])
|
147
|
-
cert = ca(params["ca"]).sign(
|
148
|
-
:csr => csr,
|
149
|
-
:profile_name => params["profile"],
|
150
|
-
:data_hash => data_hash,
|
151
|
-
:not_before => validity_period[:not_before],
|
152
|
-
:not_after => validity_period[:not_after]
|
153
|
-
)
|
154
|
-
elsif params.has_key?("spki")
|
155
|
-
spki = spki_factory.build(:spki => params["spki"], :subject => subject)
|
156
|
-
cert = ca(params["ca"]).sign(
|
157
|
-
:spki => spki,
|
158
|
-
:profile_name => params["profile"],
|
159
|
-
:data_hash => data_hash,
|
160
|
-
:not_before => validity_period[:not_before],
|
161
|
-
:not_after => validity_period[:not_after]
|
162
|
-
)
|
163
|
-
else
|
164
|
-
raise ArgumentError, "Must provide a CSR or SPKI"
|
165
|
-
end
|
166
|
-
|
167
|
-
pem = cert.to_pem
|
168
|
-
log.info pem
|
169
|
-
|
170
|
-
pem
|
171
|
-
end
|
172
|
-
|
173
|
-
post '/1/certificate/revoke/?' do
|
174
|
-
ca = params[:ca]
|
175
|
-
serial = params[:serial]
|
176
|
-
reason = params[:reason]
|
177
|
-
log.info "Revoke for serial #{serial} on CA #{ca}"
|
178
|
-
|
179
|
-
if not ca
|
180
|
-
raise ArgumentError, "CA must be provided"
|
181
|
-
end
|
182
|
-
if not crl(ca)
|
183
|
-
raise ArgumentError, "CA not found"
|
184
|
-
end
|
185
|
-
if not serial
|
186
|
-
raise ArgumentError, "Serial must be provided"
|
187
|
-
end
|
188
|
-
if not reason
|
189
|
-
reason = 0
|
190
|
-
end
|
191
|
-
|
192
|
-
crl(ca).revoke_cert(serial.to_i, reason.to_i)
|
193
|
-
|
194
|
-
crl(ca).to_pem
|
195
|
-
end
|
196
|
-
|
197
|
-
post '/1/certificate/unrevoke/?' do
|
198
|
-
ca = params[:ca]
|
199
|
-
serial = params[:serial]
|
200
|
-
log.info "Unrevoke for serial #{serial} on CA #{ca}"
|
201
|
-
|
202
|
-
if not ca
|
203
|
-
raise ArgumentError, "CA must be provided"
|
204
|
-
end
|
205
|
-
if not crl(ca)
|
206
|
-
raise ArgumentError, "CA not found"
|
207
|
-
end
|
208
|
-
if not serial
|
209
|
-
raise ArgumentError, "Serial must be provided"
|
210
|
-
end
|
211
|
-
|
212
|
-
crl(ca).unrevoke_cert(serial.to_i)
|
213
|
-
|
214
|
-
crl(ca).to_pem
|
215
|
-
end
|
216
|
-
|
217
|
-
get '/test/certificate/issue/?' do
|
218
|
-
log.info "Loaded test issuance interface"
|
219
|
-
content_type :html
|
220
|
-
erb :test_issue
|
221
|
-
end
|
222
|
-
|
223
|
-
get '/test/certificate/revoke/?' do
|
224
|
-
log.info "Loaded test revoke interface"
|
225
|
-
content_type :html
|
226
|
-
erb :test_revoke
|
227
|
-
end
|
228
|
-
|
229
|
-
get '/test/certificate/unrevoke/?' do
|
230
|
-
log.info "Loaded test unrevoke interface"
|
231
|
-
content_type :html
|
232
|
-
erb :test_unrevoke
|
233
|
-
end
|
234
|
-
end
|
12
|
+
module CertificateAuthority
|
13
|
+
module HTTP
|
14
|
+
class Server < Sinatra::Base
|
15
|
+
extend Dependo::Mixin
|
16
|
+
include Dependo::Mixin
|
17
|
+
|
18
|
+
configure do
|
19
|
+
disable :protection #disable Rack::Protection (for speed)
|
20
|
+
disable :logging
|
21
|
+
set :environment, :production
|
22
|
+
|
23
|
+
crls = {}
|
24
|
+
certificate_authorities = {}
|
25
|
+
config_pool.names.each do |name|
|
26
|
+
crls[name] = R509::CRL::Administrator.new(config_pool[name])
|
27
|
+
certificate_authorities[name] = R509::CertificateAuthority::Signer.new(config_pool[name])
|
28
|
+
end
|
29
|
+
|
30
|
+
set :crls, crls
|
31
|
+
set :certificate_authorities, certificate_authorities
|
32
|
+
set :subject_parser, R509::CertificateAuthority::HTTP::SubjectParser.new
|
33
|
+
set :validity_period_converter, R509::CertificateAuthority::HTTP::ValidityPeriodConverter.new
|
34
|
+
set :csr_factory, R509::CertificateAuthority::HTTP::Factory::CSRFactory.new
|
35
|
+
set :spki_factory, R509::CertificateAuthority::HTTP::Factory::SPKIFactory.new
|
235
36
|
end
|
37
|
+
|
38
|
+
before do
|
39
|
+
content_type :text
|
40
|
+
end
|
41
|
+
|
42
|
+
helpers do
|
43
|
+
def crl(name)
|
44
|
+
settings.crls[name]
|
45
|
+
end
|
46
|
+
def ca(name)
|
47
|
+
settings.certificate_authorities[name]
|
48
|
+
end
|
49
|
+
def subject_parser
|
50
|
+
settings.subject_parser
|
51
|
+
end
|
52
|
+
def validity_period_converter
|
53
|
+
settings.validity_period_converter
|
54
|
+
end
|
55
|
+
def csr_factory
|
56
|
+
settings.csr_factory
|
57
|
+
end
|
58
|
+
def spki_factory
|
59
|
+
settings.spki_factory
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
error do
|
64
|
+
log.error env["sinatra.error"].inspect
|
65
|
+
log.error env["sinatra.error"].backtrace.join("\n")
|
66
|
+
"Something is amiss with our CA. You should ... wait?"
|
67
|
+
end
|
68
|
+
|
69
|
+
error StandardError do
|
70
|
+
log.error env["sinatra.error"].inspect
|
71
|
+
log.error env["sinatra.error"].backtrace.join("\n")
|
72
|
+
env["sinatra.error"].inspect
|
73
|
+
end
|
74
|
+
|
75
|
+
get '/favicon.ico' do
|
76
|
+
log.debug "go away. no children."
|
77
|
+
"go away. no children"
|
78
|
+
end
|
79
|
+
|
80
|
+
get '/1/crl/:ca/get/?' do
|
81
|
+
log.info "Get CRL for #{params[:ca]}"
|
82
|
+
|
83
|
+
if not crl(params[:ca])
|
84
|
+
raise ArgumentError, "CA not found"
|
85
|
+
end
|
86
|
+
|
87
|
+
crl(params[:ca]).to_pem
|
88
|
+
end
|
89
|
+
|
90
|
+
get '/1/crl/:ca/generate/?' do
|
91
|
+
log.info "Generate CRL for #{params[:ca]}"
|
92
|
+
|
93
|
+
if not crl(params[:ca])
|
94
|
+
raise ArgumentError, "CA not found"
|
95
|
+
end
|
96
|
+
|
97
|
+
crl(params[:ca]).generate_crl
|
98
|
+
end
|
99
|
+
|
100
|
+
post '/1/certificate/issue/?' do
|
101
|
+
log.info "Issue Certificate"
|
102
|
+
raw = request.env["rack.input"].read
|
103
|
+
env["rack.input"].rewind
|
104
|
+
log.info raw
|
105
|
+
|
106
|
+
log.info params.inspect
|
107
|
+
|
108
|
+
if not params.has_key?("ca")
|
109
|
+
raise ArgumentError, "Must provide a CA"
|
110
|
+
end
|
111
|
+
if not ca(params["ca"])
|
112
|
+
raise ArgumentError, "CA not found"
|
113
|
+
end
|
114
|
+
if not params.has_key?("profile")
|
115
|
+
raise ArgumentError, "Must provide a CA profile"
|
116
|
+
end
|
117
|
+
if not params.has_key?("validityPeriod")
|
118
|
+
raise ArgumentError, "Must provide a validity period"
|
119
|
+
end
|
120
|
+
if not params.has_key?("csr") and not params.has_key?("spki")
|
121
|
+
raise ArgumentError, "Must provide a CSR or SPKI"
|
122
|
+
end
|
123
|
+
|
124
|
+
subject = subject_parser.parse(raw, "subject")
|
125
|
+
log.info subject.inspect
|
126
|
+
log.info subject.to_s
|
127
|
+
if subject.empty?
|
128
|
+
raise ArgumentError, "Must provide a subject"
|
129
|
+
end
|
130
|
+
|
131
|
+
if params.has_key?("extensions") and params["extensions"].has_key?("subjectAlternativeName")
|
132
|
+
san_names = params["extensions"]["subjectAlternativeName"].select { |name| not name.empty? }
|
133
|
+
else
|
134
|
+
san_names = []
|
135
|
+
end
|
136
|
+
|
137
|
+
validity_period = validity_period_converter.convert(params["validityPeriod"])
|
138
|
+
|
139
|
+
if params.has_key?("csr")
|
140
|
+
csr = csr_factory.build(:csr => params["csr"])
|
141
|
+
cert = ca(params["ca"]).sign(
|
142
|
+
:csr => csr,
|
143
|
+
:profile_name => params["profile"],
|
144
|
+
:subject => subject,
|
145
|
+
:san_names => san_names,
|
146
|
+
:not_before => validity_period[:not_before],
|
147
|
+
:not_after => validity_period[:not_after]
|
148
|
+
)
|
149
|
+
elsif params.has_key?("spki")
|
150
|
+
spki = spki_factory.build(:spki => params["spki"], :subject => subject)
|
151
|
+
cert = ca(params["ca"]).sign(
|
152
|
+
:spki => spki,
|
153
|
+
:profile_name => params["profile"],
|
154
|
+
:subject => subject,
|
155
|
+
:san_names => san_names,
|
156
|
+
:not_before => validity_period[:not_before],
|
157
|
+
:not_after => validity_period[:not_after]
|
158
|
+
)
|
159
|
+
else
|
160
|
+
raise ArgumentError, "Must provide a CSR or SPKI"
|
161
|
+
end
|
162
|
+
|
163
|
+
pem = cert.to_pem
|
164
|
+
log.info pem
|
165
|
+
|
166
|
+
pem
|
167
|
+
end
|
168
|
+
|
169
|
+
post '/1/certificate/revoke/?' do
|
170
|
+
ca = params[:ca]
|
171
|
+
serial = params[:serial]
|
172
|
+
reason = params[:reason]
|
173
|
+
log.info "Revoke for serial #{serial} on CA #{ca}"
|
174
|
+
|
175
|
+
if not ca
|
176
|
+
raise ArgumentError, "CA must be provided"
|
177
|
+
end
|
178
|
+
if not crl(ca)
|
179
|
+
raise ArgumentError, "CA not found"
|
180
|
+
end
|
181
|
+
if not serial
|
182
|
+
raise ArgumentError, "Serial must be provided"
|
183
|
+
end
|
184
|
+
if not reason
|
185
|
+
reason = 0
|
186
|
+
end
|
187
|
+
|
188
|
+
crl(ca).revoke_cert(serial.to_i, reason.to_i)
|
189
|
+
|
190
|
+
crl(ca).crl.to_pem
|
191
|
+
end
|
192
|
+
|
193
|
+
post '/1/certificate/unrevoke/?' do
|
194
|
+
ca = params[:ca]
|
195
|
+
serial = params[:serial]
|
196
|
+
log.info "Unrevoke for serial #{serial} on CA #{ca}"
|
197
|
+
|
198
|
+
if not ca
|
199
|
+
raise ArgumentError, "CA must be provided"
|
200
|
+
end
|
201
|
+
if not crl(ca)
|
202
|
+
raise ArgumentError, "CA not found"
|
203
|
+
end
|
204
|
+
if not serial
|
205
|
+
raise ArgumentError, "Serial must be provided"
|
206
|
+
end
|
207
|
+
|
208
|
+
crl(ca).unrevoke_cert(serial.to_i)
|
209
|
+
|
210
|
+
crl(ca).crl.to_pem
|
211
|
+
end
|
212
|
+
|
213
|
+
get '/test/certificate/issue/?' do
|
214
|
+
log.info "Loaded test issuance interface"
|
215
|
+
content_type :html
|
216
|
+
erb :test_issue
|
217
|
+
end
|
218
|
+
|
219
|
+
get '/test/certificate/revoke/?' do
|
220
|
+
log.info "Loaded test revoke interface"
|
221
|
+
content_type :html
|
222
|
+
erb :test_revoke
|
223
|
+
end
|
224
|
+
|
225
|
+
get '/test/certificate/unrevoke/?' do
|
226
|
+
log.info "Loaded test unrevoke interface"
|
227
|
+
content_type :html
|
228
|
+
erb :test_unrevoke
|
229
|
+
end
|
230
|
+
end
|
236
231
|
end
|
232
|
+
end
|
237
233
|
end
|
@@ -1,33 +1,33 @@
|
|
1
1
|
module R509
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
2
|
+
module CertificateAuthority
|
3
|
+
module HTTP
|
4
|
+
class SubjectParser
|
5
|
+
def parse(raw, name="subject")
|
6
|
+
if raw.nil?
|
7
|
+
raise ArgumentError, "Must provide a query string"
|
8
|
+
end
|
9
9
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
end
|
17
|
-
}
|
18
|
-
subject
|
19
|
-
end
|
20
|
-
|
21
|
-
if defined?(::Encoding)
|
22
|
-
def unescape(s, encoding = Encoding::UTF_8)
|
23
|
-
URI.decode_www_form_component(s, encoding)
|
24
|
-
end
|
25
|
-
else
|
26
|
-
def unescape(s, encoding = nil)
|
27
|
-
URI.decode_www_form_component(s, encoding)
|
28
|
-
end
|
29
|
-
end
|
10
|
+
subject = R509::Subject.new
|
11
|
+
raw.split(/[&;] */n).each { |pair|
|
12
|
+
key, value = pair.split('=', 2).map { |data| unescape(data) }
|
13
|
+
match = key.match(/#{name}\[(.*)\]/)
|
14
|
+
if not match.nil? and not value.empty?
|
15
|
+
subject[match[1]] = value
|
30
16
|
end
|
17
|
+
}
|
18
|
+
subject
|
19
|
+
end
|
20
|
+
|
21
|
+
if defined?(::Encoding)
|
22
|
+
def unescape(s, encoding = Encoding::UTF_8)
|
23
|
+
URI.decode_www_form_component(s, encoding)
|
24
|
+
end
|
25
|
+
else
|
26
|
+
def unescape(s, encoding = nil)
|
27
|
+
URI.decode_www_form_component(s, encoding)
|
28
|
+
end
|
31
29
|
end
|
30
|
+
end
|
32
31
|
end
|
32
|
+
end
|
33
33
|
end
|
@@ -1,16 +1,16 @@
|
|
1
|
-
module R509::CertificateAuthority::
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
end
|
1
|
+
module R509::CertificateAuthority::HTTP
|
2
|
+
class ValidityPeriodConverter
|
3
|
+
def convert(validity_period)
|
4
|
+
if validity_period.nil?
|
5
|
+
raise ArgumentError, "Must provide validity period"
|
6
|
+
end
|
7
|
+
if validity_period.to_i <= 0
|
8
|
+
raise ArgumentError, "Validity period must be positive"
|
9
|
+
end
|
10
|
+
{
|
11
|
+
:not_before => Time.now - 6 * 60 * 60,
|
12
|
+
:not_after => Time.now + validity_period.to_i,
|
13
|
+
}
|
15
14
|
end
|
15
|
+
end
|
16
16
|
end
|