certificate_authority_sonian 0.1.7
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +9 -0
- data/Gemfile.lock +39 -0
- data/README.rdoc +243 -0
- data/Rakefile +52 -0
- data/VERSION.yml +5 -0
- data/certificate_authority.gemspec +71 -0
- data/lib/certificate_authority.rb +19 -0
- data/lib/certificate_authority/certificate.rb +215 -0
- data/lib/certificate_authority/certificate_revocation_list.rb +59 -0
- data/lib/certificate_authority/distinguished_name.rb +58 -0
- data/lib/certificate_authority/extensions.rb +266 -0
- data/lib/certificate_authority/key_material.rb +135 -0
- data/lib/certificate_authority/ocsp_handler.rb +77 -0
- data/lib/certificate_authority/pkcs11_key_material.rb +65 -0
- data/lib/certificate_authority/serial_number.rb +9 -0
- data/lib/certificate_authority/signing_entity.rb +16 -0
- data/lib/tasks/certificate_authority.rake +23 -0
- data/spec/spec_helper.rb +4 -0
- data/spec/units/certificate_authority_spec.rb +4 -0
- data/spec/units/certificate_revocation_list_spec.rb +68 -0
- data/spec/units/certificate_spec.rb +428 -0
- data/spec/units/distinguished_name_spec.rb +59 -0
- data/spec/units/extensions_spec.rb +115 -0
- data/spec/units/key_material_spec.rb +154 -0
- data/spec/units/ocsp_handler_spec.rb +104 -0
- data/spec/units/pkcs11_key_material_spec.rb +41 -0
- data/spec/units/serial_number_spec.rb +20 -0
- data/spec/units/signing_entity_spec.rb +4 -0
- data/spec/units/units_helper.rb +1 -0
- metadata +123 -0
@@ -0,0 +1,215 @@
|
|
1
|
+
module CertificateAuthority
|
2
|
+
class Certificate
|
3
|
+
# include SigningEntity
|
4
|
+
include ActiveModel::Validations
|
5
|
+
|
6
|
+
attr_accessor :distinguished_name
|
7
|
+
attr_accessor :serial_number
|
8
|
+
attr_accessor :key_material
|
9
|
+
attr_accessor :not_before
|
10
|
+
attr_accessor :not_after
|
11
|
+
attr_accessor :revoked_at
|
12
|
+
attr_accessor :extensions
|
13
|
+
attr_accessor :openssl_body
|
14
|
+
|
15
|
+
alias :subject :distinguished_name #Same thing as the DN
|
16
|
+
|
17
|
+
attr_accessor :parent
|
18
|
+
|
19
|
+
validate do |certificate|
|
20
|
+
errors.add :base, "Distinguished name must be valid" unless distinguished_name.valid?
|
21
|
+
errors.add :base, "Key material must be valid" unless key_material.valid?
|
22
|
+
errors.add :base, "Serial number must be valid" unless serial_number.valid?
|
23
|
+
errors.add :base, "Extensions must be valid" unless extensions.each do |item|
|
24
|
+
unless item.respond_to?(:valid?)
|
25
|
+
true
|
26
|
+
else
|
27
|
+
item.valid?
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def initialize
|
33
|
+
self.distinguished_name = DistinguishedName.new
|
34
|
+
self.serial_number = SerialNumber.new
|
35
|
+
self.key_material = MemoryKeyMaterial.new
|
36
|
+
self.not_before = Time.now
|
37
|
+
self.not_after = Time.now + 60 * 60 * 24 * 365 #One year
|
38
|
+
self.parent = self
|
39
|
+
self.extensions = load_extensions()
|
40
|
+
|
41
|
+
self.signing_entity = false
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
def sign!(signing_profile={})
|
46
|
+
raise "Invalid certificate #{self.errors.full_messages}" unless valid?
|
47
|
+
merge_profile_with_extensions(signing_profile)
|
48
|
+
|
49
|
+
openssl_cert = OpenSSL::X509::Certificate.new
|
50
|
+
openssl_cert.version = 2
|
51
|
+
openssl_cert.not_before = self.not_before
|
52
|
+
openssl_cert.not_after = self.not_after
|
53
|
+
openssl_cert.public_key = self.key_material.public_key
|
54
|
+
|
55
|
+
openssl_cert.serial = self.serial_number.number
|
56
|
+
|
57
|
+
openssl_cert.subject = self.distinguished_name.to_x509_name
|
58
|
+
openssl_cert.issuer = parent.distinguished_name.to_x509_name
|
59
|
+
|
60
|
+
require 'tempfile'
|
61
|
+
t = Tempfile.new("bullshit_conf")
|
62
|
+
# t = File.new("/tmp/openssl.cnf")
|
63
|
+
## The config requires a file even though we won't use it
|
64
|
+
openssl_config = OpenSSL::Config.new(t.path)
|
65
|
+
|
66
|
+
factory = OpenSSL::X509::ExtensionFactory.new
|
67
|
+
factory.subject_certificate = openssl_cert
|
68
|
+
|
69
|
+
#NB: If the parent doesn't have an SSL body we're making this a self-signed cert
|
70
|
+
if parent.openssl_body.nil?
|
71
|
+
factory.issuer_certificate = openssl_cert
|
72
|
+
else
|
73
|
+
factory.issuer_certificate = parent.openssl_body
|
74
|
+
end
|
75
|
+
|
76
|
+
self.extensions.keys.each do |k|
|
77
|
+
config_extensions = extensions[k].config_extensions
|
78
|
+
openssl_config = merge_options(openssl_config,config_extensions)
|
79
|
+
end
|
80
|
+
|
81
|
+
# p openssl_config.sections
|
82
|
+
|
83
|
+
factory.config = openssl_config
|
84
|
+
|
85
|
+
# Order matters: e.g. for self-signed, subjectKeyIdentifier must come before authorityKeyIdentifier
|
86
|
+
self.extensions.keys.sort{|a,b| b<=>a}.each do |k|
|
87
|
+
e = extensions[k]
|
88
|
+
next if e.to_s.nil? or e.to_s == "" ## If the extension returns an empty string we won't include it
|
89
|
+
ext = factory.create_ext(e.openssl_identifier, e.to_s)
|
90
|
+
openssl_cert.add_extension(ext)
|
91
|
+
end
|
92
|
+
|
93
|
+
if signing_profile["digest"].nil?
|
94
|
+
digest = OpenSSL::Digest::Digest.new("SHA512")
|
95
|
+
else
|
96
|
+
digest = OpenSSL::Digest::Digest.new(signing_profile["digest"])
|
97
|
+
end
|
98
|
+
self.openssl_body = openssl_cert.sign(parent.key_material.private_key,digest)
|
99
|
+
t.close! if t.is_a?(Tempfile)# We can get rid of the ridiculous temp file
|
100
|
+
self.openssl_body
|
101
|
+
end
|
102
|
+
|
103
|
+
def is_signing_entity?
|
104
|
+
self.extensions["basicConstraints"].ca
|
105
|
+
end
|
106
|
+
|
107
|
+
def signing_entity=(signing)
|
108
|
+
self.extensions["basicConstraints"].ca = signing
|
109
|
+
end
|
110
|
+
|
111
|
+
def revoked?
|
112
|
+
!self.revoked_at.nil?
|
113
|
+
end
|
114
|
+
|
115
|
+
def to_pem
|
116
|
+
raise "Certificate has no signed body" if self.openssl_body.nil?
|
117
|
+
self.openssl_body.to_pem
|
118
|
+
end
|
119
|
+
|
120
|
+
def is_root_entity?
|
121
|
+
self.parent == self && is_signing_entity?
|
122
|
+
end
|
123
|
+
|
124
|
+
def is_intermediate_entity?
|
125
|
+
(self.parent != self) && is_signing_entity?
|
126
|
+
end
|
127
|
+
|
128
|
+
private
|
129
|
+
|
130
|
+
def merge_profile_with_extensions(signing_profile={})
|
131
|
+
return self.extensions if signing_profile["extensions"].nil?
|
132
|
+
signing_config = signing_profile["extensions"]
|
133
|
+
signing_config.keys.each do |k|
|
134
|
+
extension = self.extensions[k]
|
135
|
+
items = signing_config[k]
|
136
|
+
items.keys.each do |profile_item_key|
|
137
|
+
if extension.respond_to?("#{profile_item_key}=".to_sym)
|
138
|
+
extension.send("#{profile_item_key}=".to_sym, items[profile_item_key] )
|
139
|
+
else
|
140
|
+
p "Tried applying '#{profile_item_key}' to #{extension.class} but it doesn't respond!"
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
def load_extensions
|
147
|
+
extension_hash = {}
|
148
|
+
|
149
|
+
temp_extensions = []
|
150
|
+
basic_constraints = CertificateAuthority::Extensions::BasicContraints.new
|
151
|
+
temp_extensions << basic_constraints
|
152
|
+
crl_distribution_points = CertificateAuthority::Extensions::CrlDistributionPoints.new
|
153
|
+
temp_extensions << crl_distribution_points
|
154
|
+
subject_key_identifier = CertificateAuthority::Extensions::SubjectKeyIdentifier.new
|
155
|
+
temp_extensions << subject_key_identifier
|
156
|
+
authority_key_identifier = CertificateAuthority::Extensions::AuthorityKeyIdentifier.new
|
157
|
+
temp_extensions << authority_key_identifier
|
158
|
+
authority_info_access = CertificateAuthority::Extensions::AuthorityInfoAccess.new
|
159
|
+
temp_extensions << authority_info_access
|
160
|
+
key_usage = CertificateAuthority::Extensions::KeyUsage.new
|
161
|
+
temp_extensions << key_usage
|
162
|
+
extended_key_usage = CertificateAuthority::Extensions::ExtendedKeyUsage.new
|
163
|
+
temp_extensions << extended_key_usage
|
164
|
+
subject_alternative_name = CertificateAuthority::Extensions::SubjectAlternativeName.new
|
165
|
+
temp_extensions << subject_alternative_name
|
166
|
+
certificate_policies = CertificateAuthority::Extensions::CertificatePolicies.new
|
167
|
+
temp_extensions << certificate_policies
|
168
|
+
|
169
|
+
temp_extensions.each do |extension|
|
170
|
+
extension_hash[extension.openssl_identifier] = extension
|
171
|
+
end
|
172
|
+
|
173
|
+
extension_hash
|
174
|
+
end
|
175
|
+
|
176
|
+
def merge_options(config,hash)
|
177
|
+
hash.keys.each do |k|
|
178
|
+
config[k] = hash[k]
|
179
|
+
end
|
180
|
+
config
|
181
|
+
end
|
182
|
+
|
183
|
+
def self.from_pkcs12_file pkcs12_file, passphrase = nil
|
184
|
+
raise "#{pksc12_file} does not exist" unless File.exists?(pkcs12_file)
|
185
|
+
from_pkcs12 OpenSSL::PKCS12.new(File.read(pkcs12_file), passphrase)
|
186
|
+
end
|
187
|
+
|
188
|
+
def self.from_pkcs12 openssl_pkcs12
|
189
|
+
unless openssl_pkcs12.is_a? OpenSSL::PKCS12
|
190
|
+
raise "Can only construct from an OpenSSL::PKCS12"
|
191
|
+
end
|
192
|
+
certificate = from_openssl openssl_pkcs12.certificate
|
193
|
+
certificate.key_material.private_key = openssl_pkcs12.key
|
194
|
+
certificate
|
195
|
+
end
|
196
|
+
|
197
|
+
def self.from_openssl openssl_cert
|
198
|
+
unless openssl_cert.is_a? OpenSSL::X509::Certificate
|
199
|
+
raise "Can only construct from an OpenSSL::X509::Certificate"
|
200
|
+
end
|
201
|
+
|
202
|
+
certificate = Certificate.new
|
203
|
+
# Only subject, key_material, and body are used for signing
|
204
|
+
certificate.distinguished_name = DistinguishedName.from_openssl openssl_cert.subject
|
205
|
+
certificate.key_material.public_key = openssl_cert.public_key
|
206
|
+
certificate.openssl_body = openssl_cert
|
207
|
+
certificate.serial_number.number = openssl_cert.serial.to_i
|
208
|
+
certificate.not_before = openssl_cert.not_before
|
209
|
+
certificate.not_after = openssl_cert.not_after
|
210
|
+
# TODO extensions
|
211
|
+
certificate
|
212
|
+
end
|
213
|
+
|
214
|
+
end
|
215
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module CertificateAuthority
|
2
|
+
class CertificateRevocationList
|
3
|
+
include ActiveModel::Validations
|
4
|
+
|
5
|
+
attr_accessor :certificates
|
6
|
+
attr_accessor :parent
|
7
|
+
attr_accessor :crl_body
|
8
|
+
attr_accessor :next_update
|
9
|
+
|
10
|
+
validate do |crl|
|
11
|
+
errors.add :next_update, "Next update must be a positive value" if crl.next_update < 0
|
12
|
+
errors.add :parent, "A parent entity must be set" if crl.parent.nil?
|
13
|
+
end
|
14
|
+
|
15
|
+
def initialize
|
16
|
+
self.certificates = []
|
17
|
+
self.next_update = 60 * 60 * 4 # 4 hour default
|
18
|
+
end
|
19
|
+
|
20
|
+
def <<(cert)
|
21
|
+
raise "Only revoked certificates can be added to a CRL" unless cert.revoked?
|
22
|
+
self.certificates << cert
|
23
|
+
end
|
24
|
+
|
25
|
+
def sign!
|
26
|
+
raise "No parent entity has been set!" if self.parent.nil?
|
27
|
+
raise "Invalid CRL" unless self.valid?
|
28
|
+
|
29
|
+
revocations = self.certificates.collect do |certificate|
|
30
|
+
revocation = OpenSSL::X509::Revoked.new
|
31
|
+
x509_cert = OpenSSL::X509::Certificate.new(certificate.to_pem)
|
32
|
+
revocation.serial = x509_cert.serial
|
33
|
+
revocation.time = certificate.revoked_at
|
34
|
+
revocation
|
35
|
+
end
|
36
|
+
|
37
|
+
crl = OpenSSL::X509::CRL.new
|
38
|
+
revocations.each do |revocation|
|
39
|
+
crl.add_revoked(revocation)
|
40
|
+
end
|
41
|
+
|
42
|
+
crl.version = 1
|
43
|
+
crl.last_update = Time.now
|
44
|
+
crl.next_update = Time.now + self.next_update
|
45
|
+
|
46
|
+
signing_cert = OpenSSL::X509::Certificate.new(self.parent.to_pem)
|
47
|
+
digest = OpenSSL::Digest::Digest.new("SHA512")
|
48
|
+
crl.issuer = signing_cert.subject
|
49
|
+
self.crl_body = crl.sign(self.parent.key_material.private_key, digest)
|
50
|
+
|
51
|
+
self.crl_body
|
52
|
+
end
|
53
|
+
|
54
|
+
def to_pem
|
55
|
+
raise "No signed CRL body" if self.crl_body.nil?
|
56
|
+
self.crl_body.to_pem
|
57
|
+
end
|
58
|
+
end#CertificateRevocationList
|
59
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module CertificateAuthority
|
2
|
+
class DistinguishedName
|
3
|
+
include ActiveModel::Validations
|
4
|
+
|
5
|
+
validates_presence_of :common_name
|
6
|
+
|
7
|
+
attr_accessor :common_name
|
8
|
+
alias :cn :common_name
|
9
|
+
|
10
|
+
attr_accessor :locality
|
11
|
+
alias :l :locality
|
12
|
+
|
13
|
+
attr_accessor :state
|
14
|
+
alias :s :state
|
15
|
+
|
16
|
+
attr_accessor :country
|
17
|
+
alias :c :country
|
18
|
+
|
19
|
+
attr_accessor :organization
|
20
|
+
alias :o :organization
|
21
|
+
|
22
|
+
attr_accessor :organizational_unit
|
23
|
+
alias :ou :organizational_unit
|
24
|
+
|
25
|
+
def to_x509_name
|
26
|
+
raise "Invalid Distinguished Name" unless valid?
|
27
|
+
|
28
|
+
# NB: the capitalization in the strings counts
|
29
|
+
name = OpenSSL::X509::Name.new
|
30
|
+
name.add_entry("CN", common_name)
|
31
|
+
name.add_entry("O", organization) unless organization.blank?
|
32
|
+
name.add_entry("OU", organizational_unit) unless organizational_unit.blank?
|
33
|
+
name.add_entry("ST", state) unless state.blank?
|
34
|
+
name.add_entry("L", locality) unless locality.blank?
|
35
|
+
name.add_entry("C", country) unless country.blank?
|
36
|
+
name
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.from_openssl openssl_name
|
40
|
+
unless openssl_name.is_a? OpenSSL::X509::Name
|
41
|
+
raise "Argument must be a OpenSSL::X509::Name"
|
42
|
+
end
|
43
|
+
|
44
|
+
name = DistinguishedName.new
|
45
|
+
openssl_name.to_a.each do |k,v|
|
46
|
+
case k
|
47
|
+
when "CN" then name.common_name = v
|
48
|
+
when "L" then name.locality = v
|
49
|
+
when "ST" then name.state = v
|
50
|
+
when "C" then name.country = v
|
51
|
+
when "O" then name.organization = v
|
52
|
+
when "OU" then name.organizational_unit = v
|
53
|
+
end
|
54
|
+
end
|
55
|
+
name
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,266 @@
|
|
1
|
+
module CertificateAuthority
|
2
|
+
module Extensions
|
3
|
+
module ExtensionAPI
|
4
|
+
def to_s
|
5
|
+
raise "Implementation required"
|
6
|
+
end
|
7
|
+
|
8
|
+
def config_extensions
|
9
|
+
{}
|
10
|
+
end
|
11
|
+
|
12
|
+
def openssl_identifier
|
13
|
+
raise "Implementation required"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
class BasicContraints
|
18
|
+
include ExtensionAPI
|
19
|
+
include ActiveModel::Validations
|
20
|
+
attr_accessor :ca
|
21
|
+
attr_accessor :path_len
|
22
|
+
validates :ca, :inclusion => [true,false]
|
23
|
+
|
24
|
+
def initialize
|
25
|
+
self.ca = false
|
26
|
+
end
|
27
|
+
|
28
|
+
def is_ca?
|
29
|
+
self.ca
|
30
|
+
end
|
31
|
+
|
32
|
+
def path_len=(value)
|
33
|
+
raise "path_len must be a non-negative integer" if value < 0 or !value.is_a?(Fixnum)
|
34
|
+
@path_len = value
|
35
|
+
end
|
36
|
+
|
37
|
+
def openssl_identifier
|
38
|
+
"basicConstraints"
|
39
|
+
end
|
40
|
+
|
41
|
+
def to_s
|
42
|
+
result = ""
|
43
|
+
result += "CA:#{self.ca}"
|
44
|
+
result += ",pathlen:#{self.path_len}" unless self.path_len.nil?
|
45
|
+
result
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
class CrlDistributionPoints
|
50
|
+
include ExtensionAPI
|
51
|
+
|
52
|
+
attr_accessor :uri
|
53
|
+
|
54
|
+
def initialize
|
55
|
+
# self.uri = "http://moo.crlendPoint.example.com/something.crl"
|
56
|
+
end
|
57
|
+
|
58
|
+
def openssl_identifier
|
59
|
+
"crlDistributionPoints"
|
60
|
+
end
|
61
|
+
|
62
|
+
## NB: At this time it seems OpenSSL's extension handlers don't support
|
63
|
+
## any of the config options the docs claim to support... everything comes back
|
64
|
+
## "missing value" on GENERAL NAME. Even if copied verbatim
|
65
|
+
def config_extensions
|
66
|
+
{
|
67
|
+
# "custom_crl_fields" => {"fullname" => "URI:#{fullname}"},
|
68
|
+
# "issuer_sect" => {"CN" => "crlissuer.com", "C" => "US", "O" => "shudder"}
|
69
|
+
}
|
70
|
+
end
|
71
|
+
|
72
|
+
def to_s
|
73
|
+
return "" if self.uri.nil?
|
74
|
+
"URI:#{self.uri}"
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
class SubjectKeyIdentifier
|
79
|
+
include ExtensionAPI
|
80
|
+
def openssl_identifier
|
81
|
+
"subjectKeyIdentifier"
|
82
|
+
end
|
83
|
+
|
84
|
+
def to_s
|
85
|
+
"hash"
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
class AuthorityKeyIdentifier
|
90
|
+
include ExtensionAPI
|
91
|
+
|
92
|
+
def openssl_identifier
|
93
|
+
"authorityKeyIdentifier"
|
94
|
+
end
|
95
|
+
|
96
|
+
def to_s
|
97
|
+
"keyid,issuer"
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
class AuthorityInfoAccess
|
102
|
+
include ExtensionAPI
|
103
|
+
|
104
|
+
attr_accessor :ocsp
|
105
|
+
|
106
|
+
def initialize
|
107
|
+
self.ocsp = []
|
108
|
+
end
|
109
|
+
|
110
|
+
def openssl_identifier
|
111
|
+
"authorityInfoAccess"
|
112
|
+
end
|
113
|
+
|
114
|
+
def to_s
|
115
|
+
return "" if self.ocsp.empty?
|
116
|
+
"OCSP;URI:#{self.ocsp}"
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
class KeyUsage
|
121
|
+
include ExtensionAPI
|
122
|
+
|
123
|
+
attr_accessor :usage
|
124
|
+
|
125
|
+
def initialize
|
126
|
+
self.usage = ["digitalSignature", "nonRepudiation"]
|
127
|
+
end
|
128
|
+
|
129
|
+
def openssl_identifier
|
130
|
+
"keyUsage"
|
131
|
+
end
|
132
|
+
|
133
|
+
def to_s
|
134
|
+
"#{self.usage.join(',')}"
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
class ExtendedKeyUsage
|
139
|
+
include ExtensionAPI
|
140
|
+
|
141
|
+
attr_accessor :usage
|
142
|
+
|
143
|
+
def initialize
|
144
|
+
self.usage = ["serverAuth","clientAuth"]
|
145
|
+
end
|
146
|
+
|
147
|
+
def openssl_identifier
|
148
|
+
"extendedKeyUsage"
|
149
|
+
end
|
150
|
+
|
151
|
+
def to_s
|
152
|
+
"#{self.usage.join(',')}"
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
class SubjectAlternativeName
|
157
|
+
include ExtensionAPI
|
158
|
+
|
159
|
+
attr_accessor :uris, :dns_names, :ips
|
160
|
+
|
161
|
+
def initialize
|
162
|
+
self.uris = []
|
163
|
+
self.dns_names = []
|
164
|
+
self.ips = []
|
165
|
+
end
|
166
|
+
|
167
|
+
def uris=(value)
|
168
|
+
raise "URIs must be an array" unless value.is_a?(Array)
|
169
|
+
@uris = value
|
170
|
+
end
|
171
|
+
|
172
|
+
def dns_names=(value)
|
173
|
+
raise "DNS names must be an array" unless value.is_a?(Array)
|
174
|
+
@dns_names = value
|
175
|
+
end
|
176
|
+
|
177
|
+
def ips=(value)
|
178
|
+
raise "IPs must be an array" unless value.is_a?(Array)
|
179
|
+
@ips = value
|
180
|
+
end
|
181
|
+
|
182
|
+
def openssl_identifier
|
183
|
+
"subjectAltName"
|
184
|
+
end
|
185
|
+
|
186
|
+
def to_s
|
187
|
+
res = self.uris.map {|u| "URI:#{u}" }
|
188
|
+
res += self.dns_names.map {|d| "DNS:#{d}" }
|
189
|
+
res += self.ips.map {|i| "IP:#{i}" }
|
190
|
+
|
191
|
+
return res.join(',')
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
class CertificatePolicies
|
196
|
+
include ExtensionAPI
|
197
|
+
|
198
|
+
attr_accessor :policy_identifier
|
199
|
+
attr_accessor :cps_uris
|
200
|
+
##User notice
|
201
|
+
attr_accessor :explicit_text
|
202
|
+
attr_accessor :organization
|
203
|
+
attr_accessor :notice_numbers
|
204
|
+
|
205
|
+
def initialize
|
206
|
+
@contains_data = false
|
207
|
+
end
|
208
|
+
|
209
|
+
|
210
|
+
def openssl_identifier
|
211
|
+
"certificatePolicies"
|
212
|
+
end
|
213
|
+
|
214
|
+
def user_notice=(value={})
|
215
|
+
value.keys.each do |key|
|
216
|
+
self.send("#{key}=".to_sym, value[key])
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
def config_extensions
|
221
|
+
config_extension = {}
|
222
|
+
custom_policies = {}
|
223
|
+
notice = {}
|
224
|
+
unless self.policy_identifier.nil?
|
225
|
+
custom_policies["policyIdentifier"] = self.policy_identifier
|
226
|
+
end
|
227
|
+
|
228
|
+
if !self.cps_uris.nil? and self.cps_uris.is_a?(Array)
|
229
|
+
self.cps_uris.each_with_index do |cps_uri,i|
|
230
|
+
custom_policies["CPS.#{i}"] = cps_uri
|
231
|
+
end
|
232
|
+
end
|
233
|
+
|
234
|
+
unless self.explicit_text.nil?
|
235
|
+
notice["explicitText"] = self.explicit_text
|
236
|
+
end
|
237
|
+
|
238
|
+
unless self.organization.nil?
|
239
|
+
notice["organization"] = self.organization
|
240
|
+
end
|
241
|
+
|
242
|
+
unless self.notice_numbers.nil?
|
243
|
+
notice["noticeNumbers"] = self.notice_numbers
|
244
|
+
end
|
245
|
+
|
246
|
+
if notice.keys.size > 0
|
247
|
+
custom_policies["userNotice.1"] = "@notice"
|
248
|
+
config_extension["notice"] = notice
|
249
|
+
end
|
250
|
+
|
251
|
+
if custom_policies.keys.size > 0
|
252
|
+
config_extension["custom_policies"] = custom_policies
|
253
|
+
@contains_data = true
|
254
|
+
end
|
255
|
+
|
256
|
+
config_extension
|
257
|
+
end
|
258
|
+
|
259
|
+
def to_s
|
260
|
+
return "" unless @contains_data
|
261
|
+
"ia5org,@custom_policies"
|
262
|
+
end
|
263
|
+
end
|
264
|
+
|
265
|
+
end
|
266
|
+
end
|