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.
Files changed (203) hide show
  1. data/README.md +343 -151
  2. data/Rakefile +26 -23
  3. data/bin/r509 +126 -112
  4. data/bin/r509-parse +24 -24
  5. data/doc/R509.html +169 -7
  6. data/doc/R509/ASN1.html +370 -0
  7. data/doc/R509/ASN1/GeneralName.html +1121 -0
  8. data/doc/R509/ASN1/GeneralNames.html +843 -0
  9. data/doc/R509/ASN1/NoticeReference.html +392 -0
  10. data/doc/R509/ASN1/PolicyInformation.html +387 -0
  11. data/doc/R509/ASN1/PolicyQualifiers.html +455 -0
  12. data/doc/R509/ASN1/UserNotice.html +386 -0
  13. data/doc/R509/{Crl.html → CRL.html} +7 -7
  14. data/doc/R509/CRL/Administrator.html +1559 -0
  15. data/doc/R509/{Crl/Parser.html → CRL/SignedList.html} +501 -210
  16. data/doc/R509/{Csr.html → CSR.html} +444 -314
  17. data/doc/R509/Cert.html +866 -617
  18. data/doc/R509/Cert/Extensions.html +52 -41
  19. data/doc/R509/Cert/Extensions/AuthorityInfoAccess.html +70 -35
  20. data/doc/R509/Cert/Extensions/AuthorityKeyIdentifier.html +387 -4
  21. data/doc/R509/Cert/Extensions/BasicConstraints.html +61 -25
  22. data/doc/R509/Cert/Extensions/CRLDistributionPoints.html +354 -0
  23. data/doc/R509/Cert/Extensions/CertificatePolicies.html +340 -0
  24. data/doc/R509/Cert/Extensions/ExtendedKeyUsage.html +440 -49
  25. data/doc/R509/Cert/Extensions/{CrlDistributionPoints.html → InhibitAnyPolicy.html} +52 -35
  26. data/doc/R509/Cert/Extensions/KeyUsage.html +247 -121
  27. data/doc/R509/Cert/Extensions/NameConstraints.html +445 -0
  28. data/doc/R509/Cert/Extensions/OCSPNoCheck.html +239 -0
  29. data/doc/R509/Cert/Extensions/PolicyConstraints.html +424 -0
  30. data/doc/R509/Cert/Extensions/SubjectAlternativeName.html +437 -62
  31. data/doc/R509/Cert/Extensions/SubjectKeyIdentifier.html +52 -10
  32. data/doc/R509/CertificateAuthority.html +4 -4
  33. data/doc/R509/CertificateAuthority/Signer.html +154 -187
  34. data/doc/R509/Config.html +6 -6
  35. data/doc/R509/Config/{CaConfig.html → CAConfig.html} +451 -348
  36. data/doc/R509/Config/{CaConfigPool.html → CAConfigPool.html} +47 -47
  37. data/doc/R509/Config/CAProfile.html +1015 -0
  38. data/doc/R509/Config/SubjectItemPolicy.html +86 -86
  39. data/doc/R509/IOHelpers.html +22 -22
  40. data/doc/R509/MessageDigest.html +14 -14
  41. data/doc/R509/NameSanitizer.html +53 -53
  42. data/doc/R509/{Ocsp.html → OCSP.html} +9 -9
  43. data/doc/R509/{Ocsp → OCSP}/Request.html +7 -7
  44. data/doc/R509/{Ocsp → OCSP}/Request/Nonce.html +56 -11
  45. data/doc/R509/{Ocsp → OCSP}/Response.html +44 -44
  46. data/doc/R509/{OidMapper.html → OIDMapper.html} +23 -39
  47. data/doc/R509/PrivateKey.html +415 -168
  48. data/doc/R509/R509Error.html +3 -3
  49. data/doc/R509/{Spki.html → SPKI.html} +354 -192
  50. data/doc/R509/Subject.html +224 -113
  51. data/doc/R509/Validity.html +27 -5
  52. data/doc/R509/Validity/Checker.html +13 -13
  53. data/doc/R509/Validity/DefaultChecker.html +13 -13
  54. data/doc/R509/Validity/DefaultWriter.html +14 -14
  55. data/doc/R509/Validity/Status.html +39 -39
  56. data/doc/R509/Validity/Writer.html +18 -18
  57. data/doc/_index.html +138 -35
  58. data/doc/class_list.html +1 -1
  59. data/doc/css/style.css +10 -0
  60. data/doc/file.README.html +368 -171
  61. data/doc/file.r509.html +92 -69
  62. data/doc/frames.html +1 -1
  63. data/doc/index.html +368 -171
  64. data/doc/method_list.html +910 -390
  65. data/doc/top-level-namespace.html +3 -3
  66. data/lib/r509.rb +32 -16
  67. data/lib/r509/asn1.rb +375 -0
  68. data/lib/r509/cert.rb +381 -364
  69. data/lib/r509/cert/extensions.rb +443 -76
  70. data/lib/r509/certificate_authority.rb +407 -0
  71. data/lib/r509/config.rb +547 -351
  72. data/lib/r509/crl.rb +336 -366
  73. data/lib/r509/csr.rb +278 -289
  74. data/lib/r509/ec-hack.rb +37 -0
  75. data/lib/r509/exceptions.rb +3 -3
  76. data/lib/r509/io_helpers.rb +44 -44
  77. data/lib/r509/message_digest.rb +53 -0
  78. data/lib/r509/ocsp.rb +80 -70
  79. data/lib/r509/oid_mapper.rb +32 -0
  80. data/lib/r509/private_key.rb +228 -0
  81. data/lib/r509/spki.rb +145 -93
  82. data/lib/r509/subject.rb +203 -110
  83. data/lib/r509/validity.rb +70 -68
  84. data/lib/r509/version.rb +2 -2
  85. data/r509.yaml +92 -69
  86. data/spec/asn1_spec.rb +402 -0
  87. data/spec/cert/extensions_spec.rb +957 -494
  88. data/spec/cert_spec.rb +382 -307
  89. data/spec/certificate_authority_spec.rb +668 -250
  90. data/spec/config_spec.rb +515 -302
  91. data/spec/crl_spec.rb +197 -198
  92. data/spec/csr_spec.rb +334 -289
  93. data/spec/fixtures.rb +247 -171
  94. data/spec/fixtures/cert1.der +0 -0
  95. data/spec/fixtures/cert1.pem +0 -0
  96. data/spec/fixtures/cert1_public_key_modulus.txt +0 -0
  97. data/spec/fixtures/cert3.p12 +0 -0
  98. data/spec/fixtures/cert3.pem +0 -0
  99. data/spec/fixtures/cert3_key.pem +0 -0
  100. data/spec/fixtures/cert3_key_des3.pem +0 -0
  101. data/spec/fixtures/cert4.pem +0 -0
  102. data/spec/fixtures/cert5.pem +0 -0
  103. data/spec/fixtures/cert6.pem +0 -0
  104. data/spec/fixtures/cert_expired.pem +0 -0
  105. data/spec/fixtures/cert_inhibit.pem +24 -0
  106. data/spec/fixtures/cert_name_constraints.pem +29 -0
  107. data/spec/fixtures/cert_not_yet_valid.pem +0 -0
  108. data/spec/fixtures/cert_ocsp_no_check.pem +18 -0
  109. data/spec/fixtures/cert_policy_constraints.pem +31 -0
  110. data/spec/fixtures/cert_san.pem +0 -0
  111. data/spec/fixtures/cert_san2.pem +0 -0
  112. data/spec/fixtures/cert_unknown_extension.pem +28 -0
  113. data/spec/fixtures/config_pool_test_minimal.yaml +11 -11
  114. data/spec/fixtures/config_test.yaml +54 -36
  115. data/spec/fixtures/config_test_dsa.yaml +35 -0
  116. data/spec/fixtures/config_test_ec.yaml +35 -0
  117. data/spec/fixtures/config_test_engine_key.yaml +5 -5
  118. data/spec/fixtures/config_test_engine_no_key_name.yaml +4 -4
  119. data/spec/fixtures/config_test_minimal.yaml +4 -4
  120. data/spec/fixtures/config_test_password.yaml +5 -5
  121. data/spec/fixtures/config_test_various.yaml +111 -74
  122. data/spec/fixtures/crl_list_file.txt +0 -0
  123. data/spec/fixtures/crl_with_reason.pem +0 -0
  124. data/spec/fixtures/csr1.der +0 -0
  125. data/spec/fixtures/csr1.pem +0 -0
  126. data/spec/fixtures/csr1_key.der +0 -0
  127. data/spec/fixtures/csr1_key.pem +0 -0
  128. data/spec/fixtures/csr1_key_encrypted_des3.pem +0 -0
  129. data/spec/fixtures/csr1_newlines.pem +0 -0
  130. data/spec/fixtures/csr1_no_begin_end.pem +0 -0
  131. data/spec/fixtures/csr1_public_key_modulus.txt +0 -0
  132. data/spec/fixtures/csr2.pem +0 -0
  133. data/spec/fixtures/csr2_key.pem +0 -0
  134. data/spec/fixtures/csr3.pem +0 -0
  135. data/spec/fixtures/csr4.pem +0 -0
  136. data/spec/fixtures/csr_dsa.pem +0 -0
  137. data/spec/fixtures/csr_invalid_signature.pem +0 -0
  138. data/spec/fixtures/dsa_key.pem +0 -0
  139. data/spec/fixtures/dsa_root.cer +28 -0
  140. data/spec/fixtures/dsa_root.key +20 -0
  141. data/spec/fixtures/ec_csr2.der +0 -0
  142. data/spec/fixtures/ec_csr2.pem +8 -0
  143. data/spec/fixtures/ec_key1.der +0 -0
  144. data/spec/fixtures/ec_key1.pem +6 -0
  145. data/spec/fixtures/ec_key1_encrypted.pem +9 -0
  146. data/spec/fixtures/ec_key2.pem +6 -0
  147. data/spec/fixtures/hmacsha1.sig +1 -0
  148. data/spec/fixtures/hmacsha512.sig +1 -0
  149. data/spec/fixtures/key4.pem +0 -0
  150. data/spec/fixtures/key4_encrypted_des3.pem +0 -0
  151. data/spec/fixtures/missing_key_identifier_ca.cer +0 -0
  152. data/spec/fixtures/missing_key_identifier_ca.key +0 -0
  153. data/spec/fixtures/ocsptest.r509.local.pem +0 -0
  154. data/spec/fixtures/ocsptest.r509.local_ocsp_request.der +0 -0
  155. data/spec/fixtures/ocsptest2.r509.local.pem +0 -0
  156. data/spec/fixtures/second_ca.cer +0 -0
  157. data/spec/fixtures/second_ca.key +0 -0
  158. data/spec/fixtures/spkac.der +0 -0
  159. data/spec/fixtures/spkac.txt +0 -0
  160. data/spec/fixtures/spkac_dsa.txt +1 -1
  161. data/spec/fixtures/spkac_dsa_no_verify.txt +1 -0
  162. data/spec/fixtures/spkac_ec.txt +1 -0
  163. data/spec/fixtures/spkac_rsa_newlines.txt +13 -0
  164. data/spec/fixtures/stca.pem +0 -0
  165. data/spec/fixtures/stca_ocsp_request.der +0 -0
  166. data/spec/fixtures/stca_ocsp_response.der +0 -0
  167. data/spec/fixtures/test1.csr +0 -0
  168. data/spec/fixtures/test_ca.cer +0 -0
  169. data/spec/fixtures/test_ca.key +0 -0
  170. data/spec/fixtures/test_ca.p12 +0 -0
  171. data/spec/fixtures/test_ca_des3.key +0 -0
  172. data/spec/fixtures/test_ca_ec.cer +14 -0
  173. data/spec/fixtures/test_ca_ec.key +6 -0
  174. data/spec/fixtures/test_ca_ec_ee.cer +22 -0
  175. data/spec/fixtures/test_ca_ec_ee.key +6 -0
  176. data/spec/fixtures/test_ca_ocsp.cer +0 -0
  177. data/spec/fixtures/test_ca_ocsp.key +0 -0
  178. data/spec/fixtures/test_ca_ocsp.p12 +0 -0
  179. data/spec/fixtures/test_ca_ocsp_chain.txt +0 -0
  180. data/spec/fixtures/test_ca_ocsp_response.der +0 -0
  181. data/spec/fixtures/test_ca_subroot.cer +0 -0
  182. data/spec/fixtures/test_ca_subroot.key +0 -0
  183. data/spec/fixtures/test_ca_subroot_ocsp.cer +0 -0
  184. data/spec/fixtures/test_ca_subroot_ocsp.key +0 -0
  185. data/spec/fixtures/test_ca_subroot_ocsp_response.der +0 -0
  186. data/spec/fixtures/unknown_oid.csr +0 -0
  187. data/spec/message_digest_spec.rb +104 -84
  188. data/spec/ocsp_spec.rb +105 -105
  189. data/spec/oid_mapper_spec.rb +21 -21
  190. data/spec/private_key_spec.rb +275 -0
  191. data/spec/r509_spec.rb +35 -0
  192. data/spec/spec_helper.rb +15 -6
  193. data/spec/spki_spec.rb +221 -142
  194. data/spec/subject_spec.rb +232 -164
  195. data/spec/validity_spec.rb +91 -91
  196. metadata +79 -25
  197. data/doc/R509/Config/CaProfile.html +0 -651
  198. data/doc/R509/Crl/Administrator.html +0 -2073
  199. data/lib/r509/certificateauthority.rb +0 -290
  200. data/lib/r509/messagedigest.rb +0 -49
  201. data/lib/r509/oidmapper.rb +0 -32
  202. data/lib/r509/privatekey.rb +0 -185
  203. data/spec/privatekey_spec.rb +0 -198
