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
@@ -6,7 +6,7 @@
|
|
6
6
|
<title>
|
7
7
|
Top Level Namespace
|
8
8
|
|
9
|
-
— Documentation by YARD 0.8.
|
9
|
+
— Documentation by YARD 0.8.5
|
10
10
|
|
11
11
|
</title>
|
12
12
|
|
@@ -103,9 +103,9 @@
|
|
103
103
|
</div>
|
104
104
|
|
105
105
|
<div id="footer">
|
106
|
-
Generated on
|
106
|
+
Generated on Tue Apr 16 10:49:55 2013 by
|
107
107
|
<a href="http://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
|
108
|
-
0.8.
|
108
|
+
0.8.5 (ruby-1.9.3).
|
109
109
|
</div>
|
110
110
|
|
111
111
|
</body>
|
data/lib/r509.rb
CHANGED
@@ -1,22 +1,38 @@
|
|
1
1
|
# A module for building an easy to use CA. Includes CSR, Certificate, and CRL support.
|
2
2
|
module R509
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
3
|
+
require('r509/certificate_authority.rb')
|
4
|
+
require('r509/csr.rb')
|
5
|
+
require('r509/spki.rb')
|
6
|
+
require('r509/cert.rb')
|
7
|
+
require('r509/crl.rb')
|
8
|
+
require('r509/oid_mapper.rb')
|
9
|
+
require('r509/ocsp.rb')
|
10
|
+
require('r509/config.rb')
|
11
|
+
require('r509/private_key.rb')
|
12
|
+
require('r509/message_digest.rb')
|
13
|
+
require('r509/subject.rb')
|
14
|
+
require('r509/validity.rb')
|
15
|
+
require('r509/ec-hack.rb')
|
16
|
+
require('r509/asn1.rb')
|
17
|
+
require('r509/version.rb')
|
18
|
+
|
19
|
+
# print version information to console
|
20
|
+
def self.print_debug
|
21
|
+
puts "r509 v#{R509::VERSION}"
|
22
|
+
puts OpenSSL::OPENSSL_VERSION
|
23
|
+
puts "Ruby #{RUBY_VERSION}-p#{RUBY_PATCHLEVEL}"
|
24
|
+
puts "Elliptic curve support: #{self.ec_supported?}"
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.ec_supported?
|
28
|
+
(not defined?(OpenSSL::PKey::EC::UNSUPPORTED))
|
29
|
+
end
|
15
30
|
end
|
16
31
|
|
17
32
|
#add some global mappings we want available throughout r509
|
18
|
-
R509::
|
19
|
-
|
20
|
-
|
21
|
-
|
33
|
+
R509::OIDMapper.batch_register([
|
34
|
+
{ :oid => "2.5.4.15", :short_name => "businessCategory" }, # extended validation related
|
35
|
+
{ :oid => "1.3.6.1.4.1.311.60.2.1.2", :short_name => "jurisdictionOfIncorporationStateOrProvinceName" }, # extended validation related
|
36
|
+
{ :oid => "1.3.6.1.4.1.311.60.2.1.3", :short_name => "jurisdictionOfIncorporationCountryName" }, # extended validation related
|
37
|
+
{ :oid => "2.5.29.37.0", :short_name => "anyExtendedKeyUsage", :long_name => "Any Extended Key Usage" } # an EKU older OpenSSL frequently lacks
|
22
38
|
])
|
data/lib/r509/asn1.rb
ADDED
@@ -0,0 +1,375 @@
|
|
1
|
+
require 'ipaddr'
|
2
|
+
|
3
|
+
module R509
|
4
|
+
# Module for holding various classes related to parsed ASN.1 objects
|
5
|
+
module ASN1
|
6
|
+
# parses the ASN.1 payload and gets the extension data out for further processing
|
7
|
+
# by the subclasses
|
8
|
+
def self.get_extension_payload(ext)
|
9
|
+
asn = OpenSSL::ASN1.decode ext
|
10
|
+
# Our extension object. Here's the structure:
|
11
|
+
# Extension ::= SEQUENCE {
|
12
|
+
# extnID OBJECT IDENTIFIER,
|
13
|
+
# critical BOOLEAN DEFAULT FALSE,
|
14
|
+
# extnValue OCTET STRING
|
15
|
+
# -- contains the DER encoding of an ASN.1 value
|
16
|
+
# -- corresponding to the extension type identified
|
17
|
+
# -- by extnID
|
18
|
+
# }
|
19
|
+
OpenSSL::ASN1.decode(asn.entries.last.value).value
|
20
|
+
end
|
21
|
+
|
22
|
+
# @param [Array] names An array of strings. Can be dNSName, iPAddress, URI, or rfc822Name.
|
23
|
+
# You can also supply a directoryName, but this must be an R509::Subject or array of arrays
|
24
|
+
# @return [R509::ASN1::GeneralNames]
|
25
|
+
def self.general_name_parser(names)
|
26
|
+
if names.nil?
|
27
|
+
return nil
|
28
|
+
end
|
29
|
+
general_names = R509::ASN1::GeneralNames.new
|
30
|
+
names.map do |domain|
|
31
|
+
if !(IPAddr.new(domain.strip) rescue nil).nil?
|
32
|
+
ip = IPAddr.new(domain.strip)
|
33
|
+
general_names.create_item(:tag => 7, :value => ip.to_s)
|
34
|
+
else
|
35
|
+
case domain
|
36
|
+
when R509::Subject, Array
|
37
|
+
subject = R509::Subject.new(domain)
|
38
|
+
general_names.create_item(:tag => 4, :value => subject)
|
39
|
+
when /:\/\// #URI
|
40
|
+
general_names.create_item(:tag => 6, :value => domain.strip)
|
41
|
+
when /@/ #rfc822Name
|
42
|
+
general_names.create_item(:tag => 1, :value => domain.strip)
|
43
|
+
else #dNSName
|
44
|
+
general_names.create_item(:tag => 2, :value => domain.strip)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
general_names
|
49
|
+
end
|
50
|
+
|
51
|
+
# This class parses ASN.1 GeneralName objects. At the moment it supports
|
52
|
+
# rfc822Name, dNSName, directoryName, uniformResourceIdentifier, and iPAddress
|
53
|
+
# GeneralName ::= CHOICE {
|
54
|
+
# otherName [0] OtherName,
|
55
|
+
# rfc822Name [1] IA5String,
|
56
|
+
# dNSName [2] IA5String,
|
57
|
+
# x400Address [3] ORAddress,
|
58
|
+
# directoryName [4] Name,
|
59
|
+
# ediPartyName [5] EDIPartyName,
|
60
|
+
# uniformResourceIdentifier [6] IA5String,
|
61
|
+
# iPAddress [7] OCTET STRING,
|
62
|
+
# registeredID [8] OBJECT IDENTIFIER }
|
63
|
+
class GeneralName
|
64
|
+
# The type, represented as a symbolized version of the GeneralName (e.g. :dNSName)
|
65
|
+
attr_reader :type
|
66
|
+
# The prefix OpenSSL needs for this type when encoding it into an extension.
|
67
|
+
attr_reader :serial_prefix
|
68
|
+
# Value of the GeneralName
|
69
|
+
attr_reader :value
|
70
|
+
# Integer tag type. See GeneralName description at the top of this class
|
71
|
+
attr_reader :tag
|
72
|
+
|
73
|
+
# @param [OpenSSL::ASN1::ASN1Data,Hash] asn ASN.1 input data. Can also pass a hash with :tag and :value keys
|
74
|
+
def initialize(asn)
|
75
|
+
if asn.kind_of?(Hash) and asn.has_key?(:tag) and asn.has_key?(:value)
|
76
|
+
# this is added via create_item
|
77
|
+
@tag = asn[:tag]
|
78
|
+
@type = R509::ASN1::GeneralName.map_tag_to_type(@tag)
|
79
|
+
@serial_prefix = R509::ASN1::GeneralName.map_tag_to_serial_prefix(@tag)
|
80
|
+
@value = asn[:value]
|
81
|
+
else
|
82
|
+
@tag = asn.tag
|
83
|
+
@type = R509::ASN1::GeneralName.map_tag_to_type(@tag)
|
84
|
+
@serial_prefix = R509::ASN1::GeneralName.map_tag_to_serial_prefix(@tag)
|
85
|
+
value = asn.value
|
86
|
+
case @tag
|
87
|
+
when 1 then @value = value
|
88
|
+
when 2 then @value = value
|
89
|
+
when 4 then @value = R509::Subject.new(value.first.to_der)
|
90
|
+
when 6 then @value = value
|
91
|
+
when 7
|
92
|
+
if value.size == 4 or value.size == 16
|
93
|
+
ip = IPAddr.new_ntoh(value)
|
94
|
+
@value = ip.to_s
|
95
|
+
elsif value.size == 8 #IPv4 with netmask
|
96
|
+
ip = IPAddr.new_ntoh(value[0,4])
|
97
|
+
netmask = IPAddr.new_ntoh(value[4,4])
|
98
|
+
@value = ip.to_s + "/" + netmask.to_s
|
99
|
+
elsif value.size == 32 #IPv6 with netmask
|
100
|
+
ip = IPAddr.new_ntoh(value[0,16])
|
101
|
+
netmask = IPAddr.new_ntoh(value[16,16])
|
102
|
+
@value = ip.to_s + "/" + netmask.to_s
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
# Maps a GeneralName type to the integer tag representation
|
109
|
+
# @param [String,Symbol] type of GeneralName
|
110
|
+
# @return [Integer] tag for the type
|
111
|
+
def self.map_type_to_tag(type)
|
112
|
+
# otherName [0] OtherName,
|
113
|
+
# rfc822Name [1] IA5String,
|
114
|
+
# dNSName [2] IA5String,
|
115
|
+
# x400Address [3] ORAddress,
|
116
|
+
# directoryName [4] Name,
|
117
|
+
# ediPartyName [5] EDIPartyName,
|
118
|
+
# uniformResourceIdentifier [6] IA5String,
|
119
|
+
# iPAddress [7] OCTET STRING,
|
120
|
+
# registeredID [8] OBJECT IDENTIFIER }
|
121
|
+
case type
|
122
|
+
when "otherName", :otherName then 0
|
123
|
+
when "rfc822Name", :rfc822Name, "email" then 1
|
124
|
+
when "dNSName", :dNSName, "DNS" then 2
|
125
|
+
when "x400Address", :x400Address then 3
|
126
|
+
when "directoryName", :directoryName, "dirName" then 4
|
127
|
+
when "ediPartyName", :ediPartyName then 5
|
128
|
+
when "uniformResourceIdentifier", :uniformResourceIdentifier, "URI" then 6
|
129
|
+
when "iPAddress", :iPAddress, "IP" then 7
|
130
|
+
when "registeredID", :registeredID then 8
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
# @param [Integer] tag
|
135
|
+
# @return [String] serial prefix
|
136
|
+
def self.map_tag_to_serial_prefix(tag)
|
137
|
+
case tag
|
138
|
+
when 1 then "email"
|
139
|
+
when 2 then "DNS"
|
140
|
+
when 4 then "dirName"
|
141
|
+
when 6 then "URI"
|
142
|
+
when 7 then "IP"
|
143
|
+
else
|
144
|
+
raise R509Error, "Unimplemented GeneralName tag: #{tag}. At this time R509 does not support GeneralName types other than rfc822Name, dNSName, uniformResourceIdentifier, iPAddress, and directoryName"
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
# @param [Integer] tag
|
149
|
+
# @return [Symbol] symbol type
|
150
|
+
def self.map_tag_to_type(tag)
|
151
|
+
case tag
|
152
|
+
when 0 then :otherName
|
153
|
+
when 1 then :rfc822Name
|
154
|
+
when 2 then :dNSName
|
155
|
+
when 3 then :x400Address
|
156
|
+
when 4 then :directoryName
|
157
|
+
when 5 then :ediPartyName
|
158
|
+
when 6 then :uniformResourceIdentifier
|
159
|
+
when 7 then :iPAddress
|
160
|
+
when 8 then :registeredID
|
161
|
+
else
|
162
|
+
raise R509Error, "Invalid tag #{tag}"
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
# @private
|
167
|
+
# required for #uniq comparisons
|
168
|
+
# @return [Boolean] equality between objects
|
169
|
+
def ==(other)
|
170
|
+
(other.class == self.class and self.type == other.type && self.value == other.value)
|
171
|
+
end
|
172
|
+
alias_method :eql?, :==
|
173
|
+
|
174
|
+
# @private
|
175
|
+
# required for #uniq comparisons
|
176
|
+
def hash
|
177
|
+
"#{self.type}#{self.tag}#{self.value}".hash
|
178
|
+
end
|
179
|
+
|
180
|
+
# Used to serialize GeneralName objects when issuing new certificates inside R509::CertificateAuthority::Signer
|
181
|
+
# @return [Hash] conf section and name serialized for OpenSSL extension creation
|
182
|
+
def serialize_name
|
183
|
+
if self.type == :directoryName
|
184
|
+
conf_name = OpenSSL::Random.random_bytes(16).unpack("H*")[0]
|
185
|
+
conf = []
|
186
|
+
conf << "[#{conf_name}]"
|
187
|
+
@value.to_a.each do |el|
|
188
|
+
conf << "#{el[0]}=#{el[1]}"
|
189
|
+
end
|
190
|
+
conf = conf.join("\n")
|
191
|
+
extension_string = self.serial_prefix + ":" + conf_name
|
192
|
+
else
|
193
|
+
conf = nil
|
194
|
+
extension_string = self.serial_prefix + ":" + self.value
|
195
|
+
end
|
196
|
+
{
|
197
|
+
:conf => conf,
|
198
|
+
:extension_string => extension_string
|
199
|
+
}
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
# object to hold parsed sequences of generalnames
|
204
|
+
# these structures are used in SubjectAlternativeName, AuthorityInfoAccess, CRLDistributionPoints, etc
|
205
|
+
class GeneralNames
|
206
|
+
def initialize
|
207
|
+
@types = {
|
208
|
+
:otherName => [], # unimplemented
|
209
|
+
:rfc822Name => [],
|
210
|
+
:dNSName => [],
|
211
|
+
:x400Address => [], # unimplemented
|
212
|
+
:directoryName => [],
|
213
|
+
:ediPartyName => [], # unimplemented
|
214
|
+
:uniformResourceIdentifier => [],
|
215
|
+
:iPAddress => [],
|
216
|
+
:registeredID => [] # unimplemented
|
217
|
+
}
|
218
|
+
@ordered_names = []
|
219
|
+
end
|
220
|
+
|
221
|
+
# @private
|
222
|
+
# @param [OpenSSL::ASN1::ASN1Data] asn Takes ASN.1 data in for parsing GeneralName structures
|
223
|
+
def add_item(asn)
|
224
|
+
# map general names into our hash of arrays
|
225
|
+
if asn.kind_of?(R509::ASN1::GeneralName)
|
226
|
+
@ordered_names << asn
|
227
|
+
@types[asn.type] << asn.value
|
228
|
+
else
|
229
|
+
gn = R509::ASN1::GeneralName.new(asn)
|
230
|
+
@ordered_names << gn
|
231
|
+
@types[gn.type] << gn.value
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
235
|
+
# @private
|
236
|
+
# @param [Hash] hash A hash with (:tag or :type) and :value keys. Allows you to build GeneralName objects and add
|
237
|
+
# them to the GeneralNames object. Unless you know what you're doing you should really stay away from this.
|
238
|
+
def create_item(hash)
|
239
|
+
if not hash.respond_to?(:has_key?) or (not hash.has_key?(:tag) and not hash.has_key?(:type)) or not hash.has_key?(:value)
|
240
|
+
raise ArgumentError, "Must be a hash with (:tag or :type) and :value nodes"
|
241
|
+
end
|
242
|
+
if hash[:type]
|
243
|
+
hash[:tag] = R509::ASN1::GeneralName.map_type_to_tag(hash[:type])
|
244
|
+
end
|
245
|
+
gn = R509::ASN1::GeneralName.new(:tag => hash[:tag], :value => hash[:value])
|
246
|
+
add_item(gn)
|
247
|
+
end
|
248
|
+
|
249
|
+
# @return [Array] array of GeneralName objects
|
250
|
+
# order found in the extension
|
251
|
+
def names
|
252
|
+
@ordered_names
|
253
|
+
end
|
254
|
+
|
255
|
+
# @return [Array] Array of rfc822name strings
|
256
|
+
def rfc_822_names
|
257
|
+
@types[:rfc822Name]
|
258
|
+
end
|
259
|
+
|
260
|
+
# @return [Array] Array of dnsName strings
|
261
|
+
def dns_names
|
262
|
+
@types[:dNSName]
|
263
|
+
end
|
264
|
+
|
265
|
+
# @return [Array] Array of uri strings
|
266
|
+
def uniform_resource_identifiers
|
267
|
+
@types[:uniformResourceIdentifier]
|
268
|
+
end
|
269
|
+
alias_method :uris, :uniform_resource_identifiers
|
270
|
+
|
271
|
+
# @return [Array] Array of IP address strings
|
272
|
+
def ip_addresses
|
273
|
+
@types[:iPAddress]
|
274
|
+
end
|
275
|
+
|
276
|
+
# @return [Array] Array of directoryNames (R509::Subject objects)
|
277
|
+
def directory_names
|
278
|
+
@types[:directoryName]
|
279
|
+
end
|
280
|
+
|
281
|
+
# @return [Array] string of serialized names for OpenSSL extension creation
|
282
|
+
def serialize_names
|
283
|
+
confs = []
|
284
|
+
extension_strings = []
|
285
|
+
@ordered_names.each { |item|
|
286
|
+
data = item.serialize_name
|
287
|
+
confs << data[:conf]
|
288
|
+
extension_strings << data[:extension_string]
|
289
|
+
}
|
290
|
+
{ :conf => confs.join("\n"), :extension_string => extension_strings.join(",") }
|
291
|
+
end
|
292
|
+
end
|
293
|
+
|
294
|
+
# PolicyInformation ::= SEQUENCE {
|
295
|
+
# policyIdentifier CertPolicyId,
|
296
|
+
# policyQualifiers SEQUENCE SIZE (1..MAX) OF
|
297
|
+
# PolicyQualifierInfo OPTIONAL }
|
298
|
+
class PolicyInformation
|
299
|
+
attr_reader :policy_identifier, :policy_qualifiers
|
300
|
+
def initialize(data)
|
301
|
+
# store the policy identifier OID
|
302
|
+
@policy_identifier = data.entries[0].value
|
303
|
+
# iterate the policy qualifiers if any exist
|
304
|
+
if not data.entries[1].nil?
|
305
|
+
@policy_qualifiers = PolicyQualifiers.new
|
306
|
+
data.entries[1].each do |pq|
|
307
|
+
@policy_qualifiers.parse(pq)
|
308
|
+
end
|
309
|
+
end
|
310
|
+
end
|
311
|
+
end
|
312
|
+
|
313
|
+
# PolicyQualifierInfo ::= SEQUENCE {
|
314
|
+
# policyQualifierId PolicyQualifierId,
|
315
|
+
# qualifier ANY DEFINED BY policyQualifierId }
|
316
|
+
class PolicyQualifiers
|
317
|
+
attr_reader :cps_uris, :user_notices
|
318
|
+
def initialize
|
319
|
+
@cps_uris = []
|
320
|
+
@user_notices = []
|
321
|
+
end
|
322
|
+
|
323
|
+
# parse each PolicyQualifier and store the results into the object array
|
324
|
+
def parse(data)
|
325
|
+
oid = data.entries[0].value
|
326
|
+
case
|
327
|
+
when oid == 'id-qt-cps'
|
328
|
+
@cps_uris << data.entries[1].value
|
329
|
+
when oid == 'id-qt-unotice'
|
330
|
+
@user_notices << UserNotice.new(data.entries[1])
|
331
|
+
end
|
332
|
+
end
|
333
|
+
end
|
334
|
+
|
335
|
+
# UserNotice ::= SEQUENCE {
|
336
|
+
# noticeRef NoticeReference OPTIONAL,
|
337
|
+
# explicitText DisplayText OPTIONAL }
|
338
|
+
class UserNotice
|
339
|
+
attr_reader :notice_reference, :explicit_text
|
340
|
+
def initialize(data)
|
341
|
+
data.each do |qualifier|
|
342
|
+
#if we find another sequence, that's a noticeReference, otherwise it's explicitText
|
343
|
+
if qualifier.kind_of?(OpenSSL::ASN1::Sequence)
|
344
|
+
@notice_reference = NoticeReference.new(qualifier)
|
345
|
+
else
|
346
|
+
@explicit_text = qualifier.value
|
347
|
+
end
|
348
|
+
|
349
|
+
end if data.respond_to?(:each)
|
350
|
+
end
|
351
|
+
end
|
352
|
+
|
353
|
+
# NoticeReference ::= SEQUENCE {
|
354
|
+
# organization DisplayText,
|
355
|
+
# noticeNumbers SEQUENCE OF INTEGER }
|
356
|
+
class NoticeReference
|
357
|
+
attr_reader :organization, :notice_numbers
|
358
|
+
def initialize(data)
|
359
|
+
data.each do |notice_reference|
|
360
|
+
# if it's displaytext then it's the organization
|
361
|
+
# if it's YET ANOTHER ASN1::Sequence, then it's noticeNumbers
|
362
|
+
if notice_reference.kind_of?(OpenSSL::ASN1::Sequence)
|
363
|
+
@notice_numbers = []
|
364
|
+
notice_reference.each do |ints|
|
365
|
+
@notice_numbers << ints.value
|
366
|
+
end
|
367
|
+
else
|
368
|
+
@organization = notice_reference.value
|
369
|
+
end
|
370
|
+
end
|
371
|
+
end
|
372
|
+
end
|
373
|
+
|
374
|
+
end
|
375
|
+
end
|