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/cert/extensions.rb
CHANGED
@@ -1,16 +1,13 @@
|
|
1
1
|
require 'openssl'
|
2
|
+
require 'r509/asn1'
|
2
3
|
require 'set'
|
3
4
|
|
4
5
|
module R509
|
5
6
|
class Cert
|
7
|
+
# module to contain extension classes for R509::Cert
|
6
8
|
module Extensions
|
7
9
|
|
8
10
|
private
|
9
|
-
# Regexes for OpenSSL's parsed values
|
10
|
-
DNS_REGEX = /DNS:([^,\n]+)/
|
11
|
-
IP_ADDRESS_REGEX = /IP:([^,\n]+)/
|
12
|
-
URI_REGEX = /URI:([^,\n]+)/
|
13
|
-
|
14
11
|
R509_EXTENSION_CLASSES = Set.new
|
15
12
|
|
16
13
|
# Registers a class as being an R509 certificate extension class. Registered
|
@@ -21,10 +18,12 @@ module R509
|
|
21
18
|
R509_EXTENSION_CLASSES << r509_ext_class
|
22
19
|
end
|
23
20
|
|
21
|
+
|
24
22
|
public
|
25
23
|
# Implements the BasicConstraints certificate extension, with methods to
|
26
24
|
# provide access to the components and meaning of the extension's contents.
|
27
25
|
class BasicConstraints < OpenSSL::X509::Extension
|
26
|
+
# friendly name for BasicConstraints OID
|
28
27
|
OID = "basicConstraints"
|
29
28
|
Extensions.register_class(self)
|
30
29
|
|
@@ -34,9 +33,21 @@ module R509
|
|
34
33
|
def initialize(*args)
|
35
34
|
super(*args)
|
36
35
|
|
37
|
-
|
38
|
-
|
39
|
-
|
36
|
+
data = R509::ASN1.get_extension_payload(self)
|
37
|
+
@is_ca = false
|
38
|
+
# BasicConstraints ::= SEQUENCE {
|
39
|
+
# cA BOOLEAN DEFAULT FALSE,
|
40
|
+
# pathLenConstraint INTEGER (0..MAX) OPTIONAL }
|
41
|
+
data.entries.each do |entry|
|
42
|
+
if entry.kind_of?(OpenSSL::ASN1::Boolean)
|
43
|
+
# since the boolean is optional it may not be present
|
44
|
+
@is_ca = entry.value
|
45
|
+
else
|
46
|
+
# There are only two kinds of entries permitted so anything
|
47
|
+
# else is an integer pathlength
|
48
|
+
@path_length = entry.value
|
49
|
+
end
|
50
|
+
end
|
40
51
|
end
|
41
52
|
|
42
53
|
def is_ca?()
|
@@ -44,7 +55,8 @@ module R509
|
|
44
55
|
end
|
45
56
|
|
46
57
|
# Returns true if the path length allows this certificate to be used to
|
47
|
-
#
|
58
|
+
# create subordinate signing certificates beneath it. Does not check if
|
59
|
+
# there is a pathlen restriction in the cert chain above the current cert
|
48
60
|
def allows_sub_ca?()
|
49
61
|
return false if @path_length.nil?
|
50
62
|
return @path_length > 0
|
@@ -54,104 +66,225 @@ module R509
|
|
54
66
|
# Implements the KeyUsage certificate extension, with methods to
|
55
67
|
# provide access to the components and meaning of the extension's contents.
|
56
68
|
class KeyUsage < OpenSSL::X509::Extension
|
69
|
+
# friendly name for KeyUsage OID
|
57
70
|
OID = "keyUsage"
|
58
71
|
Extensions.register_class(self)
|
59
72
|
|
60
|
-
#
|
61
|
-
AU_DIGITAL_SIGNATURE = "Digital Signature"
|
62
|
-
# The OpenSSL friendly name for the "nonRepudiation" key use.
|
63
|
-
AU_NON_REPUDIATION = "Non Repudiation"
|
64
|
-
# The OpenSSL friendly name for the "keyEncipherment" key use.
|
65
|
-
AU_KEY_ENCIPHERMENT = "Key Encipherment"
|
66
|
-
# The OpenSSL friendly name for the "dataEncipherment" key use.
|
67
|
-
AU_DATA_ENCIPHERMENT = "Data Encipherment"
|
68
|
-
# The OpenSSL friendly name for the "keyAgreement" key use.
|
69
|
-
AU_KEY_AGREEMENT = "Key Agreement"
|
70
|
-
# The OpenSSL friendly name for the "keyCertSign" key use.
|
71
|
-
AU_CERTIFICATE_SIGN = "Certificate Sign"
|
72
|
-
# The OpenSSL friendly name for the "cRLSign" key use.
|
73
|
-
AU_CRL_SIGN = "CRL Sign"
|
74
|
-
# The OpenSSL friendly name for the "encipherOnly" key use.
|
75
|
-
AU_ENCIPHER_ONLY = "Encipher Only"
|
76
|
-
# The OpenSSL friendly name for the "decipherOnly" key use.
|
77
|
-
AU_DECIPHER_ONLY = "Decipher Only"
|
78
|
-
|
79
|
-
# An array of the key uses allowed. See the AU_* constants in this class.
|
73
|
+
# An array of the key uses allowed.
|
80
74
|
attr_reader :allowed_uses
|
81
75
|
|
76
|
+
# OpenSSL short name for Digital Signature
|
77
|
+
AU_DIGITAL_SIGNATURE = "digitalSignature"
|
78
|
+
# OpenSSL short name for Non Repudiation (also known as content commitment)
|
79
|
+
AU_NON_REPUDIATION = "nonRepudiation"
|
80
|
+
# OpenSSL short name for Key Encipherment
|
81
|
+
AU_KEY_ENCIPHERMENT = "keyEncipherment"
|
82
|
+
# OpenSSL short name for Data Encipherment
|
83
|
+
AU_DATA_ENCIPHERMENT = "dataEncipherment"
|
84
|
+
# OpenSSL short name for Key Agreement
|
85
|
+
AU_KEY_AGREEMENT = "keyAgreement"
|
86
|
+
# OpenSSL short name for Certificate Sign
|
87
|
+
AU_KEY_CERT_SIGN = "keyCertSign"
|
88
|
+
# OpenSSL short name for CRL Sign
|
89
|
+
AU_CRL_SIGN = "cRLSign"
|
90
|
+
# OpenSSL short name for Encipher Only
|
91
|
+
AU_ENCIPHER_ONLY = "encipherOnly"
|
92
|
+
# OpenSSL short name for Decipher Only
|
93
|
+
AU_DECIPHER_ONLY = "decipherOnly"
|
94
|
+
|
82
95
|
# See OpenSSL::X509::Extension#initialize
|
83
96
|
def initialize(*args)
|
84
97
|
super(*args)
|
85
98
|
|
86
|
-
|
99
|
+
data = R509::ASN1.get_extension_payload(self)
|
100
|
+
|
101
|
+
# There are 9 possible bits, which means we need 2 bytes
|
102
|
+
# to represent them all. When the last bit is not set
|
103
|
+
# the second byte is not encoded. let's add it back so we can
|
104
|
+
# have the full bitmask for comparison
|
105
|
+
if data.size == 1
|
106
|
+
data = data + "\0"
|
107
|
+
end
|
108
|
+
bit_mask = data.unpack('n')[0] # treat it as a 16-bit unsigned big endian
|
109
|
+
# KeyUsage ::= BIT STRING {
|
110
|
+
# digitalSignature (0),
|
111
|
+
# nonRepudiation (1), -- recent editions of X.509 have
|
112
|
+
# -- renamed this bit to contentCommitment
|
113
|
+
# keyEncipherment (2),
|
114
|
+
# dataEncipherment (3),
|
115
|
+
# keyAgreement (4),
|
116
|
+
# keyCertSign (5),
|
117
|
+
# cRLSign (6),
|
118
|
+
# encipherOnly (7),
|
119
|
+
# decipherOnly (8) }
|
120
|
+
@allowed_uses = []
|
121
|
+
if bit_mask & 0b1000000000000000 > 0
|
122
|
+
@digital_signature = true
|
123
|
+
@allowed_uses << AU_DIGITAL_SIGNATURE
|
124
|
+
end
|
125
|
+
if bit_mask & 0b0100000000000000 > 0
|
126
|
+
@non_repudiation = true
|
127
|
+
@allowed_uses << AU_NON_REPUDIATION
|
128
|
+
end
|
129
|
+
if bit_mask & 0b0010000000000000 > 0
|
130
|
+
@key_encipherment = true
|
131
|
+
@allowed_uses << AU_KEY_ENCIPHERMENT
|
132
|
+
end
|
133
|
+
if bit_mask & 0b0001000000000000 > 0
|
134
|
+
@data_encipherment = true
|
135
|
+
@allowed_uses << AU_DATA_ENCIPHERMENT
|
136
|
+
end
|
137
|
+
if bit_mask & 0b0000100000000000 > 0
|
138
|
+
@key_agreement = true
|
139
|
+
@allowed_uses << AU_KEY_AGREEMENT
|
140
|
+
end
|
141
|
+
if bit_mask & 0b0000010000000000 > 0
|
142
|
+
@key_cert_sign = true
|
143
|
+
@allowed_uses << AU_KEY_CERT_SIGN
|
144
|
+
end
|
145
|
+
if bit_mask & 0b0000001000000000 > 0
|
146
|
+
@crl_sign = true
|
147
|
+
@allowed_uses << AU_CRL_SIGN
|
148
|
+
end
|
149
|
+
if bit_mask & 0b0000000100000000 > 0
|
150
|
+
@encipher_only = true
|
151
|
+
@allowed_uses << AU_ENCIPHER_ONLY
|
152
|
+
end
|
153
|
+
if bit_mask & 0b0000000010000000 > 0
|
154
|
+
@decipher_only = true
|
155
|
+
@allowed_uses << AU_DECIPHER_ONLY
|
156
|
+
end
|
87
157
|
end
|
88
158
|
|
89
159
|
# Returns true if the given use is allowed by this extension.
|
90
|
-
# @param [
|
160
|
+
# @param [String] friendly_use_name key usage short name (e.g. digitalSignature, cRLSign, etc)
|
161
|
+
# or one of the AU_* constants in this class
|
162
|
+
# @return [Boolean]
|
91
163
|
def allows?( friendly_use_name )
|
92
164
|
@allowed_uses.include?( friendly_use_name )
|
93
165
|
end
|
94
166
|
|
95
167
|
def digital_signature?
|
96
|
-
|
168
|
+
(@digital_signature == true)
|
97
169
|
end
|
98
170
|
|
99
171
|
def non_repudiation?
|
100
|
-
|
172
|
+
(@non_repudiation == true)
|
101
173
|
end
|
102
174
|
|
103
175
|
def key_encipherment?
|
104
|
-
|
176
|
+
(@key_encipherment == true)
|
105
177
|
end
|
106
178
|
|
107
179
|
def data_encipherment?
|
108
|
-
|
180
|
+
(@data_encipherment == true)
|
109
181
|
end
|
110
182
|
|
111
183
|
def key_agreement?
|
112
|
-
|
184
|
+
(@key_agreement == true)
|
113
185
|
end
|
114
186
|
|
115
|
-
def
|
116
|
-
|
187
|
+
def key_cert_sign?
|
188
|
+
(@key_cert_sign == true)
|
117
189
|
end
|
118
190
|
|
119
191
|
def crl_sign?
|
120
|
-
|
192
|
+
(@crl_sign == true)
|
121
193
|
end
|
122
194
|
|
123
195
|
def encipher_only?
|
124
|
-
|
196
|
+
(@encipher_only == true)
|
125
197
|
end
|
126
198
|
|
127
199
|
def decipher_only?
|
128
|
-
|
200
|
+
(@decipher_only == true)
|
129
201
|
end
|
130
202
|
end
|
131
203
|
|
132
204
|
# Implements the ExtendedKeyUsage certificate extension, with methods to
|
133
205
|
# provide access to the components and meaning of the extension's contents.
|
134
206
|
class ExtendedKeyUsage < OpenSSL::X509::Extension
|
207
|
+
# friendly name for EKU OID
|
135
208
|
OID = "extendedKeyUsage"
|
136
209
|
Extensions.register_class(self)
|
137
210
|
|
138
|
-
# The OpenSSL
|
139
|
-
AU_WEB_SERVER_AUTH = "
|
140
|
-
# The OpenSSL
|
141
|
-
AU_WEB_CLIENT_AUTH = "
|
142
|
-
# The OpenSSL
|
143
|
-
AU_CODE_SIGNING = "
|
144
|
-
# The OpenSSL
|
145
|
-
AU_EMAIL_PROTECTION = "
|
211
|
+
# The OpenSSL short name for TLS Web Server Authentication
|
212
|
+
AU_WEB_SERVER_AUTH = "serverAuth"
|
213
|
+
# The OpenSSL short name for TLS Web Client Authentication
|
214
|
+
AU_WEB_CLIENT_AUTH = "clientAuth"
|
215
|
+
# The OpenSSL short name for Code Signing
|
216
|
+
AU_CODE_SIGNING = "codeSigning"
|
217
|
+
# The OpenSSL short name for E-mail Protection
|
218
|
+
AU_EMAIL_PROTECTION = "emailProtection"
|
219
|
+
# The OpenSSL short name for OCSP Signing
|
220
|
+
AU_OCSP_SIGNING = "OCSPSigning"
|
221
|
+
# The OpenSSL short name for Time Stamping
|
222
|
+
AU_TIME_STAMPING = "timeStamping"
|
223
|
+
# The OpenSSL short name for Any Extended Key Usage
|
224
|
+
AU_ANY_EXTENDED_KEY_USAGE = "anyExtendedKeyUsage"
|
146
225
|
|
147
|
-
# An array of the key uses allowed. See the AU_* constants in this class.
|
148
226
|
attr_reader :allowed_uses
|
149
227
|
|
150
228
|
# See OpenSSL::X509::Extension#initialize
|
151
229
|
def initialize(*args)
|
152
230
|
super(*args)
|
153
231
|
|
154
|
-
@allowed_uses =
|
232
|
+
@allowed_uses = []
|
233
|
+
data = R509::ASN1.get_extension_payload(self)
|
234
|
+
|
235
|
+
data.entries.each do |eku|
|
236
|
+
# The following key usage purposes are defined:
|
237
|
+
#
|
238
|
+
# anyExtendedKeyUsage OBJECT IDENTIFIER ::= { id-ce-extKeyUsage 0 }
|
239
|
+
#
|
240
|
+
# id-kp OBJECT IDENTIFIER ::= { id-pkix 3 }
|
241
|
+
# id-kp-serverAuth OBJECT IDENTIFIER ::= { id-kp 1 }
|
242
|
+
# -- TLS WWW server authentication
|
243
|
+
# -- Key usage bits that may be consistent: digitalSignature,
|
244
|
+
# -- keyEncipherment or keyAgreement
|
245
|
+
#
|
246
|
+
# id-kp-clientAuth OBJECT IDENTIFIER ::= { id-kp 2 }
|
247
|
+
# -- TLS WWW client authentication
|
248
|
+
# -- Key usage bits that may be consistent: digitalSignature
|
249
|
+
# -- and/or keyAgreement
|
250
|
+
#
|
251
|
+
# id-kp-codeSigning OBJECT IDENTIFIER ::= { id-kp 3 }
|
252
|
+
# -- Signing of downloadable executable code
|
253
|
+
# -- Key usage bits that may be consistent: digitalSignature
|
254
|
+
#
|
255
|
+
# id-kp-emailProtection OBJECT IDENTIFIER ::= { id-kp 4 }
|
256
|
+
# -- Email protection
|
257
|
+
# -- Key usage bits that may be consistent: digitalSignature,
|
258
|
+
# -- nonRepudiation, and/or (keyEncipherment or keyAgreement)
|
259
|
+
#
|
260
|
+
# id-kp-timeStamping OBJECT IDENTIFIER ::= { id-kp 8 }
|
261
|
+
# -- Binding the hash of an object to a time
|
262
|
+
# -- Key usage bits that may be consistent: digitalSignature
|
263
|
+
# -- and/or nonRepudiation
|
264
|
+
#
|
265
|
+
# id-kp-OCSPSigning OBJECT IDENTIFIER ::= { id-kp 9 }
|
266
|
+
# -- Signing OCSP responses
|
267
|
+
# -- Key usage bits that may be consistent: digitalSignature
|
268
|
+
# -- and/or nonRepudiation
|
269
|
+
|
270
|
+
case eku.value
|
271
|
+
when AU_WEB_SERVER_AUTH
|
272
|
+
@web_server_authentication = true
|
273
|
+
when AU_WEB_CLIENT_AUTH
|
274
|
+
@web_client_authentication = true
|
275
|
+
when AU_CODE_SIGNING
|
276
|
+
@code_signing = true
|
277
|
+
when AU_EMAIL_PROTECTION
|
278
|
+
@email_protection = true
|
279
|
+
when AU_OCSP_SIGNING
|
280
|
+
@ocsp_signing = true
|
281
|
+
when AU_TIME_STAMPING
|
282
|
+
@time_stamping = true
|
283
|
+
when AU_ANY_EXTENDED_KEY_USAGE
|
284
|
+
@any_extended_key_usage = true
|
285
|
+
end
|
286
|
+
@allowed_uses << eku.value
|
287
|
+
end
|
155
288
|
end
|
156
289
|
|
157
290
|
# Returns true if the given use is allowed by this extension.
|
@@ -161,30 +294,42 @@ module R509
|
|
161
294
|
end
|
162
295
|
|
163
296
|
def web_server_authentication?
|
164
|
-
|
297
|
+
(@web_server_authentication == true)
|
165
298
|
end
|
166
299
|
|
167
300
|
def web_client_authentication?
|
168
|
-
|
301
|
+
(@web_client_authentication == true)
|
169
302
|
end
|
170
303
|
|
171
304
|
def code_signing?
|
172
|
-
|
305
|
+
(@code_signing == true)
|
173
306
|
end
|
174
307
|
|
175
308
|
def email_protection?
|
176
|
-
|
309
|
+
(@email_protection == true)
|
177
310
|
end
|
178
311
|
|
179
|
-
|
312
|
+
def ocsp_signing?
|
313
|
+
(@ocsp_signing == true)
|
314
|
+
end
|
315
|
+
|
316
|
+
def time_stamping?
|
317
|
+
(@time_stamping == true)
|
318
|
+
end
|
319
|
+
|
320
|
+
def any_extended_key_usage?
|
321
|
+
(@any_extended_key_usage == true)
|
322
|
+
end
|
180
323
|
end
|
181
324
|
|
182
325
|
# Implements the SubjectKeyIdentifier certificate extension, with methods to
|
183
326
|
# provide access to the components and meaning of the extension's contents.
|
184
327
|
class SubjectKeyIdentifier < OpenSSL::X509::Extension
|
328
|
+
# friendly name for Subject Key Identifier OID
|
185
329
|
OID = "subjectKeyIdentifier"
|
186
330
|
Extensions.register_class(self)
|
187
331
|
|
332
|
+
# @return value of key
|
188
333
|
def key()
|
189
334
|
return self.value
|
190
335
|
end
|
@@ -193,71 +338,293 @@ module R509
|
|
193
338
|
# Implements the AuthorityKeyIdentifier certificate extension, with methods to
|
194
339
|
# provide access to the components and meaning of the extension's contents.
|
195
340
|
class AuthorityKeyIdentifier < OpenSSL::X509::Extension
|
341
|
+
# friendly name for Authority Key Identifier OID
|
196
342
|
OID = "authorityKeyIdentifier"
|
197
343
|
Extensions.register_class(self)
|
198
344
|
|
345
|
+
# key_identifier, if present, will be a hex string delimited by colons
|
346
|
+
# authority_cert_issuer, if present, will be a GeneralName object
|
347
|
+
# authority_cert_serial_number, if present, will be a hex string delimited by colons
|
348
|
+
attr_reader :key_identifier, :authority_cert_issuer, :authority_cert_serial_number
|
349
|
+
|
350
|
+
def initialize(*args)
|
351
|
+
super(*args)
|
352
|
+
|
353
|
+
data = R509::ASN1.get_extension_payload(self)
|
354
|
+
# AuthorityKeyIdentifier ::= SEQUENCE {
|
355
|
+
# keyIdentifier [0] KeyIdentifier OPTIONAL,
|
356
|
+
# authorityCertIssuer [1] GeneralNames OPTIONAL,
|
357
|
+
# authorityCertSerialNumber [2] CertificateSerialNumber OPTIONAL }
|
358
|
+
data.entries.each do |el|
|
359
|
+
case el.tag
|
360
|
+
when 0
|
361
|
+
@key_identifier = el.value.unpack("H*")[0].upcase.scan(/../).join(":")
|
362
|
+
when 1
|
363
|
+
@authority_cert_issuer = R509::ASN1::GeneralName.new(el.value.first)
|
364
|
+
when 2
|
365
|
+
arr = el.value.unpack("H*")[0].upcase.scan(/../)
|
366
|
+
# OpenSSL's convention is to drop leading 00s, so let's strip that off if
|
367
|
+
# present
|
368
|
+
if arr[0] == "00"
|
369
|
+
arr.delete_at(0)
|
370
|
+
end
|
371
|
+
@authority_cert_serial_number = arr.join(":")
|
372
|
+
end
|
373
|
+
end
|
374
|
+
|
375
|
+
end
|
376
|
+
|
199
377
|
end
|
200
378
|
|
201
379
|
# Implements the SubjectAlternativeName certificate extension, with methods to
|
202
380
|
# provide access to the components and meaning of the extension's contents.
|
203
381
|
class SubjectAlternativeName < OpenSSL::X509::Extension
|
382
|
+
# friendly name for SAN OID
|
204
383
|
OID = "subjectAltName"
|
205
384
|
Extensions.register_class(self)
|
206
385
|
|
207
|
-
|
208
|
-
attr_reader :dns_names
|
209
|
-
# An array of the IP-address alternative names, if any
|
210
|
-
attr_reader :ip_addresses
|
211
|
-
# An array of the URI alternative names, if any
|
212
|
-
attr_reader :uris
|
386
|
+
attr_reader :general_names
|
213
387
|
|
214
388
|
# See OpenSSL::X509::Extension#initialize
|
215
389
|
def initialize(*args)
|
216
390
|
super(*args)
|
217
391
|
|
218
|
-
|
219
|
-
@
|
220
|
-
|
392
|
+
data = R509::ASN1.get_extension_payload(self)
|
393
|
+
@general_names = R509::ASN1::GeneralNames.new
|
394
|
+
data.entries.each do |gn|
|
395
|
+
@general_names.add_item(gn)
|
396
|
+
end
|
397
|
+
end
|
398
|
+
|
399
|
+
# @return [Array<String>] DNS names
|
400
|
+
def dns_names
|
401
|
+
@general_names.dns_names
|
402
|
+
end
|
403
|
+
|
404
|
+
# @return [Array<String>] IP addresses formatted as dotted quad
|
405
|
+
def ip_addresses
|
406
|
+
@general_names.ip_addresses
|
407
|
+
end
|
408
|
+
|
409
|
+
# @return [Array<String>] email addresses
|
410
|
+
def rfc_822_names
|
411
|
+
@general_names.rfc_822_names
|
412
|
+
end
|
413
|
+
|
414
|
+
# @return [Array<String>] URIs (not typically found in SAN extensions)
|
415
|
+
def uris
|
416
|
+
@general_names.uris
|
417
|
+
end
|
418
|
+
|
419
|
+
# @return [Array<R509::Subject>] directory names
|
420
|
+
def directory_names
|
421
|
+
@general_names.directory_names
|
422
|
+
end
|
423
|
+
|
424
|
+
# @return [Array] array of GeneralName objects preserving order found in the extension
|
425
|
+
def names
|
426
|
+
@general_names.names
|
221
427
|
end
|
222
428
|
end
|
223
429
|
|
224
430
|
# Implements the AuthorityInfoAccess certificate extension, with methods to
|
225
431
|
# provide access to the components and meaning of the extension's contents.
|
226
432
|
class AuthorityInfoAccess < OpenSSL::X509::Extension
|
433
|
+
# friendly name for AIA OID
|
227
434
|
OID = "authorityInfoAccess"
|
228
435
|
Extensions.register_class(self)
|
229
436
|
|
230
|
-
# An array of the OCSP
|
231
|
-
attr_reader :
|
232
|
-
# An array of the CA issuers
|
233
|
-
attr_reader :
|
437
|
+
# An array of the OCSP data, if any
|
438
|
+
attr_reader :ocsp
|
439
|
+
# An array of the CA issuers data, if any
|
440
|
+
attr_reader :ca_issuers
|
234
441
|
|
235
442
|
# See OpenSSL::X509::Extension#initialize
|
236
443
|
def initialize(*args)
|
237
444
|
super(*args)
|
238
445
|
|
239
|
-
|
240
|
-
@
|
446
|
+
data = R509::ASN1.get_extension_payload(self)
|
447
|
+
@ocsp= R509::ASN1::GeneralNames.new
|
448
|
+
@ca_issuers= R509::ASN1::GeneralNames.new
|
449
|
+
data.entries.each do |access_description|
|
450
|
+
# AccessDescription ::= SEQUENCE {
|
451
|
+
# accessMethod OBJECT IDENTIFIER,
|
452
|
+
# accessLocation GeneralName }
|
453
|
+
case access_description.entries[0].value
|
454
|
+
when "OCSP"
|
455
|
+
@ocsp.add_item(access_description.entries[1])
|
456
|
+
when "caIssuers"
|
457
|
+
@ca_issuers.add_item(access_description.entries[1])
|
458
|
+
end
|
459
|
+
end
|
241
460
|
end
|
242
461
|
end
|
243
462
|
|
244
|
-
# Implements the
|
463
|
+
# Implements the CRLDistributionPoints certificate extension, with methods to
|
245
464
|
# provide access to the components and meaning of the extension's contents.
|
246
|
-
class
|
465
|
+
class CRLDistributionPoints < OpenSSL::X509::Extension
|
466
|
+
# friendly name for CDP OID
|
247
467
|
OID = "crlDistributionPoints"
|
248
468
|
Extensions.register_class(self)
|
249
469
|
|
250
470
|
# An array of the CRL URIs, if any
|
251
|
-
attr_reader :
|
471
|
+
attr_reader :crl
|
472
|
+
|
473
|
+
# See OpenSSL::X509::Extension#initialize
|
474
|
+
def initialize(*args)
|
475
|
+
super(*args)
|
476
|
+
|
477
|
+
@crl= R509::ASN1::GeneralNames.new
|
478
|
+
data = R509::ASN1.get_extension_payload(self)
|
479
|
+
data.entries.each do |distribution_point|
|
480
|
+
# DistributionPoint ::= SEQUENCE {
|
481
|
+
# distributionPoint [0] DistributionPointName OPTIONAL,
|
482
|
+
# reasons [1] ReasonFlags OPTIONAL,
|
483
|
+
# cRLIssuer [2] GeneralNames OPTIONAL }
|
484
|
+
# DistributionPointName ::= CHOICE {
|
485
|
+
# fullName [0] GeneralNames,
|
486
|
+
# nameRelativeToCRLIssuer [1] RelativeDistinguishedName }
|
487
|
+
# We're only going to handle DistributionPointName [0] for now
|
488
|
+
# so grab entries[0] and then get the fullName with value[0]
|
489
|
+
# and the value of that ASN1Data with value[0] again
|
490
|
+
@crl.add_item(distribution_point.entries[0].value[0].value[0])
|
491
|
+
end
|
492
|
+
end
|
493
|
+
end
|
494
|
+
|
495
|
+
# Implements the OCSP noCheck certificate extension
|
496
|
+
class OCSPNoCheck < OpenSSL::X509::Extension
|
497
|
+
# friendly name for OCSP No Check
|
498
|
+
OID = "noCheck"
|
499
|
+
Extensions.register_class(self)
|
252
500
|
|
253
501
|
# See OpenSSL::X509::Extension#initialize
|
502
|
+
def initialize(*args)
|
503
|
+
super(*args)
|
504
|
+
end
|
505
|
+
end
|
506
|
+
|
507
|
+
|
508
|
+
# Implements the CertificatePolicies certificate extension, with methods to
|
509
|
+
# provide access to the components and meaning of the extension's contents.
|
510
|
+
class CertificatePolicies < OpenSSL::X509::Extension
|
511
|
+
# friendly name for CP OID
|
512
|
+
OID = "certificatePolicies"
|
513
|
+
Extensions.register_class(self)
|
514
|
+
attr_reader :policies
|
515
|
+
|
516
|
+
def initialize(*args)
|
517
|
+
@policies = []
|
518
|
+
super(*args)
|
519
|
+
|
520
|
+
data = R509::ASN1.get_extension_payload(self)
|
521
|
+
|
522
|
+
# each element of this sequence should be part of a policy + qualifiers
|
523
|
+
# certificatePolicies ::= SEQUENCE SIZE (1..MAX) OF PolicyInformation
|
524
|
+
data.each do |cp|
|
525
|
+
@policies << R509::ASN1::PolicyInformation.new(cp)
|
526
|
+
end if data.respond_to?(:each)
|
527
|
+
end
|
528
|
+
end
|
529
|
+
|
530
|
+
# Implements the InhibitAnyPolicy certificate extension, with methods to
|
531
|
+
# provide access to the component and meaning of the extension's contents.
|
532
|
+
class InhibitAnyPolicy < OpenSSL::X509::Extension
|
533
|
+
# friendly name for CP OID
|
534
|
+
OID = "inhibitAnyPolicy"
|
535
|
+
Extensions.register_class(self)
|
536
|
+
|
537
|
+
attr_reader :skip_certs
|
538
|
+
|
254
539
|
def initialize(*args)
|
255
540
|
super(*args)
|
256
541
|
|
257
|
-
|
542
|
+
# id-ce-inhibitAnyPolicy OBJECT IDENTIFIER ::= { id-ce 54 }
|
543
|
+
# InhibitAnyPolicy ::= SkipCerts
|
544
|
+
# SkipCerts ::= INTEGER (0..MAX)
|
545
|
+
@skip_certs = R509::ASN1.get_extension_payload(self) # returns a non-negative integer
|
258
546
|
end
|
259
547
|
end
|
260
548
|
|
549
|
+
# Implements the PolicyConstraints certificate extension, with methods to
|
550
|
+
# provide access to the components and meaning of the extension's contents.
|
551
|
+
class PolicyConstraints < OpenSSL::X509::Extension
|
552
|
+
# friendly name for CP OID
|
553
|
+
OID = "policyConstraints"
|
554
|
+
Extensions.register_class(self)
|
555
|
+
|
556
|
+
attr_reader :require_explicit_policy
|
557
|
+
attr_reader :inhibit_policy_mapping
|
558
|
+
|
559
|
+
def initialize(*args)
|
560
|
+
super(*args)
|
561
|
+
|
562
|
+
# id-ce-policyConstraints OBJECT IDENTIFIER ::= { id-ce 36 }
|
563
|
+
# PolicyConstraints ::= SEQUENCE {
|
564
|
+
# requireExplicitPolicy [0] SkipCerts OPTIONAL,
|
565
|
+
# inhibitPolicyMapping [1] SkipCerts OPTIONAL }
|
566
|
+
#
|
567
|
+
# SkipCerts ::= INTEGER (0..MAX)
|
568
|
+
data = R509::ASN1.get_extension_payload(self)
|
569
|
+
data.each do |pc|
|
570
|
+
if pc.tag == 0
|
571
|
+
@require_explicit_policy = pc.value.bytes.to_a[0]
|
572
|
+
elsif pc.tag == 1
|
573
|
+
@inhibit_policy_mapping = pc.value.bytes.to_a[0]
|
574
|
+
end
|
575
|
+
end
|
576
|
+
end
|
577
|
+
end
|
578
|
+
|
579
|
+
# Implements the NameConstraints certificate extension, with methods to
|
580
|
+
# provide access to the components and meaning of the extension's contents.
|
581
|
+
class NameConstraints < OpenSSL::X509::Extension
|
582
|
+
# friendly name for CP OID
|
583
|
+
OID = "nameConstraints"
|
584
|
+
Extensions.register_class(self)
|
585
|
+
|
586
|
+
attr_reader :permitted_names, :excluded_names
|
587
|
+
|
588
|
+
# id-ce-nameConstraints OBJECT IDENTIFIER ::= { id-ce 30 }
|
589
|
+
# NameConstraints ::= SEQUENCE {
|
590
|
+
# permittedSubtrees [0] GeneralSubtrees OPTIONAL,
|
591
|
+
# excludedSubtrees [1] GeneralSubtrees OPTIONAL }
|
592
|
+
#
|
593
|
+
# GeneralSubtrees ::= SEQUENCE SIZE (1..MAX) OF GeneralSubtree
|
594
|
+
#
|
595
|
+
# per RFC 5280
|
596
|
+
# Within this profile, the minimum and maximum fields are not used with
|
597
|
+
# any name forms, thus, the minimum MUST be zero, and maximum MUST be
|
598
|
+
# absent
|
599
|
+
# GeneralSubtree ::= SEQUENCE {
|
600
|
+
# base GeneralName,
|
601
|
+
# minimum [0] BaseDistance DEFAULT 0,
|
602
|
+
# maximum [1] BaseDistance OPTIONAL }
|
603
|
+
#
|
604
|
+
# BaseDistance ::= INTEGER (0..MAX)
|
605
|
+
def initialize(*args)
|
606
|
+
super(*args)
|
607
|
+
|
608
|
+
@permitted_names = []
|
609
|
+
@excluded_names = []
|
610
|
+
|
611
|
+
data = R509::ASN1.get_extension_payload(self)
|
612
|
+
data.each do |gs|
|
613
|
+
gs.value.each do |asn_data|
|
614
|
+
asn_data.value.each do |obj|
|
615
|
+
gn = R509::ASN1::GeneralName.new(obj)
|
616
|
+
if gs.tag == 0 # permittedSubtrees
|
617
|
+
@permitted_names << gn
|
618
|
+
elsif gs.tag == 1 #excludedSubtrees
|
619
|
+
@excluded_names << gn
|
620
|
+
end
|
621
|
+
end
|
622
|
+
end
|
623
|
+
end
|
624
|
+
end
|
625
|
+
end
|
626
|
+
|
627
|
+
|
261
628
|
|
262
629
|
#
|
263
630
|
# Helper class methods
|
@@ -275,7 +642,7 @@ module R509
|
|
275
642
|
if r509_extensions.has_key?(r509_class)
|
276
643
|
raise ArgumentError.new("Only one extension object allowed per OID")
|
277
644
|
end
|
278
|
-
|
645
|
+
|
279
646
|
r509_extensions[r509_class] = r509_class.new( openssl_extension )
|
280
647
|
break
|
281
648
|
end
|