r509 0.8.1 → 0.9
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 +343 -151
- data/Rakefile +26 -23
- data/bin/r509 +126 -112
- data/bin/r509-parse +24 -24
- data/doc/R509.html +169 -7
- data/doc/R509/ASN1.html +370 -0
- data/doc/R509/ASN1/GeneralName.html +1121 -0
- data/doc/R509/ASN1/GeneralNames.html +843 -0
- data/doc/R509/ASN1/NoticeReference.html +392 -0
- data/doc/R509/ASN1/PolicyInformation.html +387 -0
- data/doc/R509/ASN1/PolicyQualifiers.html +455 -0
- data/doc/R509/ASN1/UserNotice.html +386 -0
- data/doc/R509/{Crl.html → CRL.html} +7 -7
- data/doc/R509/CRL/Administrator.html +1559 -0
- data/doc/R509/{Crl/Parser.html → CRL/SignedList.html} +501 -210
- data/doc/R509/{Csr.html → CSR.html} +444 -314
- data/doc/R509/Cert.html +866 -617
- data/doc/R509/Cert/Extensions.html +52 -41
- data/doc/R509/Cert/Extensions/AuthorityInfoAccess.html +70 -35
- data/doc/R509/Cert/Extensions/AuthorityKeyIdentifier.html +387 -4
- data/doc/R509/Cert/Extensions/BasicConstraints.html +61 -25
- data/doc/R509/Cert/Extensions/CRLDistributionPoints.html +354 -0
- data/doc/R509/Cert/Extensions/CertificatePolicies.html +340 -0
- data/doc/R509/Cert/Extensions/ExtendedKeyUsage.html +440 -49
- data/doc/R509/Cert/Extensions/{CrlDistributionPoints.html → InhibitAnyPolicy.html} +52 -35
- data/doc/R509/Cert/Extensions/KeyUsage.html +247 -121
- data/doc/R509/Cert/Extensions/NameConstraints.html +445 -0
- data/doc/R509/Cert/Extensions/OCSPNoCheck.html +239 -0
- data/doc/R509/Cert/Extensions/PolicyConstraints.html +424 -0
- data/doc/R509/Cert/Extensions/SubjectAlternativeName.html +437 -62
- data/doc/R509/Cert/Extensions/SubjectKeyIdentifier.html +52 -10
- data/doc/R509/CertificateAuthority.html +4 -4
- data/doc/R509/CertificateAuthority/Signer.html +154 -187
- data/doc/R509/Config.html +6 -6
- data/doc/R509/Config/{CaConfig.html → CAConfig.html} +451 -348
- data/doc/R509/Config/{CaConfigPool.html → CAConfigPool.html} +47 -47
- data/doc/R509/Config/CAProfile.html +1015 -0
- data/doc/R509/Config/SubjectItemPolicy.html +86 -86
- data/doc/R509/IOHelpers.html +22 -22
- data/doc/R509/MessageDigest.html +14 -14
- data/doc/R509/NameSanitizer.html +53 -53
- data/doc/R509/{Ocsp.html → OCSP.html} +9 -9
- data/doc/R509/{Ocsp → OCSP}/Request.html +7 -7
- data/doc/R509/{Ocsp → OCSP}/Request/Nonce.html +56 -11
- data/doc/R509/{Ocsp → OCSP}/Response.html +44 -44
- data/doc/R509/{OidMapper.html → OIDMapper.html} +23 -39
- data/doc/R509/PrivateKey.html +415 -168
- data/doc/R509/R509Error.html +3 -3
- data/doc/R509/{Spki.html → SPKI.html} +354 -192
- data/doc/R509/Subject.html +224 -113
- data/doc/R509/Validity.html +27 -5
- data/doc/R509/Validity/Checker.html +13 -13
- data/doc/R509/Validity/DefaultChecker.html +13 -13
- data/doc/R509/Validity/DefaultWriter.html +14 -14
- data/doc/R509/Validity/Status.html +39 -39
- data/doc/R509/Validity/Writer.html +18 -18
- data/doc/_index.html +138 -35
- data/doc/class_list.html +1 -1
- data/doc/css/style.css +10 -0
- data/doc/file.README.html +368 -171
- data/doc/file.r509.html +92 -69
- data/doc/frames.html +1 -1
- data/doc/index.html +368 -171
- data/doc/method_list.html +910 -390
- data/doc/top-level-namespace.html +3 -3
- data/lib/r509.rb +32 -16
- data/lib/r509/asn1.rb +375 -0
- data/lib/r509/cert.rb +381 -364
- data/lib/r509/cert/extensions.rb +443 -76
- data/lib/r509/certificate_authority.rb +407 -0
- data/lib/r509/config.rb +547 -351
- data/lib/r509/crl.rb +336 -366
- data/lib/r509/csr.rb +278 -289
- data/lib/r509/ec-hack.rb +37 -0
- data/lib/r509/exceptions.rb +3 -3
- data/lib/r509/io_helpers.rb +44 -44
- data/lib/r509/message_digest.rb +53 -0
- data/lib/r509/ocsp.rb +80 -70
- data/lib/r509/oid_mapper.rb +32 -0
- data/lib/r509/private_key.rb +228 -0
- data/lib/r509/spki.rb +145 -93
- data/lib/r509/subject.rb +203 -110
- data/lib/r509/validity.rb +70 -68
- data/lib/r509/version.rb +2 -2
- data/r509.yaml +92 -69
- data/spec/asn1_spec.rb +402 -0
- data/spec/cert/extensions_spec.rb +957 -494
- data/spec/cert_spec.rb +382 -307
- data/spec/certificate_authority_spec.rb +668 -250
- data/spec/config_spec.rb +515 -302
- data/spec/crl_spec.rb +197 -198
- data/spec/csr_spec.rb +334 -289
- data/spec/fixtures.rb +247 -171
- data/spec/fixtures/cert1.der +0 -0
- data/spec/fixtures/cert1.pem +0 -0
- data/spec/fixtures/cert1_public_key_modulus.txt +0 -0
- data/spec/fixtures/cert3.p12 +0 -0
- data/spec/fixtures/cert3.pem +0 -0
- data/spec/fixtures/cert3_key.pem +0 -0
- data/spec/fixtures/cert3_key_des3.pem +0 -0
- data/spec/fixtures/cert4.pem +0 -0
- data/spec/fixtures/cert5.pem +0 -0
- data/spec/fixtures/cert6.pem +0 -0
- data/spec/fixtures/cert_expired.pem +0 -0
- data/spec/fixtures/cert_inhibit.pem +24 -0
- data/spec/fixtures/cert_name_constraints.pem +29 -0
- data/spec/fixtures/cert_not_yet_valid.pem +0 -0
- data/spec/fixtures/cert_ocsp_no_check.pem +18 -0
- data/spec/fixtures/cert_policy_constraints.pem +31 -0
- data/spec/fixtures/cert_san.pem +0 -0
- data/spec/fixtures/cert_san2.pem +0 -0
- data/spec/fixtures/cert_unknown_extension.pem +28 -0
- data/spec/fixtures/config_pool_test_minimal.yaml +11 -11
- data/spec/fixtures/config_test.yaml +54 -36
- data/spec/fixtures/config_test_dsa.yaml +35 -0
- data/spec/fixtures/config_test_ec.yaml +35 -0
- data/spec/fixtures/config_test_engine_key.yaml +5 -5
- data/spec/fixtures/config_test_engine_no_key_name.yaml +4 -4
- data/spec/fixtures/config_test_minimal.yaml +4 -4
- data/spec/fixtures/config_test_password.yaml +5 -5
- data/spec/fixtures/config_test_various.yaml +111 -74
- data/spec/fixtures/crl_list_file.txt +0 -0
- data/spec/fixtures/crl_with_reason.pem +0 -0
- data/spec/fixtures/csr1.der +0 -0
- data/spec/fixtures/csr1.pem +0 -0
- data/spec/fixtures/csr1_key.der +0 -0
- data/spec/fixtures/csr1_key.pem +0 -0
- data/spec/fixtures/csr1_key_encrypted_des3.pem +0 -0
- data/spec/fixtures/csr1_newlines.pem +0 -0
- data/spec/fixtures/csr1_no_begin_end.pem +0 -0
- data/spec/fixtures/csr1_public_key_modulus.txt +0 -0
- data/spec/fixtures/csr2.pem +0 -0
- data/spec/fixtures/csr2_key.pem +0 -0
- data/spec/fixtures/csr3.pem +0 -0
- data/spec/fixtures/csr4.pem +0 -0
- data/spec/fixtures/csr_dsa.pem +0 -0
- data/spec/fixtures/csr_invalid_signature.pem +0 -0
- data/spec/fixtures/dsa_key.pem +0 -0
- data/spec/fixtures/dsa_root.cer +28 -0
- data/spec/fixtures/dsa_root.key +20 -0
- data/spec/fixtures/ec_csr2.der +0 -0
- data/spec/fixtures/ec_csr2.pem +8 -0
- data/spec/fixtures/ec_key1.der +0 -0
- data/spec/fixtures/ec_key1.pem +6 -0
- data/spec/fixtures/ec_key1_encrypted.pem +9 -0
- data/spec/fixtures/ec_key2.pem +6 -0
- data/spec/fixtures/hmacsha1.sig +1 -0
- data/spec/fixtures/hmacsha512.sig +1 -0
- data/spec/fixtures/key4.pem +0 -0
- data/spec/fixtures/key4_encrypted_des3.pem +0 -0
- data/spec/fixtures/missing_key_identifier_ca.cer +0 -0
- data/spec/fixtures/missing_key_identifier_ca.key +0 -0
- data/spec/fixtures/ocsptest.r509.local.pem +0 -0
- data/spec/fixtures/ocsptest.r509.local_ocsp_request.der +0 -0
- data/spec/fixtures/ocsptest2.r509.local.pem +0 -0
- data/spec/fixtures/second_ca.cer +0 -0
- data/spec/fixtures/second_ca.key +0 -0
- data/spec/fixtures/spkac.der +0 -0
- data/spec/fixtures/spkac.txt +0 -0
- data/spec/fixtures/spkac_dsa.txt +1 -1
- data/spec/fixtures/spkac_dsa_no_verify.txt +1 -0
- data/spec/fixtures/spkac_ec.txt +1 -0
- data/spec/fixtures/spkac_rsa_newlines.txt +13 -0
- data/spec/fixtures/stca.pem +0 -0
- data/spec/fixtures/stca_ocsp_request.der +0 -0
- data/spec/fixtures/stca_ocsp_response.der +0 -0
- data/spec/fixtures/test1.csr +0 -0
- data/spec/fixtures/test_ca.cer +0 -0
- data/spec/fixtures/test_ca.key +0 -0
- data/spec/fixtures/test_ca.p12 +0 -0
- data/spec/fixtures/test_ca_des3.key +0 -0
- data/spec/fixtures/test_ca_ec.cer +14 -0
- data/spec/fixtures/test_ca_ec.key +6 -0
- data/spec/fixtures/test_ca_ec_ee.cer +22 -0
- data/spec/fixtures/test_ca_ec_ee.key +6 -0
- data/spec/fixtures/test_ca_ocsp.cer +0 -0
- data/spec/fixtures/test_ca_ocsp.key +0 -0
- data/spec/fixtures/test_ca_ocsp.p12 +0 -0
- data/spec/fixtures/test_ca_ocsp_chain.txt +0 -0
- data/spec/fixtures/test_ca_ocsp_response.der +0 -0
- data/spec/fixtures/test_ca_subroot.cer +0 -0
- data/spec/fixtures/test_ca_subroot.key +0 -0
- data/spec/fixtures/test_ca_subroot_ocsp.cer +0 -0
- data/spec/fixtures/test_ca_subroot_ocsp.key +0 -0
- data/spec/fixtures/test_ca_subroot_ocsp_response.der +0 -0
- data/spec/fixtures/unknown_oid.csr +0 -0
- data/spec/message_digest_spec.rb +104 -84
- data/spec/ocsp_spec.rb +105 -105
- data/spec/oid_mapper_spec.rb +21 -21
- data/spec/private_key_spec.rb +275 -0
- data/spec/r509_spec.rb +35 -0
- data/spec/spec_helper.rb +15 -6
- data/spec/spki_spec.rb +221 -142
- data/spec/subject_spec.rb +232 -164
- data/spec/validity_spec.rb +91 -91
- metadata +79 -25
- data/doc/R509/Config/CaProfile.html +0 -651
- data/doc/R509/Crl/Administrator.html +0 -2073
- data/lib/r509/certificateauthority.rb +0 -290
- data/lib/r509/messagedigest.rb +0 -49
- data/lib/r509/oidmapper.rb +0 -32
- data/lib/r509/privatekey.rb +0 -185
- data/spec/privatekey_spec.rb +0 -198
data/lib/r509/crl.rb
CHANGED
@@ -4,376 +4,346 @@ require 'r509/exceptions'
|
|
4
4
|
require 'r509/io_helpers'
|
5
5
|
|
6
6
|
module R509
|
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
|
-
@crl.revoked.each do |revoked|
|
80
|
-
reason = get_reason(revoked)
|
81
|
-
revoked_list[revoked.serial.to_i] = { :time => revoked.time, :reason => reason }
|
82
|
-
end
|
83
|
-
|
84
|
-
revoked_list
|
85
|
-
end
|
86
|
-
|
87
|
-
# @param [Integer] serial number
|
88
|
-
# @return [Hash] hash with :time and :reason
|
89
|
-
def revoked_cert(serial)
|
90
|
-
revoked = @crl.revoked.find { |revoked| revoked.serial == serial }
|
91
|
-
if revoked
|
92
|
-
reason = get_reason(revoked)
|
93
|
-
{ :time => revoked.time, :reason => reason }
|
94
|
-
else
|
95
|
-
nil
|
96
|
-
end
|
97
|
-
end
|
98
|
-
|
99
|
-
private
|
100
|
-
def get_reason(revocation_object)
|
101
|
-
reason = nil
|
102
|
-
revocation_object.extensions.each do |extension|
|
103
|
-
if extension.oid == "CRLReason"
|
104
|
-
reason = extension.value
|
105
|
-
end
|
106
|
-
end
|
107
|
-
|
108
|
-
reason
|
109
|
-
end
|
7
|
+
# contains CRL related classes (generator and a pre-existing list loader)
|
8
|
+
module CRL
|
9
|
+
# Parses CRLs
|
10
|
+
class SignedList
|
11
|
+
include R509::IOHelpers
|
12
|
+
|
13
|
+
attr_reader :crl, :issuer
|
14
|
+
|
15
|
+
# @param [String,OpenSSL::X509::CRL] crl
|
16
|
+
def initialize(crl)
|
17
|
+
@crl = OpenSSL::X509::CRL.new(crl)
|
18
|
+
@issuer = R509::Subject.new(@crl.issuer)
|
19
|
+
end
|
20
|
+
|
21
|
+
# Helper method to quickly load a CRL from the filesystem
|
22
|
+
#
|
23
|
+
# @param [String] filename Path to file you want to load
|
24
|
+
# @return [R509::CRL::SignedList] CRL object
|
25
|
+
def self.load_from_file( filename )
|
26
|
+
return R509::CRL::SignedList.new( IOHelpers.read_data(filename) )
|
27
|
+
end
|
28
|
+
|
29
|
+
# @return [String]
|
30
|
+
def signature_algorithm
|
31
|
+
@crl.signature_algorithm
|
32
|
+
end
|
33
|
+
|
34
|
+
# Writes the CRL into the PEM format
|
35
|
+
#
|
36
|
+
# @param [String, #write] filename_or_io Either a string of the path for
|
37
|
+
# the file that you'd like to write, or an IO-like object.
|
38
|
+
def write_pem(filename_or_io)
|
39
|
+
write_data(filename_or_io, @crl.to_pem)
|
40
|
+
end
|
41
|
+
|
42
|
+
# Writes the CRL into the PEM format
|
43
|
+
#
|
44
|
+
# @param [String, #write] filename_or_io Either a string of the path for
|
45
|
+
# the file that you'd like to write, or an IO-like object.
|
46
|
+
def write_der(filename_or_io)
|
47
|
+
write_data(filename_or_io, @crl.to_der)
|
48
|
+
end
|
49
|
+
|
50
|
+
# Returns the signing time of the CRL
|
51
|
+
#
|
52
|
+
# @return [Time] when the CRL was signed
|
53
|
+
def last_update
|
54
|
+
@crl.last_update
|
55
|
+
end
|
56
|
+
|
57
|
+
# Returns the next update time for the CRL
|
58
|
+
#
|
59
|
+
# @return [Time] when it will be updated next
|
60
|
+
def next_update
|
61
|
+
@crl.next_update
|
62
|
+
end
|
63
|
+
|
64
|
+
# Pass a public key to verify that the CRL is signed by a specific certificate (call cert.public_key on that object)
|
65
|
+
#
|
66
|
+
# @param [OpenSSL::PKey::PKey] public_key
|
67
|
+
# @return [Boolean]
|
68
|
+
def verify(public_key)
|
69
|
+
@crl.verify(public_key)
|
70
|
+
end
|
71
|
+
|
72
|
+
# @param [Integer] serial number
|
73
|
+
# @return [Boolean]
|
74
|
+
def revoked?(serial)
|
75
|
+
if @crl.revoked.find { |revoked| revoked.serial == serial }
|
76
|
+
true
|
77
|
+
else
|
78
|
+
false
|
110
79
|
end
|
80
|
+
end
|
81
|
+
|
82
|
+
# Returns the CRL in PEM format
|
83
|
+
#
|
84
|
+
# @return [String] the CRL in PEM format
|
85
|
+
def to_pem
|
86
|
+
@crl.to_pem
|
87
|
+
end
|
88
|
+
|
89
|
+
alias :to_s :to_pem
|
90
|
+
|
91
|
+
# Returns the CRL in DER format
|
92
|
+
#
|
93
|
+
# @return [String] the CRL in DER format
|
94
|
+
def to_der
|
95
|
+
@crl.to_der
|
96
|
+
end
|
97
|
+
|
98
|
+
# @return [Hash] hash of serial => { :time, :reason } hashes
|
99
|
+
def revoked
|
100
|
+
revoked_list = {}
|
101
|
+
@crl.revoked.each do |revoked|
|
102
|
+
reason = get_reason(revoked)
|
103
|
+
revoked_list[revoked.serial.to_i] = { :time => revoked.time, :reason => reason }
|
104
|
+
end
|
105
|
+
|
106
|
+
revoked_list
|
107
|
+
end
|
108
|
+
|
109
|
+
# @param [Integer] serial number
|
110
|
+
# @return [Hash] hash with :time and :reason
|
111
|
+
def revoked_cert(serial)
|
112
|
+
revoked = @crl.revoked.find { |revoked| revoked.serial == serial }
|
113
|
+
if revoked
|
114
|
+
reason = get_reason(revoked)
|
115
|
+
{ :time => revoked.time, :reason => reason }
|
116
|
+
else
|
117
|
+
nil
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
private
|
122
|
+
def get_reason(revocation_object)
|
123
|
+
reason = nil
|
124
|
+
revocation_object.extensions.each do |extension|
|
125
|
+
if extension.oid == "CRLReason"
|
126
|
+
reason = extension.value
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
reason
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
# Used to manage revocations and generate CRLs
|
135
|
+
class Administrator
|
136
|
+
include R509::IOHelpers
|
111
137
|
|
112
|
-
|
113
|
-
class Administrator
|
114
|
-
include R509::IOHelpers
|
115
|
-
|
116
|
-
attr_reader :crl_number,:crl_list_file,:crl_number_file, :validity_hours
|
117
|
-
|
118
|
-
# @param [R509::Config::CaConfig] config
|
119
|
-
def initialize(config)
|
120
|
-
@config = config
|
121
|
-
|
122
|
-
unless @config.kind_of?(R509::Config::CaConfig)
|
123
|
-
raise R509Error, "config must be a kind of R509::Config::CaConfig"
|
124
|
-
end
|
125
|
-
|
126
|
-
@validity_hours = @config.crl_validity_hours
|
127
|
-
@start_skew_seconds = @config.crl_start_skew_seconds
|
128
|
-
@crl = nil
|
129
|
-
|
130
|
-
@crl_number_file = @config.crl_number_file
|
131
|
-
if not @crl_number_file.nil?
|
132
|
-
@crl_number = read_data(@crl_number_file).to_i
|
133
|
-
else
|
134
|
-
@crl_number = 0
|
135
|
-
end
|
136
|
-
|
137
|
-
|
138
|
-
@crl_list_file = @config.crl_list_file
|
139
|
-
load_crl_list(@crl_list_file)
|
140
|
-
end
|
141
|
-
|
142
|
-
# Indicates whether the serial number has been revoked, or not.
|
143
|
-
#
|
144
|
-
# @param [Integer] serial The serial number we want to check
|
145
|
-
# @return [Boolean] True if the serial number was revoked. False, otherwise.
|
146
|
-
def revoked?(serial)
|
147
|
-
@revoked_certs.has_key?(serial)
|
148
|
-
end
|
149
|
-
|
150
|
-
# @return [Array] serial, reason, revoke_time tuple
|
151
|
-
def revoked_cert(serial)
|
152
|
-
@revoked_certs[serial]
|
153
|
-
end
|
154
|
-
|
155
|
-
# Returns the CRL in PEM format
|
156
|
-
#
|
157
|
-
# @return [String] the CRL in PEM format
|
158
|
-
def to_pem
|
159
|
-
@crl.to_pem
|
160
|
-
end
|
161
|
-
|
162
|
-
alias :to_s :to_pem
|
163
|
-
|
164
|
-
# Returns the CRL in DER format
|
165
|
-
#
|
166
|
-
# @return [String] the CRL in DER format
|
167
|
-
def to_der
|
168
|
-
@crl.to_der
|
169
|
-
end
|
170
|
-
|
171
|
-
# @return [R509::Crl::Parser]
|
172
|
-
def to_crl
|
173
|
-
return nil if @crl.nil?
|
174
|
-
return R509::Crl::Parser.new(@crl)
|
175
|
-
end
|
176
|
-
|
177
|
-
# Writes the CRL into the PEM format
|
178
|
-
#
|
179
|
-
# @param [String, #write] filename_or_io Either a string of the path for
|
180
|
-
# the file that you'd like to write, or an IO-like object.
|
181
|
-
def write_pem(filename_or_io)
|
182
|
-
write_data(filename_or_io, @crl.to_pem)
|
183
|
-
end
|
184
|
-
|
185
|
-
# Writes the CRL into the PEM format
|
186
|
-
#
|
187
|
-
# @param [String, #write] filename_or_io Either a string of the path for
|
188
|
-
# the file that you'd like to write, or an IO-like object.
|
189
|
-
def write_der(filename_or_io)
|
190
|
-
write_data(filename_or_io, @crl.to_der)
|
191
|
-
end
|
192
|
-
|
193
|
-
# Returns the signing time of the CRL
|
194
|
-
#
|
195
|
-
# @return [Time] when the CRL was signed
|
196
|
-
def last_update
|
197
|
-
@crl.last_update
|
198
|
-
end
|
199
|
-
|
200
|
-
# Returns the next update time for the CRL
|
201
|
-
#
|
202
|
-
# @return [Time] when it will be updated next
|
203
|
-
def next_update
|
204
|
-
@crl.next_update
|
205
|
-
end
|
206
|
-
|
207
|
-
# Adds a certificate to the revocation list. After calling you must call generate_crl to sign a new CRL
|
208
|
-
#
|
209
|
-
# @param serial [Integer] serial number of the certificate to revoke
|
210
|
-
# @param reason [Integer] reason for revocation
|
211
|
-
# @param revoke_time [Integer]
|
212
|
-
# @param generate_and_save [Boolean] whether we want to generate the CRL and save its file (default=true)
|
213
|
-
#
|
214
|
-
# reason codes defined by rfc 5280
|
215
|
-
#
|
216
|
-
# CRLReason ::= ENUMERATED {
|
217
|
-
# unspecified (0),
|
218
|
-
# keyCompromise (1),
|
219
|
-
# cACompromise (2),
|
220
|
-
# affiliationChanged (3),
|
221
|
-
# superseded (4),
|
222
|
-
# cessationOfOperation (5),
|
223
|
-
# certificateHold (6),
|
224
|
-
# removeFromCRL (8),
|
225
|
-
# privilegeWithdrawn (9),
|
226
|
-
# aACompromise (10) }
|
227
|
-
def revoke_cert(serial,reason=nil, revoke_time=Time.now.to_i, generate_and_save=true)
|
228
|
-
if not reason.to_i.between?(0,10)
|
229
|
-
reason = 0
|
230
|
-
end
|
231
|
-
serial = serial.to_i
|
232
|
-
reason = reason.to_i
|
233
|
-
revoke_time = revoke_time.to_i
|
234
|
-
if revoked?(serial)
|
235
|
-
raise R509::R509Error, "Cannot revoke a previously revoked certificate"
|
236
|
-
end
|
237
|
-
@revoked_certs[serial] = {:reason => reason, :revoke_time => revoke_time}
|
238
|
-
if generate_and_save
|
239
|
-
generate_crl()
|
240
|
-
save_crl_list()
|
241
|
-
end
|
242
|
-
nil
|
243
|
-
end
|
244
|
-
|
245
|
-
# Remove serial from revocation list. After unrevoking you must call generate_crl to sign a new CRL
|
246
|
-
#
|
247
|
-
# @param serial [Integer] serial number of the certificate to remove from revocation
|
248
|
-
def unrevoke_cert(serial)
|
249
|
-
@revoked_certs.delete(serial)
|
250
|
-
generate_crl()
|
251
|
-
save_crl_list()
|
252
|
-
nil
|
253
|
-
end
|
254
|
-
|
255
|
-
# Remove serial from revocation list
|
256
|
-
#
|
257
|
-
# @return [String] PEM encoded signed CRL
|
258
|
-
def generate_crl
|
259
|
-
crl = OpenSSL::X509::CRL.new
|
260
|
-
crl.version = 1
|
261
|
-
now = Time.at Time.now.to_i
|
262
|
-
crl.last_update = now-@start_skew_seconds
|
263
|
-
crl.next_update = now+@validity_hours*3600
|
264
|
-
crl.issuer = @config.ca_cert.subject
|
265
|
-
|
266
|
-
self.revoked_certs.each do |serial, reason, revoke_time|
|
267
|
-
revoked = OpenSSL::X509::Revoked.new
|
268
|
-
revoked.serial = OpenSSL::BN.new serial.to_s
|
269
|
-
revoked.time = Time.at(revoke_time)
|
270
|
-
if !reason.nil?
|
271
|
-
enum = OpenSSL::ASN1::Enumerated(reason) #see reason codes below
|
272
|
-
ext = OpenSSL::X509::Extension.new("CRLReason", enum)
|
273
|
-
revoked.add_extension(ext)
|
274
|
-
end
|
275
|
-
#now add it to the crl
|
276
|
-
crl.add_revoked(revoked)
|
277
|
-
end
|
278
|
-
|
279
|
-
ef = OpenSSL::X509::ExtensionFactory.new
|
280
|
-
ef.issuer_certificate = @config.ca_cert.cert
|
281
|
-
ef.crl = crl
|
282
|
-
#grab crl number from file, increment, write back
|
283
|
-
crl_number = increment_crl_number
|
284
|
-
crlnum = OpenSSL::ASN1::Integer(crl_number)
|
285
|
-
crl.add_extension(OpenSSL::X509::Extension.new("crlNumber", crlnum))
|
286
|
-
extensions = []
|
287
|
-
extensions << ["authorityKeyIdentifier", "keyid:always,issuer:always", false]
|
288
|
-
extensions.each{|oid, value, critical|
|
289
|
-
crl.add_extension(ef.create_extension(oid, value, critical))
|
290
|
-
}
|
291
|
-
crl.sign(@config.ca_cert.key.key, OpenSSL::Digest::SHA1.new)
|
292
|
-
@crl = crl
|
293
|
-
@crl.to_pem
|
294
|
-
end
|
295
|
-
|
296
|
-
# @return [Array<Array>] Returns an array of serial, reason, revoke_time
|
297
|
-
# tuples.
|
298
|
-
def revoked_certs
|
299
|
-
ret = []
|
300
|
-
@revoked_certs.keys.sort.each do |serial|
|
301
|
-
ret << [serial, @revoked_certs[serial][:reason], @revoked_certs[serial][:revoke_time]]
|
302
|
-
end
|
303
|
-
ret
|
304
|
-
end
|
305
|
-
|
306
|
-
# Saves the CRL list to a filename or IO. If the class was initialized
|
307
|
-
# with :crl_list_file, then the filename specified by that will be used
|
308
|
-
# by default.
|
309
|
-
# @param [String, #write, nil] filename_or_io If provided, the generated
|
310
|
-
# crl will be written to either the file (if a string), or IO. If nil,
|
311
|
-
# then the @crl_list_file will be used. If that is nil, then an error
|
312
|
-
# will be raised.
|
313
|
-
def save_crl_list(filename_or_io = @crl_list_file)
|
314
|
-
return nil if filename_or_io.nil?
|
315
|
-
|
316
|
-
data = []
|
317
|
-
self.revoked_certs.each do |serial, reason, revoke_time|
|
318
|
-
data << [serial, revoke_time, reason].join(',')
|
319
|
-
end
|
320
|
-
write_data(filename_or_io, data.join("\n"))
|
321
|
-
nil
|
322
|
-
end
|
323
|
-
|
324
|
-
# Save the CRL number to a filename or IO. If the class was initialized
|
325
|
-
# with :crl_number_file, then the filename specified by that will be used
|
326
|
-
# by default.
|
327
|
-
# @param [String, #write, nil] filename_or_io If provided, the current
|
328
|
-
# crl number will be written to either the file (if a string), or IO. If nil,
|
329
|
-
# then the @crl_number_file will be used. If that is nil, then an error
|
330
|
-
# will be raised.
|
331
|
-
def save_crl_number(filename_or_io = @crl_number_file)
|
332
|
-
return nil if filename_or_io.nil?
|
333
|
-
# No valid filename or IO was specified, so bail.
|
334
|
-
|
335
|
-
write_data(filename_or_io, self.crl_number.to_s)
|
336
|
-
nil
|
337
|
-
end
|
338
|
-
|
339
|
-
private
|
340
|
-
|
341
|
-
# Increments the crl_number.
|
342
|
-
# @return [Integer] the new CRL number
|
343
|
-
#
|
344
|
-
def increment_crl_number
|
345
|
-
@crl_number += 1
|
346
|
-
save_crl_number()
|
347
|
-
@crl_number
|
348
|
-
end
|
349
|
-
|
350
|
-
# Loads the certificate revocation list from file.
|
351
|
-
# @param [String, #read, nil] filename_or_io The
|
352
|
-
# crl will be read from either the file (if a string), or IO.
|
353
|
-
def load_crl_list(filename_or_io)
|
354
|
-
@revoked_certs = {}
|
355
|
-
|
356
|
-
if filename_or_io.nil?
|
357
|
-
generate_crl
|
358
|
-
return nil
|
359
|
-
end
|
360
|
-
|
361
|
-
data = read_data(filename_or_io)
|
362
|
-
|
363
|
-
data.each_line do |line|
|
364
|
-
line.chomp!
|
365
|
-
serial, revoke_time, reason = line.split(',', 3)
|
366
|
-
serial = serial.to_i
|
367
|
-
reason = (reason == '') ? nil : reason.to_i
|
368
|
-
revoke_time = (revoke_time == '') ? nil : revoke_time.to_i
|
369
|
-
self.revoke_cert(serial, reason, revoke_time, false)
|
370
|
-
end
|
371
|
-
generate_crl
|
372
|
-
save_crl_list
|
373
|
-
nil
|
374
|
-
end
|
138
|
+
attr_reader :crl_number,:crl_list_file,:crl_number_file, :validity_hours, :crl
|
375
139
|
|
140
|
+
# @param [R509::Config::CAConfig] config
|
141
|
+
def initialize(config)
|
142
|
+
@config = config
|
143
|
+
|
144
|
+
unless @config.kind_of?(R509::Config::CAConfig)
|
145
|
+
raise R509Error, "config must be a kind of R509::Config::CAConfig"
|
146
|
+
end
|
147
|
+
|
148
|
+
@validity_hours = @config.crl_validity_hours
|
149
|
+
@start_skew_seconds = @config.crl_start_skew_seconds
|
150
|
+
@crl = nil
|
151
|
+
|
152
|
+
@crl_number_file = @config.crl_number_file
|
153
|
+
if not @crl_number_file.nil?
|
154
|
+
@crl_number = read_data(@crl_number_file).to_i
|
155
|
+
else
|
156
|
+
@crl_number = 0
|
376
157
|
end
|
158
|
+
|
159
|
+
|
160
|
+
@crl_list_file = @config.crl_list_file
|
161
|
+
load_crl_list(@crl_list_file)
|
162
|
+
end
|
163
|
+
|
164
|
+
# Indicates whether the serial number has been revoked, or not.
|
165
|
+
#
|
166
|
+
# @param [Integer] serial The serial number we want to check
|
167
|
+
# @return [Boolean] True if the serial number was revoked. False, otherwise.
|
168
|
+
def revoked?(serial)
|
169
|
+
@revoked_certs.has_key?(serial)
|
170
|
+
end
|
171
|
+
|
172
|
+
# @return [Array] serial, reason, revoke_time tuple
|
173
|
+
def revoked_cert(serial)
|
174
|
+
@revoked_certs[serial]
|
175
|
+
end
|
176
|
+
|
177
|
+
# Adds a certificate to the revocation list. After calling you must call generate_crl to sign a new CRL
|
178
|
+
#
|
179
|
+
# @param serial [Integer] serial number of the certificate to revoke
|
180
|
+
# @param reason [Integer] reason for revocation
|
181
|
+
# @param revoke_time [Integer]
|
182
|
+
# @param generate_and_save [Boolean] whether we want to generate the CRL and save its file (default=true)
|
183
|
+
#
|
184
|
+
# reason codes defined by rfc 5280
|
185
|
+
#
|
186
|
+
# CRLReason ::= ENUMERATED {
|
187
|
+
# unspecified (0),
|
188
|
+
# keyCompromise (1),
|
189
|
+
# cACompromise (2),
|
190
|
+
# affiliationChanged (3),
|
191
|
+
# superseded (4),
|
192
|
+
# cessationOfOperation (5),
|
193
|
+
# certificateHold (6),
|
194
|
+
# removeFromCRL (8),
|
195
|
+
# privilegeWithdrawn (9),
|
196
|
+
# aACompromise (10) }
|
197
|
+
def revoke_cert(serial,reason=nil, revoke_time=Time.now.to_i, generate_and_save=true)
|
198
|
+
if not reason.to_i.between?(0,10)
|
199
|
+
reason = 0
|
200
|
+
end
|
201
|
+
serial = serial.to_i
|
202
|
+
reason = reason.to_i
|
203
|
+
revoke_time = revoke_time.to_i
|
204
|
+
if revoked?(serial)
|
205
|
+
raise R509::R509Error, "Cannot revoke a previously revoked certificate"
|
206
|
+
end
|
207
|
+
@revoked_certs[serial] = {:reason => reason, :revoke_time => revoke_time}
|
208
|
+
if generate_and_save
|
209
|
+
generate_crl
|
210
|
+
save_crl_list
|
211
|
+
end
|
212
|
+
nil
|
213
|
+
end
|
214
|
+
|
215
|
+
# Remove serial from revocation list. After unrevoking you must call generate_crl to sign a new CRL
|
216
|
+
#
|
217
|
+
# @param serial [Integer] serial number of the certificate to remove from revocation
|
218
|
+
def unrevoke_cert(serial)
|
219
|
+
@revoked_certs.delete(serial)
|
220
|
+
generate_crl
|
221
|
+
save_crl_list
|
222
|
+
nil
|
223
|
+
end
|
224
|
+
|
225
|
+
# Remove serial from revocation list
|
226
|
+
#
|
227
|
+
# @return [String] PEM encoded signed CRL
|
228
|
+
def generate_crl
|
229
|
+
crl = OpenSSL::X509::CRL.new
|
230
|
+
crl.version = 1
|
231
|
+
now = Time.at Time.now.to_i
|
232
|
+
crl.last_update = now-@start_skew_seconds
|
233
|
+
crl.next_update = now+@validity_hours*3600
|
234
|
+
crl.issuer = @config.ca_cert.subject.name
|
235
|
+
|
236
|
+
self.revoked_certs.each do |serial, reason, revoke_time|
|
237
|
+
revoked = OpenSSL::X509::Revoked.new
|
238
|
+
revoked.serial = OpenSSL::BN.new serial.to_s
|
239
|
+
revoked.time = Time.at(revoke_time)
|
240
|
+
if !reason.nil?
|
241
|
+
enum = OpenSSL::ASN1::Enumerated(reason) #see reason codes below
|
242
|
+
ext = OpenSSL::X509::Extension.new("CRLReason", enum)
|
243
|
+
revoked.add_extension(ext)
|
244
|
+
end
|
245
|
+
#now add it to the crl
|
246
|
+
crl.add_revoked(revoked)
|
247
|
+
end
|
248
|
+
|
249
|
+
ef = OpenSSL::X509::ExtensionFactory.new
|
250
|
+
ef.issuer_certificate = @config.ca_cert.cert
|
251
|
+
ef.crl = crl
|
252
|
+
#grab crl number from file, increment, write back
|
253
|
+
crl_number = increment_crl_number
|
254
|
+
crlnum = OpenSSL::ASN1::Integer(crl_number)
|
255
|
+
crl.add_extension(OpenSSL::X509::Extension.new("crlNumber", crlnum))
|
256
|
+
extensions = []
|
257
|
+
extensions << ["authorityKeyIdentifier", "keyid:always,issuer:always", false]
|
258
|
+
extensions.each{|oid, value, critical|
|
259
|
+
crl.add_extension(ef.create_extension(oid, value, critical))
|
260
|
+
}
|
261
|
+
crl.sign(@config.ca_cert.key.key, OpenSSL::Digest::SHA1.new)
|
262
|
+
@crl = R509::CRL::SignedList.new crl
|
263
|
+
@crl.to_pem
|
264
|
+
end
|
265
|
+
|
266
|
+
# @return [Array<Array>] Returns an array of serial, reason, revoke_time
|
267
|
+
# tuples.
|
268
|
+
def revoked_certs
|
269
|
+
ret = []
|
270
|
+
@revoked_certs.keys.sort.each do |serial|
|
271
|
+
ret << [serial, @revoked_certs[serial][:reason], @revoked_certs[serial][:revoke_time]]
|
272
|
+
end
|
273
|
+
ret
|
274
|
+
end
|
275
|
+
|
276
|
+
# Saves the CRL list to a filename or IO. If the class was initialized
|
277
|
+
# with :crl_list_file, then the filename specified by that will be used
|
278
|
+
# by default.
|
279
|
+
# @param [String, #write, nil] filename_or_io If provided, the generated
|
280
|
+
# crl will be written to either the file (if a string), or IO. If nil,
|
281
|
+
# then the @crl_list_file will be used. If that is nil, then an error
|
282
|
+
# will be raised.
|
283
|
+
def save_crl_list(filename_or_io = @crl_list_file)
|
284
|
+
return nil if filename_or_io.nil?
|
285
|
+
|
286
|
+
data = []
|
287
|
+
self.revoked_certs.each do |serial, reason, revoke_time|
|
288
|
+
data << [serial, revoke_time, reason].join(',')
|
289
|
+
end
|
290
|
+
write_data(filename_or_io, data.join("\n"))
|
291
|
+
nil
|
292
|
+
end
|
293
|
+
|
294
|
+
# Save the CRL number to a filename or IO. If the class was initialized
|
295
|
+
# with :crl_number_file, then the filename specified by that will be used
|
296
|
+
# by default.
|
297
|
+
# @param [String, #write, nil] filename_or_io If provided, the current
|
298
|
+
# crl number will be written to either the file (if a string), or IO. If nil,
|
299
|
+
# then the @crl_number_file will be used. If that is nil, then an error
|
300
|
+
# will be raised.
|
301
|
+
def save_crl_number(filename_or_io = @crl_number_file)
|
302
|
+
return nil if filename_or_io.nil?
|
303
|
+
# No valid filename or IO was specified, so bail.
|
304
|
+
|
305
|
+
write_data(filename_or_io, self.crl_number.to_s)
|
306
|
+
nil
|
307
|
+
end
|
308
|
+
|
309
|
+
private
|
310
|
+
|
311
|
+
# Increments the crl_number.
|
312
|
+
# @return [Integer] the new CRL number
|
313
|
+
#
|
314
|
+
def increment_crl_number
|
315
|
+
@crl_number += 1
|
316
|
+
save_crl_number
|
317
|
+
@crl_number
|
318
|
+
end
|
319
|
+
|
320
|
+
# Loads the certificate revocation list from file.
|
321
|
+
# @param [String, #read, nil] filename_or_io The
|
322
|
+
# crl will be read from either the file (if a string), or IO.
|
323
|
+
def load_crl_list(filename_or_io)
|
324
|
+
@revoked_certs = {}
|
325
|
+
|
326
|
+
if filename_or_io.nil?
|
327
|
+
generate_crl
|
328
|
+
return nil
|
329
|
+
end
|
330
|
+
|
331
|
+
data = read_data(filename_or_io)
|
332
|
+
|
333
|
+
data.each_line do |line|
|
334
|
+
line.chomp!
|
335
|
+
serial, revoke_time, reason = line.split(',', 3)
|
336
|
+
serial = serial.to_i
|
337
|
+
reason = (reason == '') ? nil : reason.to_i
|
338
|
+
revoke_time = (revoke_time == '') ? nil : revoke_time.to_i
|
339
|
+
self.revoke_cert(serial, reason, revoke_time, false)
|
340
|
+
end
|
341
|
+
generate_crl
|
342
|
+
save_crl_list
|
343
|
+
nil
|
344
|
+
end
|
345
|
+
|
377
346
|
end
|
347
|
+
end
|
378
348
|
end
|
379
349
|
|