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
@@ -1,290 +0,0 @@
|
|
1
|
-
require 'openssl'
|
2
|
-
require 'r509/config'
|
3
|
-
require 'r509/cert'
|
4
|
-
require 'r509/exceptions'
|
5
|
-
|
6
|
-
# CertificateAuthority related classes
|
7
|
-
module R509::CertificateAuthority
|
8
|
-
# Contains the certification authority signing operation methods
|
9
|
-
class Signer
|
10
|
-
# @param [R509::Config] config
|
11
|
-
def initialize(config=nil)
|
12
|
-
@config = config
|
13
|
-
|
14
|
-
if not @config.nil? and not @config.kind_of?(R509::Config::CaConfig)
|
15
|
-
raise R509::R509Error, "config must be a kind of R509::Config::CaConfig or nil (for self-sign only)"
|
16
|
-
end
|
17
|
-
if not @config.nil? and not @config.ca_cert.has_private_key?
|
18
|
-
raise R509::R509Error, "You must have a private key associated with your CA certificate to issue"
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
# Signs a CSR
|
23
|
-
# @option options :csr [R509::Csr]
|
24
|
-
# @option options :spki [R509::Spki]
|
25
|
-
# @option options :profile_name [String] The CA profile you want to use (eg "server in your config)
|
26
|
-
# @option options :data_hash [Hash] a hash containing the subject and SAN names you want encoded for this cert. Generate by calling Csr#to_hash or Spki#to_hash
|
27
|
-
# @option options :message_digest [String] the message digest to use for this certificate instead of the config's default
|
28
|
-
# @option options :serial [String] the serial number you want to issue the certificate with
|
29
|
-
# @option options :not_before [Time] the notBefore for the certificate
|
30
|
-
# @option options :not_after [Time] the notAfter for the certificate
|
31
|
-
# @return [R509::Cert] the signed cert object
|
32
|
-
def sign(options)
|
33
|
-
if @config.nil?
|
34
|
-
raise R509::R509Error, "When instantiating the signer without a config you can only call #selfsign"
|
35
|
-
elsif @config.num_profiles == 0
|
36
|
-
raise R509::R509Error, "You must have at least one CaProfile on your CaConfig to issue"
|
37
|
-
end
|
38
|
-
|
39
|
-
if options.has_key?(:csr) and options.has_key?(:spki)
|
40
|
-
raise ArgumentError, "You can't pass both :csr and :spki"
|
41
|
-
elsif not options.has_key?(:csr) and not options.has_key?(:spki)
|
42
|
-
raise ArgumentError, "You must supply either :csr or :spki"
|
43
|
-
elsif options.has_key?(:csr)
|
44
|
-
if not options[:csr].kind_of?(R509::Csr)
|
45
|
-
raise ArgumentError, "You must pass an R509::Csr object for :csr"
|
46
|
-
else
|
47
|
-
signable_object = options[:csr]
|
48
|
-
end
|
49
|
-
elsif not options.has_key?(:csr) and options.has_key?(:spki)
|
50
|
-
if not options[:spki].kind_of?(R509::Spki)
|
51
|
-
raise ArgumentError, "You must pass an R509::Spki object for :spki"
|
52
|
-
else
|
53
|
-
signable_object = options[:spki]
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
|
-
if options.has_key?(:data_hash)
|
58
|
-
san_names = options[:data_hash][:san_names]
|
59
|
-
subject = options[:data_hash][:subject]
|
60
|
-
else
|
61
|
-
san_names = signable_object.to_hash[:san_names]
|
62
|
-
subject = signable_object.to_hash[:subject]
|
63
|
-
end
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
if options.has_key?(:csr) and not options[:csr].verify_signature
|
68
|
-
raise R509::R509Error, "Certificate request signature is invalid."
|
69
|
-
end
|
70
|
-
|
71
|
-
#handle DSA here
|
72
|
-
if options.has_key?(:message_digest)
|
73
|
-
message_digest = R509::MessageDigest.new(options[:message_digest])
|
74
|
-
else
|
75
|
-
message_digest = R509::MessageDigest.new(@config.message_digest)
|
76
|
-
end
|
77
|
-
|
78
|
-
profile = @config.profile(options[:profile_name])
|
79
|
-
|
80
|
-
validated_subject = validate_subject(subject,profile)
|
81
|
-
|
82
|
-
cert = build_cert(
|
83
|
-
:subject => validated_subject.name,
|
84
|
-
:issuer => @config.ca_cert.subject,
|
85
|
-
:not_before => options[:not_before],
|
86
|
-
:not_after => options[:not_after],
|
87
|
-
:public_key => signable_object.public_key,
|
88
|
-
:serial => options[:serial]
|
89
|
-
)
|
90
|
-
|
91
|
-
basic_constraints = profile.basic_constraints
|
92
|
-
key_usage = profile.key_usage
|
93
|
-
extended_key_usage = profile.extended_key_usage
|
94
|
-
certificate_policies = profile.certificate_policies
|
95
|
-
|
96
|
-
build_extensions(
|
97
|
-
:subject_certificate => cert,
|
98
|
-
:issuer_certificate => @config.ca_cert.cert,
|
99
|
-
:basic_constraints => basic_constraints,
|
100
|
-
:key_usage => key_usage,
|
101
|
-
:extended_key_usage => extended_key_usage,
|
102
|
-
:certificate_policies => certificate_policies,
|
103
|
-
:san_names => san_names
|
104
|
-
)
|
105
|
-
|
106
|
-
|
107
|
-
#@config.ca_cert.key.key ... ugly. ca_cert returns R509::Cert
|
108
|
-
# #key returns R509::PrivateKey and #key on that returns OpenSSL object we need
|
109
|
-
cert.sign( @config.ca_cert.key.key, message_digest.digest )
|
110
|
-
R509::Cert.new(:cert => cert)
|
111
|
-
end
|
112
|
-
|
113
|
-
# Self-signs a CSR
|
114
|
-
# @option options :csr [R509::Csr]
|
115
|
-
# @option options :message_digest [String] the message digest to use for this certificate (defaults to sha1)
|
116
|
-
# @option options :serial [String] the serial number you want to issue the certificate with (defaults to random)
|
117
|
-
# @option options :not_before [Time] the notBefore for the certificate (defaults to now)
|
118
|
-
# @option options :not_after [Time] the notAfter for the certificate (defaults to 1 year)
|
119
|
-
# @option options :san_names [Array] Optional array of subject alternative names
|
120
|
-
# @return [R509::Cert] the signed cert object
|
121
|
-
def selfsign(options)
|
122
|
-
if not options.kind_of?(Hash)
|
123
|
-
raise ArgumentError, "You must pass a hash of options consisting of at minimum :csr"
|
124
|
-
end
|
125
|
-
csr = options[:csr]
|
126
|
-
if csr.key.nil?
|
127
|
-
raise ArgumentError, 'CSR must also have a private key to self sign'
|
128
|
-
end
|
129
|
-
cert = build_cert(
|
130
|
-
:subject => csr.subject.name,
|
131
|
-
:issuer => csr.subject.name,
|
132
|
-
:not_before => options[:not_before],
|
133
|
-
:not_after => options[:not_after],
|
134
|
-
:public_key => csr.public_key,
|
135
|
-
:serial => options[:serial]
|
136
|
-
)
|
137
|
-
|
138
|
-
if options.has_key?(:san_names)
|
139
|
-
san_names = options[:san_names]
|
140
|
-
else
|
141
|
-
san_names = csr.san_names
|
142
|
-
end
|
143
|
-
|
144
|
-
build_extensions(
|
145
|
-
:subject_certificate => cert,
|
146
|
-
:issuer_certificate => cert,
|
147
|
-
:basic_constraints => "CA:TRUE",
|
148
|
-
:san_names => san_names
|
149
|
-
)
|
150
|
-
|
151
|
-
|
152
|
-
if options.has_key?(:message_digest)
|
153
|
-
message_digest = R509::MessageDigest.new(options[:message_digest])
|
154
|
-
else
|
155
|
-
message_digest = R509::MessageDigest.new('sha1')
|
156
|
-
end
|
157
|
-
|
158
|
-
# Csr#key returns R509::PrivateKey and #key on that returns OpenSSL object we need
|
159
|
-
cert.sign( csr.key.key, message_digest.digest )
|
160
|
-
R509::Cert.new(:cert => cert)
|
161
|
-
end
|
162
|
-
|
163
|
-
private
|
164
|
-
|
165
|
-
def process_san_names(domains)
|
166
|
-
domains.map { |domain| 'DNS: '+domain }.join(",")
|
167
|
-
end
|
168
|
-
|
169
|
-
def build_conf(section,data)
|
170
|
-
conf = ["[#{section}]"]
|
171
|
-
conf.concat data
|
172
|
-
conf.join "\n"
|
173
|
-
end
|
174
|
-
|
175
|
-
def validate_subject(subject,profile)
|
176
|
-
if profile.subject_item_policy.nil? then
|
177
|
-
subject
|
178
|
-
else
|
179
|
-
profile.subject_item_policy.validate_subject(subject)
|
180
|
-
end
|
181
|
-
end
|
182
|
-
|
183
|
-
def build_cert(options)
|
184
|
-
|
185
|
-
cert = OpenSSL::X509::Certificate.new
|
186
|
-
|
187
|
-
cert.subject = options[:subject]
|
188
|
-
cert.issuer = options[:issuer]
|
189
|
-
cert.not_before = calculate_not_before(options[:not_before])
|
190
|
-
cert.not_after = calculate_not_after(options[:not_after],cert.not_before)
|
191
|
-
cert.public_key = options[:public_key]
|
192
|
-
cert.serial = create_serial(options[:serial])
|
193
|
-
cert.version = 2 #2 means v3
|
194
|
-
cert
|
195
|
-
end
|
196
|
-
|
197
|
-
def create_serial(serial)
|
198
|
-
if not serial.nil?
|
199
|
-
serial = OpenSSL::BN.new(serial.to_s)
|
200
|
-
else
|
201
|
-
# generate random serial in accordance with best practices
|
202
|
-
# guidelines state 20-bits of entropy, but we can cram more in
|
203
|
-
# per rfc5280 conforming CAs can make the serial field up to 20 octets
|
204
|
-
# to prevent even the incredibly remote possibility of collision we'll
|
205
|
-
# concatenate current time (to the microsecond) with a random num
|
206
|
-
rand = OpenSSL::BN.rand(96,0) # 96 bits is 12 bytes (octets).
|
207
|
-
serial = OpenSSL::BN.new((Time.now.to_f*1000000).to_i.to_s + rand.to_s)
|
208
|
-
# since second param is 0 the most significant bit must always be 1
|
209
|
-
# this theoretically gives us 95 bits of entropy + microtime, which
|
210
|
-
# adds a non-zero quantity of entropy. depending upon how predictable
|
211
|
-
# your issuance is, this could range from a reasonably large quantity
|
212
|
-
# of entropy to very little
|
213
|
-
end
|
214
|
-
serial
|
215
|
-
end
|
216
|
-
|
217
|
-
def build_extensions(options)
|
218
|
-
ef = OpenSSL::X509::ExtensionFactory.new
|
219
|
-
|
220
|
-
ef.subject_certificate = options[:subject_certificate]
|
221
|
-
|
222
|
-
ef.issuer_certificate = options[:issuer_certificate]
|
223
|
-
|
224
|
-
ext = []
|
225
|
-
if not options[:basic_constraints].nil?
|
226
|
-
ext << ef.create_extension("basicConstraints", options[:basic_constraints], true)
|
227
|
-
end
|
228
|
-
if options.has_key?(:key_usage) and not options[:key_usage].empty?
|
229
|
-
ext << ef.create_extension("keyUsage", options[:key_usage].join(","))
|
230
|
-
end
|
231
|
-
if options.has_key?(:extended_key_usage) and not options[:extended_key_usage].empty?
|
232
|
-
ext << ef.create_extension("extendedKeyUsage", options[:extended_key_usage].join(","))
|
233
|
-
end
|
234
|
-
ext << ef.create_extension("subjectKeyIdentifier", "hash")
|
235
|
-
|
236
|
-
#attach the key identifier if it's not a self-sign
|
237
|
-
if not ef.subject_certificate == ef.issuer_certificate and R509::Cert.new(:cert=>options[:issuer_certificate]).extensions['subjectKeyIdentifier']
|
238
|
-
ext << ef.create_extension("authorityKeyIdentifier", "keyid:always,issuer:always")
|
239
|
-
end
|
240
|
-
|
241
|
-
if not options[:certificate_policies].nil? and not options[:certificate_policies].empty?
|
242
|
-
conf = []
|
243
|
-
conf_names = []
|
244
|
-
i = 0
|
245
|
-
options[:certificate_policies].each do |policy|
|
246
|
-
conf << build_conf("certPolicies#{i}",policy)
|
247
|
-
conf_names << "@certPolicies#{i}"
|
248
|
-
i+=1
|
249
|
-
end
|
250
|
-
ef.config = OpenSSL::Config.parse(conf.join("\n"))
|
251
|
-
ext << ef.create_extension("certificatePolicies", conf_names.join(","))
|
252
|
-
end
|
253
|
-
#ef.config = OpenSSL::Config.parse(<<-_end_of_cnf_)
|
254
|
-
#[certPolicies]
|
255
|
-
#CPS.1 = http://www.example.com/cps
|
256
|
-
#_end_of_cnf_
|
257
|
-
|
258
|
-
if options.has_key?(:san_names) and not options[:san_names].empty?
|
259
|
-
ext << ef.create_extension("subjectAltName", process_san_names(options[:san_names]))
|
260
|
-
end
|
261
|
-
|
262
|
-
if not @config.nil? and not @config.cdp_location.nil?
|
263
|
-
ext << ef.create_extension("crlDistributionPoints", @config.cdp_location)
|
264
|
-
end
|
265
|
-
|
266
|
-
if not @config.nil? and not @config.ocsp_location.nil? then
|
267
|
-
ext << ef.create_extension("authorityInfoAccess",
|
268
|
-
"OCSP;" << @config.ocsp_location)
|
269
|
-
end
|
270
|
-
options[:subject_certificate].extensions = ext
|
271
|
-
nil
|
272
|
-
end
|
273
|
-
|
274
|
-
def calculate_not_before(not_before)
|
275
|
-
if not_before.nil?
|
276
|
-
#not_before will be set to 6 hours before now to prevent issues with bad system clocks (clients don't sync)
|
277
|
-
not_before = Time.now - 6 * 60 * 60
|
278
|
-
end
|
279
|
-
not_before
|
280
|
-
end
|
281
|
-
|
282
|
-
def calculate_not_after(not_after,not_before)
|
283
|
-
if not_after.nil?
|
284
|
-
not_after = not_before + 365 * 24 * 60 * 60
|
285
|
-
end
|
286
|
-
not_after
|
287
|
-
end
|
288
|
-
|
289
|
-
end
|
290
|
-
end
|
data/lib/r509/messagedigest.rb
DELETED
@@ -1,49 +0,0 @@
|
|
1
|
-
require 'openssl'
|
2
|
-
|
3
|
-
module R509
|
4
|
-
#MessageDigest allows you to specify MDs in a more friendly fashion
|
5
|
-
class MessageDigest
|
6
|
-
attr_reader :name, :digest
|
7
|
-
|
8
|
-
# @param [String,OpenSSL::Digest] arg
|
9
|
-
def initialize(arg)
|
10
|
-
if arg.kind_of?(String)
|
11
|
-
@name = arg.downcase
|
12
|
-
@digest = translate_name_to_digest
|
13
|
-
else
|
14
|
-
@digest = arg
|
15
|
-
@name = translate_digest_to_name
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
19
|
-
private
|
20
|
-
|
21
|
-
# @return [OpenSSL::Digest]
|
22
|
-
def translate_name_to_digest
|
23
|
-
case @name
|
24
|
-
when 'sha1' then OpenSSL::Digest::SHA1.new
|
25
|
-
when 'sha256' then OpenSSL::Digest::SHA256.new
|
26
|
-
when 'sha512' then OpenSSL::Digest::SHA512.new
|
27
|
-
when 'md5' then OpenSSL::Digest::MD5.new
|
28
|
-
when 'dss1' then OpenSSL::Digest::DSS1.new
|
29
|
-
else
|
30
|
-
@name = "sha1"
|
31
|
-
OpenSSL::Digest::SHA1.new
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
# @return [String]
|
36
|
-
def translate_digest_to_name
|
37
|
-
case @digest
|
38
|
-
when OpenSSL::Digest::SHA1 then 'sha1'
|
39
|
-
when OpenSSL::Digest::SHA256 then 'sha256'
|
40
|
-
when OpenSSL::Digest::SHA512 then 'sha512'
|
41
|
-
when OpenSSL::Digest::MD5 then 'md5'
|
42
|
-
when OpenSSL::Digest::DSS1 then 'dss1'
|
43
|
-
else
|
44
|
-
raise ArgumentError, "Unknown digest"
|
45
|
-
end
|
46
|
-
end
|
47
|
-
end
|
48
|
-
end
|
49
|
-
|
data/lib/r509/oidmapper.rb
DELETED
@@ -1,32 +0,0 @@
|
|
1
|
-
require 'openssl'
|
2
|
-
|
3
|
-
module R509
|
4
|
-
# Helps map raw OIDs to friendlier short names
|
5
|
-
class OidMapper
|
6
|
-
# Register an OID so we have a friendly short name
|
7
|
-
# @param [String] oid A string representation of the OID you want to map (e.g. "1.6.2.3.55")
|
8
|
-
# @param [String] short_name The short name (e.g. CN, O, OU, emailAddress)
|
9
|
-
# @param [String] long_name Optional long name. Defaults to the same as short_name
|
10
|
-
# @return [Boolean] success/failure
|
11
|
-
def self.register(oid,short_name,long_name=nil)
|
12
|
-
if long_name.nil?
|
13
|
-
long_name = short_name
|
14
|
-
end
|
15
|
-
OpenSSL::ASN1::ObjectId.register(oid, short_name, long_name)
|
16
|
-
end
|
17
|
-
|
18
|
-
# Register a batch of OIDs so we have friendly short names
|
19
|
-
# @param [Array] oids An array of hashes
|
20
|
-
# @example
|
21
|
-
# R509::OidMapper.batch_register([
|
22
|
-
# {:oid => "1.2.3.4.5", :short_name => "sName", :long_name => "lName"},
|
23
|
-
# {:oid => "1.2.3.4.6", :short_name => "oName"}
|
24
|
-
# ]
|
25
|
-
def self.batch_register(oids)
|
26
|
-
oids.each do |oid_hash|
|
27
|
-
self.register(oid_hash[:oid],oid_hash[:short_name],oid_hash[:long_name])
|
28
|
-
end
|
29
|
-
nil
|
30
|
-
end
|
31
|
-
end
|
32
|
-
end
|
data/lib/r509/privatekey.rb
DELETED
@@ -1,185 +0,0 @@
|
|
1
|
-
require 'openssl'
|
2
|
-
require 'r509/io_helpers'
|
3
|
-
require 'r509/exceptions'
|
4
|
-
|
5
|
-
module R509
|
6
|
-
#private key management
|
7
|
-
class PrivateKey
|
8
|
-
include R509::IOHelpers
|
9
|
-
|
10
|
-
# @option opts [Symbol] :type :rsa/:dsa
|
11
|
-
# @option opts [Integer] :bit_strength
|
12
|
-
# @option opts [String] :password
|
13
|
-
# @option opts [String,OpenSSL::PKey::RSA,OpenSSL::PKey::DSA] :key
|
14
|
-
# @option opts [OpenSSL::Engine] :engine
|
15
|
-
# @option opts [string] :key_name (used with engine)
|
16
|
-
def initialize(opts)
|
17
|
-
if not opts.kind_of?(Hash)
|
18
|
-
raise ArgumentError, 'Must provide a hash of options'
|
19
|
-
end
|
20
|
-
|
21
|
-
if opts.has_key?(:engine) and opts.has_key?(:key)
|
22
|
-
raise ArgumentError, 'You can\'t pass both :key and :engine'
|
23
|
-
elsif opts.has_key?(:key_name) and not opts.has_key?(:engine)
|
24
|
-
raise ArgumentError, 'When providing a :key_name you MUST provide an :engine'
|
25
|
-
elsif opts.has_key?(:engine) and not opts.has_key?(:key_name)
|
26
|
-
raise ArgumentError, 'When providing an :engine you MUST provide a :key_name'
|
27
|
-
elsif opts.has_key?(:engine) and opts.has_key?(:key_name)
|
28
|
-
if not opts[:engine].kind_of?(OpenSSL::Engine)
|
29
|
-
raise ArgumentError, 'When providing an engine, it must be of type OpenSSL::Engine'
|
30
|
-
end
|
31
|
-
@engine = opts[:engine]
|
32
|
-
@key_name = opts[:key_name]
|
33
|
-
end
|
34
|
-
|
35
|
-
if opts.has_key?(:key)
|
36
|
-
password = opts[:password] || nil
|
37
|
-
#OpenSSL::PKey.read solves this begin/rescue garbage but is only
|
38
|
-
#available to Ruby 1.9.3+
|
39
|
-
begin
|
40
|
-
@key = OpenSSL::PKey::RSA.new(opts[:key],password)
|
41
|
-
rescue OpenSSL::PKey::RSAError
|
42
|
-
begin
|
43
|
-
@key = OpenSSL::PKey::DSA.new(opts[:key],password)
|
44
|
-
rescue
|
45
|
-
raise R509::R509Error, "Failed to load private key. Invalid key or incorrect password."
|
46
|
-
end
|
47
|
-
end
|
48
|
-
else
|
49
|
-
bit_strength = opts[:bit_strength] || 2048
|
50
|
-
type = opts[:type] || :rsa
|
51
|
-
case type
|
52
|
-
when :rsa
|
53
|
-
@key = OpenSSL::PKey::RSA.new(bit_strength)
|
54
|
-
when :dsa
|
55
|
-
@key = OpenSSL::PKey::DSA.new(bit_strength)
|
56
|
-
else
|
57
|
-
raise ArgumentError, 'Must provide :rsa or :dsa as type when key or engine is nil'
|
58
|
-
end
|
59
|
-
end
|
60
|
-
end
|
61
|
-
|
62
|
-
# Helper method to quickly load a private key from the filesystem
|
63
|
-
#
|
64
|
-
# @param [String] filename Path to file you want to load
|
65
|
-
# @return [R509::PrivateKey] PrivateKey object
|
66
|
-
def self.load_from_file( filename, password = nil )
|
67
|
-
return R509::PrivateKey.new(:key => IOHelpers.read_data(filename), :password => password )
|
68
|
-
end
|
69
|
-
|
70
|
-
|
71
|
-
# @return [Integer]
|
72
|
-
def bit_strength
|
73
|
-
if self.rsa?
|
74
|
-
return self.public_key.n.num_bits
|
75
|
-
elsif self.dsa?
|
76
|
-
return self.public_key.p.num_bits
|
77
|
-
end
|
78
|
-
end
|
79
|
-
|
80
|
-
# @return [OpenSSL::PKey::RSA,OpenSSL::PKey::DSA,OpenSSL::Engine pkey] this method may return the PKey object itself or a handle to the private key in the HSM (which will not show the private key, just public)
|
81
|
-
def key
|
82
|
-
if in_hardware?
|
83
|
-
@engine.load_private_key(@key_name)
|
84
|
-
else
|
85
|
-
@key
|
86
|
-
end
|
87
|
-
end
|
88
|
-
|
89
|
-
# @return [Boolean] whether the key is resident in hardware or not
|
90
|
-
def in_hardware?
|
91
|
-
if not @engine.nil?
|
92
|
-
true
|
93
|
-
else
|
94
|
-
false
|
95
|
-
end
|
96
|
-
end
|
97
|
-
|
98
|
-
# @return [OpenSSL::PKey::RSA,OpenSSL::PKey::DSA] public key
|
99
|
-
def public_key
|
100
|
-
self.key.public_key
|
101
|
-
end
|
102
|
-
|
103
|
-
alias :to_s :public_key
|
104
|
-
|
105
|
-
# Converts the key into the PEM format
|
106
|
-
#
|
107
|
-
# @return [String] the key converted into PEM format.
|
108
|
-
def to_pem
|
109
|
-
if in_hardware?
|
110
|
-
raise R509::R509Error, "This method cannot be called when using keys in hardware"
|
111
|
-
end
|
112
|
-
self.key.to_pem
|
113
|
-
end
|
114
|
-
|
115
|
-
# Converts the key into encrypted PEM format
|
116
|
-
#
|
117
|
-
# @param [String,OpenSSL::Cipher] cipher to use for encryption
|
118
|
-
# full list of available ciphers can be obtained with OpenSSL::Cipher.ciphers
|
119
|
-
# (common ones are des3, aes256, aes128)
|
120
|
-
# @param [String] password password
|
121
|
-
# @return [String] the key converted into encrypted PEM format.
|
122
|
-
def to_encrypted_pem(cipher,password)
|
123
|
-
if in_hardware?
|
124
|
-
raise R509::R509Error, "This method cannot be called when using keys in hardware"
|
125
|
-
end
|
126
|
-
cipher = OpenSSL::Cipher::Cipher.new(cipher)
|
127
|
-
self.key.to_pem(cipher,password)
|
128
|
-
end
|
129
|
-
|
130
|
-
|
131
|
-
# Converts the key into the DER format
|
132
|
-
#
|
133
|
-
# @return [String] the key converted into DER format.
|
134
|
-
def to_der
|
135
|
-
if in_hardware?
|
136
|
-
raise R509::R509Error, "This method cannot be called when using keys in hardware"
|
137
|
-
end
|
138
|
-
self.key.to_der
|
139
|
-
end
|
140
|
-
|
141
|
-
# Writes the key into the PEM format
|
142
|
-
#
|
143
|
-
# @param [String, #write] filename_or_io Either a string of the path for
|
144
|
-
# the file that you'd like to write, or an IO-like object.
|
145
|
-
def write_pem(filename_or_io)
|
146
|
-
write_data(filename_or_io, self.to_pem)
|
147
|
-
end
|
148
|
-
|
149
|
-
|
150
|
-
# Writes the key into encrypted PEM format with specified cipher
|
151
|
-
#
|
152
|
-
# @param [String, #write] filename_or_io Either a string of the path for
|
153
|
-
# the file that you'd like to write, or an IO-like object.
|
154
|
-
# @param [String,OpenSSL::Cipher] cipher to use for encryption
|
155
|
-
# full list of available ciphers can be obtained with OpenSSL::Cipher.ciphers
|
156
|
-
# (common ones are des3, aes256, aes128)
|
157
|
-
# @param [String] password password
|
158
|
-
def write_encrypted_pem(filename_or_io,cipher,password)
|
159
|
-
write_data(filename_or_io, to_encrypted_pem(cipher,password))
|
160
|
-
end
|
161
|
-
|
162
|
-
# Writes the key into the DER format
|
163
|
-
#
|
164
|
-
# @param [String, #write] filename_or_io Either a string of the path for
|
165
|
-
# the file that you'd like to write, or an IO-like object.
|
166
|
-
def write_der(filename_or_io)
|
167
|
-
write_data(filename_or_io, self.to_der)
|
168
|
-
end
|
169
|
-
|
170
|
-
|
171
|
-
# Returns whether the public key is RSA
|
172
|
-
#
|
173
|
-
# @return [Boolean] true if the public key is RSA, false otherwise
|
174
|
-
def rsa?
|
175
|
-
self.key.kind_of?(OpenSSL::PKey::RSA)
|
176
|
-
end
|
177
|
-
|
178
|
-
# Returns whether the public key is DSA
|
179
|
-
#
|
180
|
-
# @return [Boolean] true if the public key is DSA, false otherwise
|
181
|
-
def dsa?
|
182
|
-
self.key.kind_of?(OpenSSL::PKey::DSA)
|
183
|
-
end
|
184
|
-
end
|
185
|
-
end
|