Module: Chef::Mixin::OpenSSLHelper
- Included in:
- Resource::OpensslDhparam, Resource::OpensslEcPrivateKey, Resource::OpensslEcPublicKey, Resource::OpensslRsaPrivateKey, Resource::OpensslRsaPublicKey, Resource::OpensslX509Certificate, Resource::OpensslX509Crl, Resource::OpensslX509Request
- Defined in:
- lib/chef/mixin/openssl_helper.rb
Overview
various helpers for use with openssl. Currently used by the openssl_* resources
Instance Method Summary collapse
-
#cert_need_renewal?(cert_file, renew_before_expiry) ⇒ true, false
(also: #cert_need_renewall?)
Return true if a certificate need to be renewed (or doesn’t exist) according to the number of days before expiration given.
-
#crl_file_valid?(crl_file) ⇒ Boolean
given a crl file path see if it’s actually a crl.
-
#dhparam_pem_valid?(dhparam_pem_path) ⇒ Boolean
validate a dhparam file from path.
-
#encrypt_ec_key(ec_key, key_password, key_cipher) ⇒ String
generate a pem file given a cipher, key, an optional key_password.
-
#encrypt_rsa_key(rsa_key, key_password, key_cipher) ⇒ String
generate a pem file given a cipher, key, an optional key_password.
-
#gen_dhparam(key_length, generator) ⇒ OpenSSL::PKey::DH
generate a dhparam file.
-
#gen_ec_priv_key(curve) ⇒ OpenSSL::PKey::DH
generate an ec private key given curve type.
-
#gen_ec_pub_key(priv_key, priv_key_password = nil) ⇒ String
generate pem format of the public key given a private key.
-
#gen_rsa_priv_key(key_length) ⇒ OpenSSL::PKey::DH
generate an RSA private key given key length.
-
#gen_rsa_pub_key(priv_key, priv_key_password = nil) ⇒ String
generate pem format of the public key given a private key.
-
#gen_serial ⇒ Integer
generate a random Serial.
-
#gen_x509_cert(request, extension, info, key) ⇒ OpenSSL::X509::Certificate
generate a Certificate given a X509 request.
-
#gen_x509_crl(ca_private_key, info) ⇒ OpenSSL::X509::CRL
generate a X509 CRL given a CA.
-
#gen_x509_extensions(extensions) ⇒ Array
generate an array of X509 Extensions given a hash of extensions.
-
#gen_x509_request(subject, key) ⇒ OpenSSL::X509::Request
generate a csr pem file given a subject and a private key.
-
#get_key_filename(cert_filename) ⇒ String
determine the key filename from the cert filename.
-
#get_next_crl_number(crl) ⇒ Integer
generate the next CRL number available for a X509 CRL given.
-
#key_length_valid?(number) ⇒ Boolean
is the key length a valid key length.
-
#priv_key_file_valid?(key_file, key_password = nil) ⇒ Boolean
given either a key file path or key file content see if it’s actually a private key.
-
#renew_x509_crl(crl, ca_private_key, info) ⇒ OpenSSL::X509::CRL
renew a X509 crl given.
-
#revoke_x509_crl(revoke_info, crl, ca_private_key, info) ⇒ OpenSSL::X509::CRL
add a serial given in the crl given.
-
#serial_revoked?(crl, serial) ⇒ true, false
check is a serial given is revoked in a crl given.
Instance Method Details
#cert_need_renewal?(cert_file, renew_before_expiry) ⇒ true, false Also known as: cert_need_renewall?
Return true if a certificate need to be renewed (or doesn’t exist) according to the number of days before expiration given
412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 |
# File 'lib/chef/mixin/openssl_helper.rb', line 412 def cert_need_renewal?(cert_file, renew_before_expiry) resp = true cert_content = ::File.exist?(cert_file) ? File.read(cert_file) : cert_file begin cert = OpenSSL::X509::Certificate.new cert_content rescue ::OpenSSL::X509::CertificateError return resp end unless cert.not_after <= Time.now + 3600 * 24 * renew_before_expiry resp = false end resp end |
#crl_file_valid?(crl_file) ⇒ Boolean
given a crl file path see if it’s actually a crl
77 78 79 80 81 82 83 84 |
# File 'lib/chef/mixin/openssl_helper.rb', line 77 def crl_file_valid?(crl_file) begin ::OpenSSL::X509::CRL.new ::File.read(crl_file) rescue ::OpenSSL::X509::CRLError, Errno::ENOENT return false end true end |
#dhparam_pem_valid?(dhparam_pem_path) ⇒ Boolean
validate a dhparam file from path
42 43 44 45 46 47 48 49 |
# File 'lib/chef/mixin/openssl_helper.rb', line 42 def dhparam_pem_valid?(dhparam_pem_path) # Check if the dhparam.pem file exists # Verify the dhparam.pem file contains a key return false unless ::File.exist?(dhparam_pem_path) dhparam = ::OpenSSL::PKey::DH.new File.read(dhparam_pem_path) dhparam.params_ok? end |
#encrypt_ec_key(ec_key, key_password, key_cipher) ⇒ String
generate a pem file given a cipher, key, an optional key_password
192 193 194 195 196 197 198 199 200 |
# File 'lib/chef/mixin/openssl_helper.rb', line 192 def encrypt_ec_key(ec_key, key_password, key_cipher) raise TypeError, "ec_key must be a Ruby OpenSSL::PKey::EC object" unless ec_key.is_a?(::OpenSSL::PKey::EC) raise TypeError, "key_password must be a string" unless key_password.is_a?(String) raise TypeError, "key_cipher must be a string" unless key_cipher.is_a?(String) raise ArgumentError, "Specified key_cipher is not available on this system" unless ::OpenSSL::Cipher.ciphers.include?(key_cipher) cipher = ::OpenSSL::Cipher.new(key_cipher) ec_key.to_pem(cipher, key_password) end |
#encrypt_rsa_key(rsa_key, key_password, key_cipher) ⇒ String
generate a pem file given a cipher, key, an optional key_password
143 144 145 146 147 148 149 150 151 |
# File 'lib/chef/mixin/openssl_helper.rb', line 143 def encrypt_rsa_key(rsa_key, key_password, key_cipher) raise TypeError, "rsa_key must be a Ruby OpenSSL::PKey::RSA object" unless rsa_key.is_a?(::OpenSSL::PKey::RSA) raise TypeError, "key_password must be a string" unless key_password.is_a?(String) raise TypeError, "key_cipher must be a string" unless key_cipher.is_a?(String) raise ArgumentError, "Specified key_cipher is not available on this system" unless ::OpenSSL::Cipher.ciphers.include?(key_cipher) cipher = ::OpenSSL::Cipher.new(key_cipher) rsa_key.to_pem(cipher, key_password) end |
#gen_dhparam(key_length, generator) ⇒ OpenSSL::PKey::DH
generate a dhparam file
110 111 112 113 114 115 |
# File 'lib/chef/mixin/openssl_helper.rb', line 110 def gen_dhparam(key_length, generator) raise ArgumentError, "Key length must be a power of 2 greater than or equal to 1024" unless key_length_valid?(key_length) raise TypeError, "Generator must be an integer" unless generator.is_a?(Integer) ::OpenSSL::PKey::DH.new(key_length, generator) end |
#gen_ec_priv_key(curve) ⇒ OpenSSL::PKey::DH
generate an ec private key given curve type
156 157 158 159 160 161 |
# File 'lib/chef/mixin/openssl_helper.rb', line 156 def gen_ec_priv_key(curve) raise TypeError, "curve must be a string" unless curve.is_a?(String) raise ArgumentError, "Specified curve is not available on this system" unless %w{prime256v1 secp384r1 secp521r1}.include?(curve) ::OpenSSL::PKey::EC.generate(curve) end |
#gen_ec_pub_key(priv_key, priv_key_password = nil) ⇒ String
generate pem format of the public key given a private key
167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 |
# File 'lib/chef/mixin/openssl_helper.rb', line 167 def gen_ec_pub_key(priv_key, priv_key_password = nil) # if the file exists try to read the content # if not assume we were passed the key and set the string to the content key_content = ::File.exist?(priv_key) ? File.read(priv_key) : priv_key key = ::OpenSSL::PKey::EC.new key_content, priv_key_password # Get curve type (prime256v1...) group = ::OpenSSL::PKey::EC::Group.new(key.group.curve_name) # Get Generator point & public point (priv * generator) generator = group.generator pub_point = generator.mul(key.private_key) key.public_key = pub_point # Public Key in pem public_key = ::OpenSSL::PKey::EC.new public_key.group = group public_key.public_key = pub_point public_key.to_pem end |
#gen_rsa_priv_key(key_length) ⇒ OpenSSL::PKey::DH
generate an RSA private key given key length
120 121 122 123 124 |
# File 'lib/chef/mixin/openssl_helper.rb', line 120 def gen_rsa_priv_key(key_length) raise ArgumentError, "Key length must be a power of 2 greater than or equal to 1024" unless key_length_valid?(key_length) ::OpenSSL::PKey::RSA.new(key_length) end |
#gen_rsa_pub_key(priv_key, priv_key_password = nil) ⇒ String
generate pem format of the public key given a private key
130 131 132 133 134 135 136 |
# File 'lib/chef/mixin/openssl_helper.rb', line 130 def gen_rsa_pub_key(priv_key, priv_key_password = nil) # if the file exists try to read the content # if not assume we were passed the key and set the string to the content key_content = ::File.exist?(priv_key) ? File.read(priv_key) : priv_key key = ::OpenSSL::PKey::RSA.new key_content, priv_key_password key.public_key.to_pem end |
#gen_serial ⇒ Integer
generate a random Serial
242 243 244 |
# File 'lib/chef/mixin/openssl_helper.rb', line 242 def gen_serial ::OpenSSL::BN.generate_prime(160) end |
#gen_x509_cert(request, extension, info, key) ⇒ OpenSSL::X509::Certificate
generate a Certificate given a X509 request
252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 |
# File 'lib/chef/mixin/openssl_helper.rb', line 252 def gen_x509_cert(request, extension, info, key) raise TypeError, "request must be a Ruby OpenSSL::X509::Request" unless request.is_a?(::OpenSSL::X509::Request) raise TypeError, "extension must be a Ruby Array" unless extension.is_a?(Array) raise TypeError, "info must be a Ruby Hash" unless info.is_a?(Hash) raise TypeError, "key must be a Ruby OpenSSL::PKey::EC object or a Ruby OpenSSL::PKey::RSA object" unless key.is_a?(::OpenSSL::PKey::EC) || key.is_a?(::OpenSSL::PKey::RSA) raise ArgumentError, "info must contain a validity" unless info.key?("validity") raise TypeError, "info['validity'] must be a Ruby Integer object" unless info["validity"].is_a?(Integer) cert = ::OpenSSL::X509::Certificate.new ef = ::OpenSSL::X509::ExtensionFactory.new cert.serial = gen_serial cert.version = 2 cert.subject = request.subject cert.public_key = request.public_key cert.not_before = Time.now cert.not_after = cert.not_before + info["validity"] * 24 * 60 * 60 if info["issuer"].nil? cert.issuer = request.subject ef.issuer_certificate = cert extension << ef.create_extension("basicConstraints", "CA:TRUE", true) else raise TypeError, "info['issuer'] must be a Ruby OpenSSL::X509::Certificate object" unless info["issuer"].is_a?(::OpenSSL::X509::Certificate) cert.issuer = info["issuer"].subject ef.issuer_certificate = info["issuer"] end ef.subject_certificate = cert if openssl_config = __openssl_config ef.config = openssl_config end cert.extensions = extension cert.add_extension ef.create_extension("subjectKeyIdentifier", "hash") cert.add_extension ef.create_extension("authorityKeyIdentifier", "keyid:always,issuer:always") cert.sign(key, ::OpenSSL::Digest.new("SHA256")) cert end |
#gen_x509_crl(ca_private_key, info) ⇒ OpenSSL::X509::CRL
generate a X509 CRL given a CA
299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 |
# File 'lib/chef/mixin/openssl_helper.rb', line 299 def gen_x509_crl(ca_private_key, info) raise TypeError, "ca_private_key must be a Ruby OpenSSL::PKey::EC object or a Ruby OpenSSL::PKey::RSA object" unless ca_private_key.is_a?(::OpenSSL::PKey::EC) || ca_private_key.is_a?(::OpenSSL::PKey::RSA) raise TypeError, "info must be a Ruby Hash" unless info.is_a?(Hash) raise ArgumentError, "info must contain a issuer and a validity" unless info.key?("issuer") && info.key?("validity") raise TypeError, "info['issuer'] must be a Ruby OpenSSL::X509::Certificate object" unless info["issuer"].is_a?(::OpenSSL::X509::Certificate) raise TypeError, "info['validity'] must be a Ruby Integer object" unless info["validity"].is_a?(Integer) crl = ::OpenSSL::X509::CRL.new ef = ::OpenSSL::X509::ExtensionFactory.new crl.version = 1 crl.issuer = info["issuer"].subject crl.last_update = Time.now crl.next_update = Time.now + 3600 * 24 * info["validity"] if openssl_config = __openssl_config ef.config = openssl_config end ef.issuer_certificate = info["issuer"] crl.add_extension ::OpenSSL::X509::Extension.new("crlNumber", ::OpenSSL::ASN1::Integer(1)) crl.add_extension ef.create_extension("authorityKeyIdentifier", "keyid:always,issuer:always") crl.sign(ca_private_key, ::OpenSSL::Digest.new("SHA256")) crl end |
#gen_x509_extensions(extensions) ⇒ Array
generate an array of X509 Extensions given a hash of extensions
225 226 227 228 229 230 231 232 233 234 235 236 237 238 |
# File 'lib/chef/mixin/openssl_helper.rb', line 225 def gen_x509_extensions(extensions) raise TypeError, "extensions must be a Ruby Hash object" unless extensions.is_a?(Hash) exts = [] extensions.each do |ext_name, ext_prop| raise TypeError, "#{ext_name} must contain a Ruby Hash" unless ext_prop.is_a?(Hash) raise ArgumentError, "keys in #{ext_name} must be 'values' and 'critical'" unless ext_prop.key?("values") && ext_prop.key?("critical") raise TypeError, "the key 'values' must contain a Ruby Arrays" unless ext_prop["values"].is_a?(Array) raise TypeError, "the key 'critical' must be a Ruby Boolean true/false" unless ext_prop["critical"].is_a?(TrueClass) || ext_prop["critical"].is_a?(FalseClass) exts << ::OpenSSL::X509::ExtensionFactory.new.create_extension(ext_name, ext_prop["values"].join(","), ext_prop["critical"]) end exts end |
#gen_x509_request(subject, key) ⇒ OpenSSL::X509::Request
generate a csr pem file given a subject and a private key
206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 |
# File 'lib/chef/mixin/openssl_helper.rb', line 206 def gen_x509_request(subject, key) raise TypeError, "subject must be a Ruby OpenSSL::X509::Name object" unless subject.is_a?(::OpenSSL::X509::Name) raise TypeError, "key must be a Ruby OpenSSL::PKey::EC or a Ruby OpenSSL::PKey::RSA object" unless key.is_a?(::OpenSSL::PKey::EC) || key.is_a?(::OpenSSL::PKey::RSA) request = ::OpenSSL::X509::Request.new request.version = 0 request.subject = subject request.public_key = key # Chef 12 backward compatibility ::OpenSSL::PKey::EC.send(:alias_method, :private?, :private_key?) request.sign(key, ::OpenSSL::Digest.new("SHA256")) request end |
#get_key_filename(cert_filename) ⇒ String
determine the key filename from the cert filename
26 27 28 29 30 |
# File 'lib/chef/mixin/openssl_helper.rb', line 26 def get_key_filename(cert_filename) cert_file_path, cert_filename = ::File.split(cert_filename) cert_filename = ::File.basename(cert_filename, ::File.extname(cert_filename)) cert_file_path + ::File::SEPARATOR + cert_filename + ".key" end |
#get_next_crl_number(crl) ⇒ Integer
generate the next CRL number available for a X509 CRL given
330 331 332 333 334 335 336 337 338 |
# File 'lib/chef/mixin/openssl_helper.rb', line 330 def get_next_crl_number(crl) raise TypeError, "crl must be a Ruby OpenSSL::X509::CRL object" unless crl.is_a?(::OpenSSL::X509::CRL) crlnum = 1 crl.extensions.each do |e| crlnum = e.value if e.oid == "crlNumber" end crlnum.to_i + 1 end |
#key_length_valid?(number) ⇒ Boolean
is the key length a valid key length
35 36 37 |
# File 'lib/chef/mixin/openssl_helper.rb', line 35 def key_length_valid?(number) number >= 1024 && ( number & (number - 1) == 0 ) end |
#priv_key_file_valid?(key_file, key_password = nil) ⇒ Boolean
given either a key file path or key file content see if it’s actually a private key
56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 |
# File 'lib/chef/mixin/openssl_helper.rb', line 56 def priv_key_file_valid?(key_file, key_password = nil) # if the file exists try to read the content # if not assume we were passed the key and set the string to the content key_content = ::File.exist?(key_file) ? File.read(key_file) : key_file begin key = ::OpenSSL::PKey.read key_content, key_password rescue ::OpenSSL::PKey::PKeyError, ArgumentError return false end if key.is_a?(::OpenSSL::PKey::EC) key.private_key? else key.private? end end |
#renew_x509_crl(crl, ca_private_key, info) ⇒ OpenSSL::X509::CRL
renew a X509 crl given
381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 |
# File 'lib/chef/mixin/openssl_helper.rb', line 381 def renew_x509_crl(crl, ca_private_key, info) raise TypeError, "crl must be a Ruby OpenSSL::X509::CRL object" unless crl.is_a?(::OpenSSL::X509::CRL) raise TypeError, "ca_private_key must be a Ruby OpenSSL::PKey::EC object or a Ruby OpenSSL::PKey::RSA object" unless ca_private_key.is_a?(::OpenSSL::PKey::EC) || ca_private_key.is_a?(::OpenSSL::PKey::RSA) raise TypeError, "info must be a Ruby Hash" unless info.is_a?(Hash) raise ArgumentError, "info must contain a issuer and a validity" unless info.key?("issuer") && info.key?("validity") raise TypeError, "info['issuer'] must be a Ruby OpenSSL::X509::Certificate object" unless info["issuer"].is_a?(::OpenSSL::X509::Certificate) raise TypeError, "info['validity'] must be a Ruby Integer object" unless info["validity"].is_a?(Integer) crl.last_update = Time.now crl.next_update = crl.last_update + 3600 * 24 * info["validity"] ef = ::OpenSSL::X509::ExtensionFactory.new if openssl_config = __openssl_config ef.config = openssl_config end ef.issuer_certificate = info["issuer"] crl.extensions = [ ::OpenSSL::X509::Extension.new("crlNumber", ::OpenSSL::ASN1::Integer(get_next_crl_number(crl)))] crl.add_extension ef.create_extension("authorityKeyIdentifier", "keyid:always,issuer:always") crl.sign(ca_private_key, ::OpenSSL::Digest.new("SHA256")) crl end |
#revoke_x509_crl(revoke_info, crl, ca_private_key, info) ⇒ OpenSSL::X509::CRL
add a serial given in the crl given
346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 |
# File 'lib/chef/mixin/openssl_helper.rb', line 346 def revoke_x509_crl(revoke_info, crl, ca_private_key, info) raise TypeError, "revoke_info must be a Ruby Hash object" unless revoke_info.is_a?(Hash) raise TypeError, "crl must be a Ruby OpenSSL::X509::CRL object" unless crl.is_a?(::OpenSSL::X509::CRL) raise TypeError, "ca_private_key must be a Ruby OpenSSL::PKey::EC object or a Ruby OpenSSL::PKey::RSA object" unless ca_private_key.is_a?(::OpenSSL::PKey::EC) || ca_private_key.is_a?(::OpenSSL::PKey::RSA) raise TypeError, "info must be a Ruby Hash" unless info.is_a?(Hash) raise ArgumentError, "revoke_info must contain a serial and a reason" unless revoke_info.key?("serial") && revoke_info.key?("reason") raise TypeError, "revoke_info['serial'] must be a Ruby String or Integer object" unless revoke_info["serial"].is_a?(String) || revoke_info["serial"].is_a?(Integer) raise TypeError, "revoke_info['reason'] must be a Ruby Integer object" unless revoke_info["reason"].is_a?(Integer) raise ArgumentError, "info must contain a issuer and a validity" unless info.key?("issuer") && info.key?("validity") raise TypeError, "info['issuer'] must be a Ruby OpenSSL::X509::Certificate object" unless info["issuer"].is_a?(::OpenSSL::X509::Certificate) raise TypeError, "info['validity'] must be a Ruby Integer object" unless info["validity"].is_a?(Integer) revoked = ::OpenSSL::X509::Revoked.new revoked.serial = if revoke_info["serial"].is_a?(String) revoke_info["serial"].to_i(16) else revoke_info["serial"] end revoked.time = Time.now ext = ::OpenSSL::X509::Extension.new("CRLReason", ::OpenSSL::ASN1::Enumerated(revoke_info["reason"])) revoked.add_extension(ext) crl.add_revoked(revoked) renew_x509_crl(crl, ca_private_key, info) end |
#serial_revoked?(crl, serial) ⇒ true, false
check is a serial given is revoked in a crl given
90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 |
# File 'lib/chef/mixin/openssl_helper.rb', line 90 def serial_revoked?(crl, serial) raise TypeError, "crl must be a Ruby OpenSSL::X509::CRL object" unless crl.is_a?(::OpenSSL::X509::CRL) raise TypeError, "serial must be a Ruby String or Integer object" unless serial.is_a?(String) || serial.is_a?(Integer) serial_to_verify = if serial.is_a?(String) serial.to_i(16) else serial end status = false crl.revoked.each do |revoked| status = true if revoked.serial == serial_to_verify end status end |