@@ -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
- @is_ca = ! ( self.value =~ /CA:TRUE/ ).nil?
38
- pathlen_match = self.value.match( /pathlen:(\d+)/ )
39
- @path_length = pathlen_match[1].to_i unless pathlen_match.nil?
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
- # sign CA certificates.
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
- # The OpenSSL friendly name for the "digitalSignature" key use.
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
- @allowed_uses = self.value.split(",").map {|use| use.strip}
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 [string] friendly_use_name One of the AU_* constants in this class.
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
- allows?( AU_DIGITAL_SIGNATURE )
168
+ (@digital_signature == true)
97
169
  end
98
170
 
99
171
  def non_repudiation?
100
- allows?( AU_NON_REPUDIATION )
172
+ (@non_repudiation == true)
101
173
  end
102
174
 
103
175
  def key_encipherment?
104
- allows?( AU_KEY_ENCIPHERMENT )
176
+ (@key_encipherment == true)
105
177
  end
106
178
 
107
179
  def data_encipherment?
108
- allows?( AU_DATA_ENCIPHERMENT )
180
+ (@data_encipherment == true)
109
181
  end
110
182
 
111
183
  def key_agreement?
112
- allows?( AU_KEY_AGREEMENT )
184
+ (@key_agreement == true)
113
185
  end
