Module: VagrantDockerCertificatesManager::Cert

Defined in:
lib/vagrant-docker-certificates-manager/util/cert.rb

Constant Summary collapse

MARKER =
"VDCM"

Class Method Summary collapse

Class Method Details

.default_name_from(path) ⇒ Object



26
27
28
29
# File 'lib/vagrant-docker-certificates-manager/util/cert.rb', line 26

def default_name_from(path)
  base = File.basename(path).sub(/\.(pem|crt|cer)$/i, "")
  base.empty? ? "local.dev" : base
end

.fingerprint_of(cert) ⇒ Object



35
36
37
# File 'lib/vagrant-docker-certificates-manager/util/cert.rb', line 35

def fingerprint_of(cert)
  OpenSSL::Digest::SHA1.hexdigest(cert.to_der).upcase
end

.generate_ca(common_name:, days: 3650, org: "VDCM", country: "FR") ⇒ Array<OpenSSL::X509::Certificate, OpenSSL::PKey::RSA>

Generates a local development certificate authority.

The generated CA is intended for local Docker/Vagrant workflows, not as a public browser-trusted certificate authority.

Parameters:

  • cn (String)

    Common name for the CA certificate.

  • days (Integer) (defaults to: 3650)

    Validity period in days.

  • org (String) (defaults to: "VDCM")

    Organization attribute written to the subject.

  • country (String) (defaults to: "FR")

    Country attribute written to the subject.

Returns:

  • (Array<OpenSSL::X509::Certificate, OpenSSL::PKey::RSA>)

    Generated certificate and private key.



49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
# File 'lib/vagrant-docker-certificates-manager/util/cert.rb', line 49

def generate_ca(common_name:, days: 3650, org: "VDCM", country: "FR")
  key  = OpenSSL::PKey::RSA.generate(2048)
  cert = OpenSSL::X509::Certificate.new

  cert.version    = 2
  cert.serial     = 1
  cert.subject    = OpenSSL::X509::Name.parse("/CN=#{common_name}/O=#{org}/C=#{country}")
  cert.issuer     = cert.subject
  cert.public_key = key.public_key
  cert.not_before = Time.now
  cert.not_after  = Time.now + (days * 24 * 60 * 60)

  ef = OpenSSL::X509::ExtensionFactory.new(cert, cert)
  cert.add_extension(ef.create_extension("basicConstraints",     "CA:TRUE",             true))
  cert.add_extension(ef.create_extension("keyUsage",             "keyCertSign,cRLSign", true))
  cert.add_extension(ef.create_extension("subjectKeyIdentifier", "hash"))
  cert.sign(key, OpenSSL::Digest.new("SHA256"))

  [cert, key]
end

.generate_crl(ca_cert, ca_key, days: 3650) ⇒ OpenSSL::X509::CRL

Generates an empty certificate revocation list for the local CA.

Parameters:

  • ca_cert (OpenSSL::X509::Certificate)

    CA certificate used as issuer.

  • ca_key (OpenSSL::PKey::RSA)

    CA private key used to sign the CRL.

  • days (Integer) (defaults to: 3650)

    Validity period in days.

Returns:

  • (OpenSSL::X509::CRL)

    Signed CRL.



119
120
121
122
123
124
125
126
127
# File 'lib/vagrant-docker-certificates-manager/util/cert.rb', line 119

def generate_crl(ca_cert, ca_key, days: 3650)
  crl = OpenSSL::X509::CRL.new
  crl.issuer      = ca_cert.subject
  crl.version     = 1
  crl.last_update = Time.now
  crl.next_update = Time.now + (days * 24 * 60 * 60)
  crl.sign(ca_key, OpenSSL::Digest.new("SHA256"))
  crl
end

.generate_server(ca_cert, ca_key, domain:, days: 825, crl_url: nil) ⇒ Array<OpenSSL::X509::Certificate, OpenSSL::PKey::RSA>

Generates a server certificate signed by the supplied local CA.

Modern clients validate the SAN extension; the CN is kept mainly for readability and compatibility with older tooling.

Parameters:

  • ca_cert (OpenSSL::X509::Certificate)

    CA certificate used as issuer.

  • ca_key (OpenSSL::PKey::RSA)

    CA private key used to sign the certificate.

  • domain (String)

    DNS name written to CN and subjectAltName.

  • days (Integer) (defaults to: 825)

    Validity period in days.

  • crl_url (String, nil) (defaults to: nil)

    Optional CRL distribution point URL.

Returns:

  • (Array<OpenSSL::X509::Certificate, OpenSSL::PKey::RSA>)

    Generated certificate and private key.



88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
# File 'lib/vagrant-docker-certificates-manager/util/cert.rb', line 88

def generate_server(ca_cert, ca_key, domain:, days: 825, crl_url: nil)
  key  = OpenSSL::PKey::RSA.generate(2048)
  cert = OpenSSL::X509::Certificate.new

  cert.version    = 2
  cert.serial     = 2
  cert.subject    = OpenSSL::X509::Name.parse("/CN=#{domain}")
  cert.issuer     = ca_cert.subject
  cert.public_key = key.public_key
  cert.not_before = Time.now
  cert.not_after  = Time.now + (days * 24 * 60 * 60)

  ef = OpenSSL::X509::ExtensionFactory.new(ca_cert, cert)
  cert.add_extension(ef.create_extension("subjectAltName",   "DNS:#{domain}"))
  cert.add_extension(ef.create_extension("basicConstraints", "CA:FALSE"))
  cert.add_extension(ef.create_extension("keyUsage",         "digitalSignature,keyEncipherment", true))
  cert.add_extension(ef.create_extension("extendedKeyUsage", "serverAuth"))
  unless crl_url.to_s.strip.empty?
    cert.add_extension(ef.create_extension("crlDistributionPoints", "URI:#{crl_url}"))
  end
  cert.sign(ca_key, OpenSSL::Digest.new("SHA256"))

  [cert, key]
end

.load_ca(cert_path, key_path) ⇒ Object



70
71
72
73
74
75
# File 'lib/vagrant-docker-certificates-manager/util/cert.rb', line 70

def load_ca(cert_path, key_path)
  [
    OpenSSL::X509::Certificate.new(File.read(cert_path)),
    OpenSSL::PKey::RSA.new(File.read(key_path))
  ]
end

.nickname_for(name) ⇒ Object



31
32
33
# File 'lib/vagrant-docker-certificates-manager/util/cert.rb', line 31

def nickname_for(name)
  "#{MARKER}:#{name}"
end

.read_cert(path) ⇒ Object



11
12
13
# File 'lib/vagrant-docker-certificates-manager/util/cert.rb', line 11

def read_cert(path)
  OpenSSL::X509::Certificate.new(File.read(path))
end

.sha1(path) ⇒ Object



15
16
17
18
# File 'lib/vagrant-docker-certificates-manager/util/cert.rb', line 15

def sha1(path)
  cert = read_cert(path)
  OpenSSL::Digest::SHA1.hexdigest(cert.to_der).upcase
end

.subject_cn(path) ⇒ Object



20
21
22
23
24
# File 'lib/vagrant-docker-certificates-manager/util/cert.rb', line 20

def subject_cn(path)
  cert = read_cert(path)
  pair = cert.subject.to_a.find { |(k, _v, _t)| k == "CN" }
  pair ? pair[1].to_s : nil
end