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,31 @@
|
|
1
|
+
<html>
|
2
|
+
<head>
|
3
|
+
<title>Revoke</title>
|
4
|
+
</head>
|
5
|
+
<body>
|
6
|
+
|
7
|
+
<h1>Revoke a certificate</h1>
|
8
|
+
|
9
|
+
<form method="post" action="/1/certificate/revoke/">
|
10
|
+
<p>
|
11
|
+
CA:
|
12
|
+
<br />
|
13
|
+
<input type="text" name="ca" />
|
14
|
+
</p>
|
15
|
+
<p>
|
16
|
+
Serial:
|
17
|
+
<br />
|
18
|
+
<input type="text" name="serial" />
|
19
|
+
</p>
|
20
|
+
<p>
|
21
|
+
Reason:
|
22
|
+
<br />
|
23
|
+
<input type="text" name="reason" value="0" />
|
24
|
+
</p>
|
25
|
+
<p>
|
26
|
+
<input type="submit" value="Revoke" />
|
27
|
+
</p>
|
28
|
+
</form>
|
29
|
+
|
30
|
+
</body>
|
31
|
+
</html>
|
@@ -0,0 +1,26 @@
|
|
1
|
+
<html>
|
2
|
+
<head>
|
3
|
+
<title>Unrevoke</title>
|
4
|
+
</head>
|
5
|
+
<body>
|
6
|
+
|
7
|
+
<h1>Unrevoke a certificate</h1>
|
8
|
+
|
9
|
+
<form method="post" action="/1/certificate/unrevoke/">
|
10
|
+
<p>
|
11
|
+
CA:
|
12
|
+
<br />
|
13
|
+
<input type="text" name="ca" />
|
14
|
+
</p>
|
15
|
+
<p>
|
16
|
+
Serial:
|
17
|
+
<br />
|
18
|
+
<input type="text" name="serial" />
|
19
|
+
</p>
|
20
|
+
<p>
|
21
|
+
<input type="submit" value="Unrevoke" />
|
22
|
+
</p>
|
23
|
+
</form>
|
24
|
+
|
25
|
+
</body>
|
26
|
+
</html>
|
@@ -0,0 +1,22 @@
|
|
1
|
+
-----BEGIN CERTIFICATE-----
|
2
|
+
MIIDjzCCAnegAwIBAgIJAP/ZxwuHN9GUMA0GCSqGSIb3DQEBBQUAMF4xCzAJBgNV
|
3
|
+
BAYTAlVTMREwDwYDVQQIDAhJbGxpbm9pczEQMA4GA1UEBwwHQ2hpY2FnbzEYMBYG
|
4
|
+
A1UECgwPUnVieSBDQSBQcm9qZWN0MRAwDgYDVQQDDAdUZXN0IENBMB4XDTExMDIy
|
5
|
+
MDIwNDkxMloXDTMwMDQyMTIwNDkxMlowXjELMAkGA1UEBhMCVVMxETAPBgNVBAgM
|
6
|
+
CElsbGlub2lzMRAwDgYDVQQHDAdDaGljYWdvMRgwFgYDVQQKDA9SdWJ5IENBIFBy
|
7
|
+
b2plY3QxEDAOBgNVBAMMB1Rlc3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw
|
8
|
+
ggEKAoIBAQCot8/z3jxARkwyRk0csM84dlWs91W95wPpunx3P3otqUxCJaOAyQl/
|
9
|
+
uY8deg0xDmsime2JQ6aG6m76y3LfT4J1tlkhtmiGKhNJir1kgL8sL0wTXYXvwZEw
|
10
|
+
qEEYD5mKi7Na9eo4R0ydqd9KAquVIhKywXvV1+Y4RDTx+f1WXmMCBaYZ76/rXmIe
|
11
|
+
oE0avriRiihOtlgto+VNJw7VRnvq+cEd81BT62wRk1fG1lcpCqTEfEtKEI6PqqZh
|
12
|
+
E2f+6lNmhZZ3Tj7NeNgYQLd4q+L1y030Vj4onpAIJrwfQq3dxTmrLDqnN2WOFsbo
|
13
|
+
0qGh3s4yqUaOYd2wIBgCp7fEf5X/yh53AgMBAAGjUDBOMB0GA1UdDgQWBBR5dbuE
|
14
|
+
Osss3noJvjEbQ7wcKk1TWDAfBgNVHSMEGDAWgBR5dbuEOsss3noJvjEbQ7wcKk1T
|
15
|
+
WDAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4IBAQAp/Fh+WWjN7x/SZzMm
|
16
|
+
H1INAS4loufHXzkqI+3bClAzlhzpBVbY7dHwD6H8oKh06EtppbN0Bw3iqXSRyoNd
|
17
|
+
+lDXccPij5dCuTFQ97DOrv7kGlhiM95XVkx4mvnd+i6jKwgOpllgDE/nKOwC42bq
|
18
|
+
lIikcp7XLQUPt7amyX9UeJ8lxQ/niSkT868ls2IZQkF3XEhCi+5VlefEoaiMQZmD
|
19
|
+
RyEd/vlsSnpXI5LLpkFq+NFGvgdI7+UADNIyDplyivD4VyQ6+zisX3r1ojroa6Y+
|
20
|
+
WyXxLx+AVJVnGjngr2fDKr4ggsYWlJEs8lJ0r90jliYrSPA6rIldTbs3k9AbUp8G
|
21
|
+
kS6X
|
22
|
+
-----END CERTIFICATE-----
|
@@ -0,0 +1,28 @@
|
|
1
|
+
-----BEGIN PRIVATE KEY-----
|
2
|
+
MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCot8/z3jxARkwy
|
3
|
+
Rk0csM84dlWs91W95wPpunx3P3otqUxCJaOAyQl/uY8deg0xDmsime2JQ6aG6m76
|
4
|
+
y3LfT4J1tlkhtmiGKhNJir1kgL8sL0wTXYXvwZEwqEEYD5mKi7Na9eo4R0ydqd9K
|
5
|
+
AquVIhKywXvV1+Y4RDTx+f1WXmMCBaYZ76/rXmIeoE0avriRiihOtlgto+VNJw7V
|
6
|
+
Rnvq+cEd81BT62wRk1fG1lcpCqTEfEtKEI6PqqZhE2f+6lNmhZZ3Tj7NeNgYQLd4
|
7
|
+
q+L1y030Vj4onpAIJrwfQq3dxTmrLDqnN2WOFsbo0qGh3s4yqUaOYd2wIBgCp7fE
|
8
|
+
f5X/yh53AgMBAAECggEAMlRS5m6fDpVp2X17N1nPFwrF2AkYPMQTOL/2rSP0cHaW
|
9
|
+
Vw0fTyWpfb5+4M4t7Tpd3z6Hy3Cw1oJMhOf35oGzayXwRMxDNfKLOl72zGpTnPym
|
10
|
+
9wfpEnJtu1QVxvWwWdH+uN2u9wbd5hJsl4lgYeZ+KXDqXgo/lP1TxfNLDV6urkVA
|
11
|
+
wsR5URtG2ddOTOUjhUvizWPt1QCVE6Z5YVe9haTtQ4EtLjPywd8LB01AXRkvLIlL
|
12
|
+
2XpL/EeuzF8GLd3GRMjEwgnT7WWBLHe7zIZqsRVx318Pu+WoWRFXUbKxrwH05tUV
|
13
|
+
kx9Gh//L01uNwGx7qHSyJxTmYibjJXQAyQqVpPY4oQKBgQDgTbRVX65AEj5x8J3A
|
14
|
+
1UEBDZE6ZQL/AnM+yPZGFSTfM04ebLUKyeIDvoSUxrbWMSf8JJwU1OCA+zJiXSld
|
15
|
+
0BNBwd/irr9aglEuOBx6ITJypaKtehR5TtZGWsrvJT9f7IZ5Tc29gC0wiKNOqQ09
|
16
|
+
O4J7/tvk2j4viAawGHSlRvJDnwKBgQDAj0X1kLwaH+QlVHU9QyRbK8qJsouot8Km
|
17
|
+
BEjkZQ4D+7BQ/3mfFszJ/AG6tMA6FgikskyEics7XrtNYPZOGZ1xxKpi1an6o8cm
|
18
|
+
GKUavZnkR5eHNwUEl2c9V5lNMbtAbCbIz30sCBFluicw37M2O8n38xTDSnXcRTbN
|
19
|
+
n3rqpu52KQKBgE3MRc8Sx7JrYYNNjLnUfZ5q4UNaw8ZFSEmvlFPMg6Ry/BZraAPc
|
20
|
+
7+qSixO7NLFoDVFUNVq4V0IFXn1liLKEOBmnsArEx5QR/SxFxALMPt4q+ximbjGB
|
21
|
+
Gar/VMHLroaL2Dx8su6WZZYe3l2rHu9tE54EUKq407bSvFcZtGObDu5LAoGAfxmS
|
22
|
+
ueYQ4sWOF73JrOg2hR9AjucVHAY/KsnFO0wglix5Ut1ub73i6qe2lIBeKXkFt4Ag
|
23
|
+
1ZMGXGfJBegsa5youcFwHdCeY9vaxaCayi2/+FfxAsUkQMWW1XyOqc9bo8g/SWj7
|
24
|
+
XCbvJNBcsfvWFMQeKdV/LPBnHz9oTw0nWt9YoxECgYAA9f2l6btaIV2JuDEzR8LK
|
25
|
+
8MjXZMwmg9pEiUT0DsiiVIkJDsHdb2i54DhkfEKxi+ZDTO8AoFpqc4GTwobQHgPk
|
26
|
+
N6Qa+wvig8SAWTS0dbPq2usvmx8cOiuqbn88VVl9jmFT6WTRJzNVRgVMM8xwl3uS
|
27
|
+
odNmWsnOyWhM8h5LMVNb5w==
|
28
|
+
-----END PRIVATE KEY-----
|
@@ -0,0 +1,18 @@
|
|
1
|
+
certificate_authorities: {
|
2
|
+
test_ca: {
|
3
|
+
ca_cert: {
|
4
|
+
cert: "test_ca.cer",
|
5
|
+
key: "test_ca.key"
|
6
|
+
},
|
7
|
+
cdp_location: 'URI:http://crl.domain.com/test_ca.crl',
|
8
|
+
message_digest: 'SHA1', #SHA1, SHA256, SHA512 supported. MD5 too, but you really shouldn't use that unless you have a good reason
|
9
|
+
profiles: {
|
10
|
+
server: {
|
11
|
+
basic_constraints: "CA:FALSE",
|
12
|
+
key_usage: [digitalSignature,keyEncipherment],
|
13
|
+
extended_key_usage: [serverAuth],
|
14
|
+
certificate_policies: [ [ "policyIdentifier=2.16.840.1.9999999999.1.2.3.4.1", "CPS.1=http://example.com/cps"] ]
|
15
|
+
}
|
16
|
+
}
|
17
|
+
}
|
18
|
+
}
|
data/spec/http_spec.rb
ADDED
@@ -0,0 +1,250 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
2
|
+
require "openssl"
|
3
|
+
|
4
|
+
describe R509::CertificateAuthority::Http::Server do
|
5
|
+
before :all do
|
6
|
+
#config_pool registry is in spec_helper because we need to register it
|
7
|
+
#BEFORE we include r509-ca-http
|
8
|
+
Dependo::Registry[:log] = Logger.new(nil)
|
9
|
+
end
|
10
|
+
|
11
|
+
before :each do
|
12
|
+
@crls = { "test_ca" => double("crl") }
|
13
|
+
@certificate_authorities = { "test_ca" => double("test_ca") }
|
14
|
+
@subject_parser = double("subject parser")
|
15
|
+
@validity_period_converter = double("validity period converter")
|
16
|
+
@csr_factory = double("csr factory")
|
17
|
+
@spki_factory = double("spki factory")
|
18
|
+
end
|
19
|
+
|
20
|
+
def app
|
21
|
+
@app ||= R509::CertificateAuthority::Http::Server
|
22
|
+
@app.send(:set, :crls, @crls)
|
23
|
+
@app.send(:set, :certificate_authorities, @certificate_authorities)
|
24
|
+
@app.send(:set, :subject_parser, @subject_parser)
|
25
|
+
@app.send(:set, :validity_period_converter, @validity_period_converter)
|
26
|
+
@app.send(:set, :csr_factory, @csr_factory)
|
27
|
+
@app.send(:set, :spki_factory, @spki_factory)
|
28
|
+
end
|
29
|
+
|
30
|
+
context "get CRL" do
|
31
|
+
it "gets the CRL" do
|
32
|
+
@crls["test_ca"].should_receive(:to_pem).and_return("generated crl")
|
33
|
+
get "/1/crl/test_ca/get"
|
34
|
+
last_response.should be_ok
|
35
|
+
last_response.content_type.should match /text\/plain/
|
36
|
+
last_response.body.should == "generated crl"
|
37
|
+
end
|
38
|
+
it "when CA is not found" do
|
39
|
+
get "/1/crl/bogus/get/"
|
40
|
+
last_response.status.should == 500
|
41
|
+
last_response.body.should == "#<ArgumentError: CA not found>"
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
context "generate CRL" do
|
46
|
+
it "generates the CRL" do
|
47
|
+
@crls["test_ca"].should_receive(:generate_crl).and_return("generated crl")
|
48
|
+
get "/1/crl/test_ca/generate"
|
49
|
+
last_response.should be_ok
|
50
|
+
last_response.body.should == "generated crl"
|
51
|
+
end
|
52
|
+
it "when CA is not found" do
|
53
|
+
get "/1/crl/bogus/generate/"
|
54
|
+
last_response.status.should == 500
|
55
|
+
last_response.body.should == "#<ArgumentError: CA not found>"
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
context "issue certificate" do
|
60
|
+
it "when no parameters are given" do
|
61
|
+
post "/1/certificate/issue"
|
62
|
+
last_response.should_not be_ok
|
63
|
+
last_response.body.should == "#<ArgumentError: Must provide a CA>"
|
64
|
+
end
|
65
|
+
it "when there's a profile, subject, CSR, validity period, but no ca" do
|
66
|
+
post "/1/certificate/issue", "profile" => "my profile", "subject" => "subject", "csr" => "my csr", "validityPeriod" => 365
|
67
|
+
last_response.should_not be_ok
|
68
|
+
last_response.body.should == "#<ArgumentError: Must provide a CA>"
|
69
|
+
end
|
70
|
+
it "when there's a ca, profile, subject, CSR, but no validity period" do
|
71
|
+
post "/1/certificate/issue", "ca" => "test_ca", "profile" => "my profile", "subject" => "subject", "csr" => "my csr"
|
72
|
+
last_response.should_not be_ok
|
73
|
+
last_response.body.should == "#<ArgumentError: Must provide a validity period>"
|
74
|
+
end
|
75
|
+
it "when there's a ca, profile, subject, validity period, but no CSR" do
|
76
|
+
post "/1/certificate/issue", "ca" => "test_ca", "profile" => "my profile", "subject" => "subject", "validityPeriod" => 365
|
77
|
+
last_response.should_not be_ok
|
78
|
+
last_response.body.should == "#<ArgumentError: Must provide a CSR or SPKI>"
|
79
|
+
end
|
80
|
+
it "when there's a ca, profile, CSR, validity period, but no subject" do
|
81
|
+
@subject_parser.should_receive(:parse).with(anything, "subject").and_return(R509::Subject.new)
|
82
|
+
post "/1/certificate/issue", "ca" => "test_ca", "profile" => "profile", "validityPeriod" => 365, "csr" => "csr"
|
83
|
+
last_response.should_not be_ok
|
84
|
+
last_response.body.should == "#<ArgumentError: Must provide a subject>"
|
85
|
+
end
|
86
|
+
it "when there's a ca, subject, CSR, validity period, but no profile" do
|
87
|
+
post "/1/certificate/issue", "ca" => "test_ca", "subject" => "subject", "validityPeriod" => 365, "csr" => "csr"
|
88
|
+
last_response.should_not be_ok
|
89
|
+
last_response.body.should == "#<ArgumentError: Must provide a CA profile>"
|
90
|
+
end
|
91
|
+
it "when the given CA is not found" do
|
92
|
+
post "/1/certificate/issue", "ca" => "some bogus CA"
|
93
|
+
last_response.should_not be_ok
|
94
|
+
last_response.body.should == "#<ArgumentError: CA not found>"
|
95
|
+
end
|
96
|
+
it "fails to issue" do
|
97
|
+
csr = double("csr")
|
98
|
+
@csr_factory.should_receive(:build).with({:csr => "csr"}).and_return(csr)
|
99
|
+
@validity_period_converter.should_receive(:convert).with("365").and_return({:not_before => 1, :not_after => 2})
|
100
|
+
subject = R509::Subject.new [["CN", "domain.com"]]
|
101
|
+
@subject_parser.should_receive(:parse).with(anything, "subject").and_return(subject)
|
102
|
+
@certificate_authorities["test_ca"].should_receive(:sign).with(:csr => csr, :profile_name => "profile", :data_hash => {:subject => subject, :san_names => []}, :not_before => 1, :not_after => 2).and_raise(R509::R509Error.new("failed to issue because of: good reason"))
|
103
|
+
|
104
|
+
post "/1/certificate/issue", "ca" => "test_ca", "profile" => "profile", "subject" => "subject", "validityPeriod" => 365, "csr" => "csr"
|
105
|
+
last_response.should_not be_ok
|
106
|
+
last_response.body.should == "#<R509::R509Error: failed to issue because of: good reason>"
|
107
|
+
end
|
108
|
+
it "issues a CSR with no SAN extensions" do
|
109
|
+
csr = double("csr")
|
110
|
+
@csr_factory.should_receive(:build).with(:csr => "csr").and_return(csr)
|
111
|
+
@validity_period_converter.should_receive(:convert).with("365").and_return({:not_before => 1, :not_after => 2})
|
112
|
+
subject = R509::Subject.new [["CN", "domain.com"]]
|
113
|
+
@subject_parser.should_receive(:parse).with(anything, "subject").and_return(subject)
|
114
|
+
cert = double("cert")
|
115
|
+
@certificate_authorities["test_ca"].should_receive(:sign).with(:csr => csr, :profile_name => "profile", :data_hash => {:subject => subject, :san_names => []}, :not_before => 1, :not_after => 2).and_return(cert)
|
116
|
+
cert.should_receive(:to_pem).and_return("signed cert")
|
117
|
+
|
118
|
+
post "/1/certificate/issue", "ca" => "test_ca", "profile" => "profile", "subject" => "subject", "validityPeriod" => 365, "csr" => "csr"
|
119
|
+
last_response.should be_ok
|
120
|
+
last_response.body.should == "signed cert"
|
121
|
+
end
|
122
|
+
it "issues a CSR with SAN extensions" do
|
123
|
+
csr = double("csr")
|
124
|
+
@csr_factory.should_receive(:build).with(:csr => "csr").and_return(csr)
|
125
|
+
@validity_period_converter.should_receive(:convert).with("365").and_return({:not_before => 1, :not_after => 2})
|
126
|
+
subject = R509::Subject.new [["CN", "domain.com"]]
|
127
|
+
@subject_parser.should_receive(:parse).with(anything, "subject").and_return(subject)
|
128
|
+
cert = double("cert")
|
129
|
+
@certificate_authorities["test_ca"].should_receive(:sign).with(:csr => csr, :profile_name => "profile", :data_hash => {:subject => subject, :san_names => ["domain1.com", "domain2.com"]}, :not_before => 1, :not_after => 2).and_return(cert)
|
130
|
+
cert.should_receive(:to_pem).and_return("signed cert")
|
131
|
+
|
132
|
+
post "/1/certificate/issue", "ca" => "test_ca", "profile" => "profile", "subject" => "subject", "validityPeriod" => 365, "csr" => "csr", "extensions[subjectAlternativeName][]" => ["domain1.com","domain2.com"]
|
133
|
+
last_response.should be_ok
|
134
|
+
last_response.body.should == "signed cert"
|
135
|
+
end
|
136
|
+
it "issues an SPKI without SAN extensions" do
|
137
|
+
@validity_period_converter.should_receive(:convert).with("365").and_return({:not_before => 1, :not_after => 2})
|
138
|
+
subject = R509::Subject.new [["CN", "domain.com"]]
|
139
|
+
@subject_parser.should_receive(:parse).with(anything, "subject").and_return(subject)
|
140
|
+
spki = double("spki")
|
141
|
+
@spki_factory.should_receive(:build).with(:spki => "spki", :subject => subject).and_return(spki)
|
142
|
+
cert = double("cert")
|
143
|
+
@certificate_authorities["test_ca"].should_receive(:sign).with(:spki => spki, :profile_name => "profile", :data_hash => {:subject => subject, :san_names => []}, :not_before => 1, :not_after => 2).and_return(cert)
|
144
|
+
cert.should_receive(:to_pem).and_return("signed cert")
|
145
|
+
|
146
|
+
post "/1/certificate/issue", "ca" => "test_ca", "profile" => "profile", "subject" => "subject", "validityPeriod" => 365, "spki" => "spki"
|
147
|
+
last_response.should be_ok
|
148
|
+
last_response.body.should == "signed cert"
|
149
|
+
end
|
150
|
+
it "issues an SPKI with SAN extensions" do
|
151
|
+
@validity_period_converter.should_receive(:convert).with("365").and_return({:not_before => 1, :not_after => 2})
|
152
|
+
subject = R509::Subject.new [["CN", "domain.com"]]
|
153
|
+
@subject_parser.should_receive(:parse).with(anything, "subject").and_return(subject)
|
154
|
+
spki = double("spki")
|
155
|
+
@spki_factory.should_receive(:build).with(:spki => "spki", :subject => subject).and_return(spki)
|
156
|
+
cert = double("cert")
|
157
|
+
@certificate_authorities["test_ca"].should_receive(:sign).with(:spki => spki, :profile_name => "profile", :data_hash => {:subject => subject, :san_names => ["domain1.com", "domain2.com"]}, :not_before => 1, :not_after => 2).and_return(cert)
|
158
|
+
cert.should_receive(:to_pem).and_return("signed cert")
|
159
|
+
|
160
|
+
post "/1/certificate/issue", "ca" => "test_ca", "profile" => "profile", "subject" => "subject", "validityPeriod" => 365, "spki" => "spki", "extensions[subjectAlternativeName][]" => ["domain1.com","domain2.com"]
|
161
|
+
last_response.should be_ok
|
162
|
+
last_response.body.should == "signed cert"
|
163
|
+
end
|
164
|
+
it "when there are empty SAN names" do
|
165
|
+
csr = double("csr")
|
166
|
+
@csr_factory.should_receive(:build).with(:csr => "csr").and_return(csr)
|
167
|
+
@validity_period_converter.should_receive(:convert).with("365").and_return({:not_before => 1, :not_after => 2})
|
168
|
+
subject = R509::Subject.new [["CN", "domain.com"]]
|
169
|
+
@subject_parser.should_receive(:parse).with(anything, "subject").and_return(subject)
|
170
|
+
cert = double("cert")
|
171
|
+
@certificate_authorities["test_ca"].should_receive(:sign).with(:csr => csr, :profile_name => "profile", :data_hash => {:subject => subject, :san_names => ["domain1.com", "domain2.com"]}, :not_before => 1, :not_after => 2).and_return(cert)
|
172
|
+
cert.should_receive(:to_pem).and_return("signed cert")
|
173
|
+
|
174
|
+
post "/1/certificate/issue", "ca" => "test_ca", "profile" => "profile", "subject" => "subject", "validityPeriod" => 365, "csr" => "csr", "extensions[subjectAlternativeName][]" => ["domain1.com","domain2.com","",""]
|
175
|
+
last_response.should be_ok
|
176
|
+
last_response.body.should == "signed cert"
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
context "revoke certificate" do
|
181
|
+
it "when no CA is given" do
|
182
|
+
post "/1/certificate/revoke", "serial" => "foo"
|
183
|
+
last_response.status.should == 500
|
184
|
+
last_response.body.should == "#<ArgumentError: CA must be provided>"
|
185
|
+
end
|
186
|
+
it "when CA is not found" do
|
187
|
+
post "/1/certificate/revoke", "ca" => "bogus ca name", "serial" => "foo"
|
188
|
+
last_response.status.should == 500
|
189
|
+
last_response.body.should == "#<ArgumentError: CA not found>"
|
190
|
+
end
|
191
|
+
it "when no serial is given" do
|
192
|
+
post "/1/certificate/revoke", "ca" => "test_ca"
|
193
|
+
last_response.should_not be_ok
|
194
|
+
last_response.body.should == "#<ArgumentError: Serial must be provided>"
|
195
|
+
end
|
196
|
+
it "when serial is given but not reason" do
|
197
|
+
@crls["test_ca"].should_receive(:revoke_cert).with(12345, 0).and_return(nil)
|
198
|
+
@crls["test_ca"].should_receive(:to_pem).and_return("generated crl")
|
199
|
+
post "/1/certificate/revoke", "ca" => "test_ca", "serial" => "12345"
|
200
|
+
last_response.should be_ok
|
201
|
+
last_response.body.should == "generated crl"
|
202
|
+
end
|
203
|
+
it "when serial and reason are given" do
|
204
|
+
@crls["test_ca"].should_receive(:revoke_cert).with(12345, 1).and_return(nil)
|
205
|
+
@crls["test_ca"].should_receive(:to_pem).and_return("generated crl")
|
206
|
+
post "/1/certificate/revoke", "ca" => "test_ca", "serial" => "12345", "reason" => "1"
|
207
|
+
last_response.should be_ok
|
208
|
+
last_response.body.should == "generated crl"
|
209
|
+
end
|
210
|
+
it "when serial is not an integer" do
|
211
|
+
@crls["test_ca"].should_receive(:revoke_cert).with(0, 0).and_raise(R509::R509Error.new("some r509 error"))
|
212
|
+
post "/1/certificate/revoke", "ca" => "test_ca", "serial" => "foo"
|
213
|
+
last_response.should_not be_ok
|
214
|
+
last_response.body.should == "#<R509::R509Error: some r509 error>"
|
215
|
+
end
|
216
|
+
it "when reason is not an integer" do
|
217
|
+
@crls["test_ca"].should_receive(:revoke_cert).with(12345, 0).and_return(nil)
|
218
|
+
@crls["test_ca"].should_receive(:to_pem).and_return("generated crl")
|
219
|
+
post "/1/certificate/revoke", "ca" => "test_ca", "serial" => "12345", "reason" => "foo"
|
220
|
+
last_response.should be_ok
|
221
|
+
last_response.body.should == "generated crl"
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
context "unrevoke certificate" do
|
226
|
+
it "when no CA is given" do
|
227
|
+
post "/1/certificate/unrevoke", "serial" => "foo"
|
228
|
+
last_response.status.should == 500
|
229
|
+
last_response.body.should == "#<ArgumentError: CA must be provided>"
|
230
|
+
end
|
231
|
+
it "when CA is not found" do
|
232
|
+
post "/1/certificate/unrevoke", "ca" => "bogus ca", "serial" => "foo"
|
233
|
+
last_response.status.should == 500
|
234
|
+
last_response.body.should == "#<ArgumentError: CA not found>"
|
235
|
+
end
|
236
|
+
it "when no serial is given" do
|
237
|
+
post "/1/certificate/unrevoke", "ca" => "test_ca"
|
238
|
+
last_response.should_not be_ok
|
239
|
+
last_response.body.should == "#<ArgumentError: Serial must be provided>"
|
240
|
+
end
|
241
|
+
it "when serial is given" do
|
242
|
+
@crls["test_ca"].should_receive(:unrevoke_cert).with(12345).and_return(nil)
|
243
|
+
@crls["test_ca"].should_receive(:to_pem).and_return("generated crl")
|
244
|
+
post "/1/certificate/unrevoke", "ca" => "test_ca", "serial" => "12345"
|
245
|
+
last_response.should be_ok
|
246
|
+
last_response.body.should == "generated crl"
|
247
|
+
end
|
248
|
+
end
|
249
|
+
|
250
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
if (RUBY_VERSION.split('.')[1].to_i > 8)
|
2
|
+
require 'simplecov'
|
3
|
+
SimpleCov.start
|
4
|
+
end
|
5
|
+
|
6
|
+
$:.unshift File.expand_path("../../lib", __FILE__)
|
7
|
+
$:.unshift File.expand_path("../", __FILE__)
|
8
|
+
require 'rubygems'
|
9
|
+
#require 'fixtures'
|
10
|
+
require 'rspec'
|
11
|
+
require 'rack/test'
|
12
|
+
require 'r509'
|
13
|
+
require 'dependo'
|
14
|
+
require 'logger'
|
15
|
+
|
16
|
+
Dependo::Registry[:config_pool] = R509::Config::CaConfigPool.from_yaml("certificate_authorities", File.read(File.dirname(__FILE__)+"/fixtures/test_config.yaml"), {:ca_root_path => "#{File.dirname(__FILE__)}/fixtures"})
|
17
|
+
|
18
|
+
require 'r509/certificateauthority/http/server'
|
19
|
+
|
20
|
+
RSpec.configure do |conf|
|
21
|
+
conf.include Rack::Test::Methods
|
22
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
2
|
+
|
3
|
+
describe R509::CertificateAuthority::Http::SubjectParser do
|
4
|
+
before :all do
|
5
|
+
@parser = R509::CertificateAuthority::Http::SubjectParser.new
|
6
|
+
end
|
7
|
+
|
8
|
+
it "when the query string is nil" do
|
9
|
+
expect { @parser.parse(nil) }.to raise_error(ArgumentError, "Must provide a query string")
|
10
|
+
end
|
11
|
+
it "when the query string is empty" do
|
12
|
+
subject = @parser.parse("")
|
13
|
+
subject.empty?.should == true
|
14
|
+
end
|
15
|
+
it "when the query string doesn't contain any subject data" do
|
16
|
+
subject = @parser.parse("validityPeriod=1095&data=blahblah")
|
17
|
+
subject.empty?.should == true
|
18
|
+
end
|
19
|
+
it "when there is one subject component" do
|
20
|
+
subject = @parser.parse("validityPeriod=1095&subject[CN]=domain.com&data=blahblah")
|
21
|
+
subject.empty?.should == false
|
22
|
+
subject["CN"].should == "domain.com"
|
23
|
+
end
|
24
|
+
it "when there are three subject components should maintain order" do
|
25
|
+
subject = @parser.parse("validityPeriod=1095&subject[CN]=domain.com&subject[O]=org&subject[L]=locality&data=blahblah")
|
26
|
+
subject.empty?.should == false
|
27
|
+
subject["CN"].should == "domain.com"
|
28
|
+
subject["O"].should == "org"
|
29
|
+
subject["L"].should == "locality"
|
30
|
+
subject.to_s.should == "/CN=domain.com/O=org/L=locality"
|
31
|
+
end
|
32
|
+
it "when one of the subject components has an unknown key" do
|
33
|
+
expect { subject = @parser.parse("validityPeriod=1095&subject[CN]=domain.com&subject[NOTATHING]=org&subject[L]=locality&data=blahblah") }.to raise_error(OpenSSL::X509::NameError)
|
34
|
+
end
|
35
|
+
it "when one of the subject components is just an OID" do
|
36
|
+
subject = @parser.parse("validityPeriod=1095&subject[CN]=domain.com&subject[1.3.6.1.4.1.311.60.2.1.300]=org&subject[L]=locality&data=blahblah")
|
37
|
+
subject.empty?.should == false
|
38
|
+
subject["CN"].should == "domain.com"
|
39
|
+
subject["1.3.6.1.4.1.311.60.2.1.300"].should == "org"
|
40
|
+
subject["L"].should == "locality"
|
41
|
+
subject.to_s.should == "/CN=domain.com/1.3.6.1.4.1.311.60.2.1.300=org/L=locality"
|
42
|
+
end
|
43
|
+
it "when one of the subject components is an empty string" do
|
44
|
+
subject = @parser.parse("validityPeriod=1095&subject[CN]=domain.com&subject[O]=&subject[L]=locality&data=blahblah")
|
45
|
+
subject.empty?.should == false
|
46
|
+
subject["CN"].should == "domain.com"
|
47
|
+
subject["O"].should == nil
|
48
|
+
subject["L"].should == "locality"
|
49
|
+
subject.to_s.should == "/CN=domain.com/L=locality"
|
50
|
+
end
|
51
|
+
end
|