114
186
 
115
- def certificate_sign?
116
- allows?( AU_CERTIFICATE_SIGN )
187
+ def key_cert_sign?
188
+ (@key_cert_sign == true)
117
189
  end
118
190
 
119
191
  def crl_sign?
120
- allows?( AU_CRL_SIGN )
192
+ (@crl_sign == true)
121
193
  end
122
194
 
123
195
  def encipher_only?
124
- allows?( AU_ENCIPHER_ONLY )
196
+ (@encipher_only == true)
125
197
  end
126
198
 
127
199
  def decipher_only?
128
- allows?( AU_DECIPHER_ONLY )
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 friendly name for the "serverAuth" extended key use.
139
- AU_WEB_SERVER_AUTH = "TLS Web Server Authentication"
140
- # The OpenSSL friendly name for the "clientAuth" extended key use.
141
- AU_WEB_CLIENT_AUTH = "TLS Web Client Authentication"
142
- # The OpenSSL friendly name for the "codeSigning" extended key use.
143
- AU_CODE_SIGNING = "Code Signing"
144
- # The OpenSSL friendly name for the "emailProtection" extended key use.
145
- AU_EMAIL_PROTECTION = "E-mail 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 = self.value.split(",").map {|use| use.strip}
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
- allows?( AU_WEB_SERVER_AUTH )
297
+ (@web_server_authentication == true)
165
298
  end
