r509-ca-http 0.1
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/README.md +122 -0
- data/Rakefile +38 -0
- data/doc/R509.html +117 -0
- data/doc/R509/CertificateAuthority.html +117 -0
- data/doc/R509/CertificateAuthority/Http.html +131 -0
- data/doc/R509/CertificateAuthority/Http/Factory.html +115 -0
- data/doc/R509/CertificateAuthority/Http/Factory/CsrFactory.html +189 -0
- data/doc/R509/CertificateAuthority/Http/Factory/SpkiFactory.html +189 -0
- data/doc/R509/CertificateAuthority/Http/Server.html +133 -0
- data/doc/R509/CertificateAuthority/Http/SubjectParser.html +265 -0
- data/doc/R509/CertificateAuthority/Http/ValidityPeriodConverter.html +207 -0
- data/doc/_index.html +206 -0
- data/doc/class_list.html +53 -0
- data/doc/css/common.css +1 -0
- data/doc/css/full_list.css +57 -0
- data/doc/css/style.css +328 -0
- data/doc/file.README.html +209 -0
- data/doc/file_list.html +55 -0
- data/doc/frames.html +28 -0
- data/doc/index.html +209 -0
- data/doc/js/app.js +214 -0
- data/doc/js/full_list.js +173 -0
- data/doc/js/jquery.js +4 -0
- data/doc/method_list.html +92 -0
- data/doc/top-level-namespace.html +112 -0
- data/lib/r509/certificateauthority/http/factory.rb +15 -0
- data/lib/r509/certificateauthority/http/server.rb +237 -0
- data/lib/r509/certificateauthority/http/subjectparser.rb +33 -0
- data/lib/r509/certificateauthority/http/validityperiodconverter.rb +16 -0
- data/lib/r509/certificateauthority/http/version.rb +7 -0
- data/lib/r509/certificateauthority/http/views/test_issue.erb +85 -0
- data/lib/r509/certificateauthority/http/views/test_revoke.erb +31 -0
- data/lib/r509/certificateauthority/http/views/test_unrevoke.erb +26 -0
- data/spec/fixtures/test_ca.cer +22 -0
- data/spec/fixtures/test_ca.key +28 -0
- data/spec/fixtures/test_config.yaml +18 -0
- data/spec/http_spec.rb +250 -0
- data/spec/spec_helper.rb +22 -0
- data/spec/subject_parser_spec.rb +51 -0
- data/spec/validity_period_converter_spec.rb +79 -0
- metadata +165 -0
@@ -0,0 +1,112 @@
|
|
1
|
+
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
2
|
+
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
3
|
+
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
4
|
+
<head>
|
5
|
+
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
6
|
+
<title>
|
7
|
+
Top Level Namespace
|
8
|
+
|
9
|
+
— Documentation by YARD 0.8.3
|
10
|
+
|
11
|
+
</title>
|
12
|
+
|
13
|
+
<link rel="stylesheet" href="css/style.css" type="text/css" media="screen" charset="utf-8" />
|
14
|
+
|
15
|
+
<link rel="stylesheet" href="css/common.css" type="text/css" media="screen" charset="utf-8" />
|
16
|
+
|
17
|
+
<script type="text/javascript" charset="utf-8">
|
18
|
+
hasFrames = window.top.frames.main ? true : false;
|
19
|
+
relpath = '';
|
20
|
+
framesUrl = "frames.html#!" + escape(window.location.href);
|
21
|
+
</script>
|
22
|
+
|
23
|
+
|
24
|
+
<script type="text/javascript" charset="utf-8" src="js/jquery.js"></script>
|
25
|
+
|
26
|
+
<script type="text/javascript" charset="utf-8" src="js/app.js"></script>
|
27
|
+
|
28
|
+
|
29
|
+
</head>
|
30
|
+
<body>
|
31
|
+
<div id="header">
|
32
|
+
<div id="menu">
|
33
|
+
|
34
|
+
<a href="_index.html">Index</a> »
|
35
|
+
|
36
|
+
|
37
|
+
<span class="title">Top Level Namespace</span>
|
38
|
+
|
39
|
+
|
40
|
+
<div class="noframes"><span class="title">(</span><a href="." target="_top">no frames</a><span class="title">)</span></div>
|
41
|
+
</div>
|
42
|
+
|
43
|
+
<div id="search">
|
44
|
+
|
45
|
+
<a class="full_list_link" id="class_list_link"
|
46
|
+
href="class_list.html">
|
47
|
+
Class List
|
48
|
+
</a>
|
49
|
+
|
50
|
+
<a class="full_list_link" id="method_list_link"
|
51
|
+
href="method_list.html">
|
52
|
+
Method List
|
53
|
+
</a>
|
54
|
+
|
55
|
+
<a class="full_list_link" id="file_list_link"
|
56
|
+
href="file_list.html">
|
57
|
+
File List
|
58
|
+
</a>
|
59
|
+
|
60
|
+
</div>
|
61
|
+
<div class="clear"></div>
|
62
|
+
</div>
|
63
|
+
|
64
|
+
<iframe id="search_frame"></iframe>
|
65
|
+
|
66
|
+
<div id="content"><h1>Top Level Namespace
|
67
|
+
|
68
|
+
|
69
|
+
|
70
|
+
</h1>
|
71
|
+
|
72
|
+
<dl class="box">
|
73
|
+
|
74
|
+
|
75
|
+
|
76
|
+
|
77
|
+
|
78
|
+
|
79
|
+
|
80
|
+
|
81
|
+
</dl>
|
82
|
+
<div class="clear"></div>
|
83
|
+
|
84
|
+
<h2>Defined Under Namespace</h2>
|
85
|
+
<p class="children">
|
86
|
+
|
87
|
+
|
88
|
+
<strong class="modules">Modules:</strong> <span class='object_link'><a href="R509.html" title="R509 (module)">R509</a></span>
|
89
|
+
|
90
|
+
|
91
|
+
|
92
|
+
|
93
|
+
</p>
|
94
|
+
|
95
|
+
|
96
|
+
|
97
|
+
|
98
|
+
|
99
|
+
|
100
|
+
|
101
|
+
|
102
|
+
|
103
|
+
</div>
|
104
|
+
|
105
|
+
<div id="footer">
|
106
|
+
Generated on Thu Nov 8 14:58:26 2012 by
|
107
|
+
<a href="http://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
|
108
|
+
0.8.3 (ruby-1.9.3).
|
109
|
+
</div>
|
110
|
+
|
111
|
+
</body>
|
112
|
+
</html>
|
@@ -0,0 +1,237 @@
|
|
1
|
+
require 'rubygems' if RUBY_VERSION < "1.9"
|
2
|
+
require 'sinatra/base'
|
3
|
+
require 'r509'
|
4
|
+
require "#{File.dirname(__FILE__)}/subjectparser"
|
5
|
+
require "#{File.dirname(__FILE__)}/validityperiodconverter"
|
6
|
+
require "#{File.dirname(__FILE__)}/factory"
|
7
|
+
require 'base64'
|
8
|
+
require 'yaml'
|
9
|
+
require 'logger'
|
10
|
+
require 'dependo'
|
11
|
+
|
12
|
+
module R509
|
13
|
+
module CertificateAuthority
|
14
|
+
module Http
|
15
|
+
class Server < Sinatra::Base
|
16
|
+
extend Dependo::Mixin
|
17
|
+
include Dependo::Mixin
|
18
|
+
|
19
|
+
configure do
|
20
|
+
disable :protection #disable Rack::Protection (for speed)
|
21
|
+
disable :logging
|
22
|
+
set :environment, :production
|
23
|
+
|
24
|
+
crls = {}
|
25
|
+
certificate_authorities = {}
|
26
|
+
config_pool.names.each do |name|
|
27
|
+
crls[name] = R509::Crl::Administrator.new(config_pool[name])
|
28
|
+
certificate_authorities[name] = R509::CertificateAuthority::Signer.new(config_pool[name])
|
29
|
+
end
|
30
|
+
|
31
|
+
set :crls, crls
|
32
|
+
set :certificate_authorities, certificate_authorities
|
33
|
+
set :subject_parser, R509::CertificateAuthority::Http::SubjectParser.new
|
34
|
+
set :validity_period_converter, R509::CertificateAuthority::Http::ValidityPeriodConverter.new
|
35
|
+
set :csr_factory, R509::CertificateAuthority::Http::Factory::CsrFactory.new
|
36
|
+
set :spki_factory, R509::CertificateAuthority::Http::Factory::SpkiFactory.new
|
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
|
235
|
+
end
|
236
|
+
end
|
237
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module R509
|
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
|
+
|
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
|
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
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,16 @@
|
|
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
|
+
}
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
<html>
|
2
|
+
<head>
|
3
|
+
<title>Issue</title>
|
4
|
+
</head>
|
5
|
+
<body>
|
6
|
+
|
7
|
+
<h1>Issue a certificate</h1>
|
8
|
+
|
9
|
+
<form method="post" action="/1/certificate/issue/">
|
10
|
+
<p>
|
11
|
+
CA:
|
12
|
+
<br />
|
13
|
+
<input type="text" name="ca" />
|
14
|
+
</p>
|
15
|
+
<p>
|
16
|
+
Profile:
|
17
|
+
<br />
|
18
|
+
<input type="text" name="profile" />
|
19
|
+
</p>
|
20
|
+
<p>
|
21
|
+
Validity Period (in seconds):
|
22
|
+
<br />
|
23
|
+
<input type="text" name="validityPeriod" />
|
24
|
+
</p>
|
25
|
+
<p>
|
26
|
+
C:
|
27
|
+
<br />
|
28
|
+
<input type="text" name="subject[C]" />
|
29
|
+
</p>
|
30
|
+
<p>
|
31
|
+
ST:
|
32
|
+
<br />
|
33
|
+
<input type="text" name="subject[ST]" />
|
34
|
+
</p>
|
35
|
+
<p>
|
36
|
+
L:
|
37
|
+
<br />
|
38
|
+
<input type="text" name="subject[L]" />
|
39
|
+
</p>
|
40
|
+
<p>
|
41
|
+
O:
|
42
|
+
<br />
|
43
|
+
<input type="text" name="subject[O]" />
|
44
|
+
</p>
|
45
|
+
<p>
|
46
|
+
OU:
|
47
|
+
<br />
|
48
|
+
<input type="text" name="subject[OU]" />
|
49
|
+
</p>
|
50
|
+
<p>
|
51
|
+
CN:
|
52
|
+
<br />
|
53
|
+
<input type="text" name="subject[CN]" />
|
54
|
+
</p>
|
55
|
+
<p>
|
56
|
+
emailAddress:
|
57
|
+
<br />
|
58
|
+
<input type="text" name="subject[emailAddress]" />
|
59
|
+
</p>
|
60
|
+
<p>
|
61
|
+
SAN:
|
62
|
+
<br />
|
63
|
+
<input type="text" name="extensions[subjectAlternativeName][]" />
|
64
|
+
<br />
|
65
|
+
<input type="text" name="extensions[subjectAlternativeName][]" />
|
66
|
+
<br />
|
67
|
+
<input type="text" name="extensions[subjectAlternativeName][]" />
|
68
|
+
<br />
|
69
|
+
<input type="text" name="extensions[subjectAlternativeName][]" />
|
70
|
+
<br />
|
71
|
+
<input type="text" name="extensions[subjectAlternativeName][]" />
|
72
|
+
<br />
|
73
|
+
</p>
|
74
|
+
<p>
|
75
|
+
CSR:
|
76
|
+
<br />
|
77
|
+
<textarea name="csr" rows="6" cols="80"></textarea>
|
78
|
+
</p>
|
79
|
+
<p>
|
80
|
+
<input type="submit" value="Gimme" />
|
81
|
+
</p>
|
82
|
+
</form>
|
83
|
+
|
84
|
+
</body>
|
85
|
+
</html>
|