166
299
 
167
300
  def web_client_authentication?
168
- allows?( AU_WEB_CLIENT_AUTH )
301
+ (@web_client_authentication == true)
169
302
  end
170
303
 
171
304
  def code_signing?
172
- allows?( AU_CODE_SIGNING )
305
+ (@code_signing == true)
173
306
  end
174
307
 
175
308
  def email_protection?
176
- allows?( AU_EMAIL_PROTECTION )
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
- # An array of the DNS alternative names, if any
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
- @dns_names = self.value.scan( DNS_REGEX ).map { |match| match[0] }
219
- @ip_addresses = self.value.scan( IP_ADDRESS_REGEX ).map { |match| match[0] }
220
- @uris = self.value.scan( URI_REGEX ).map { |match| match[0] }
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 URIs, if any
231
- attr_reader :ocsp_uris
232
- # An array of the CA issuers URIs, if any
233
- attr_reader :ca_issuers_uris
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
- @ocsp_uris = self.value.scan( /OCSP - #{URI_REGEX}/ ).map { |match| match[0] }
240
- @ca_issuers_uris = self.value.scan( /CA Issuers - #{URI_REGEX}/ ).map { |match| match[0] }
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 CrlDistributionPoints certificate extension, with methods to
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 CrlDistributionPoints < OpenSSL::X509::Extension
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 :crl_uris
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
- @crl_uris = self.value.scan( URI_REGEX ).map { |match| match[0] }
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