3
# The tedious class that does all the manipulations to the
4
# certificate to correctly sign it. Yay.
5
class Puppet::SSL::CertificateFactory
6
# How we convert from various units to the required seconds.
8
"y" => 365 * 24 * 60 * 60,
14
attr_reader :name, :cert_type, :csr, :issuer, :serial
16
def initialize(cert_type, csr, issuer, serial)
17
@cert_type, @csr, @issuer, @serial = cert_type, csr, issuer, serial
22
# Actually generate our certificate.
24
@cert = OpenSSL::X509::Certificate.new
26
@cert.version = 2 # X509v3
27
@cert.subject = @csr.subject
28
@cert.issuer = @issuer.subject
29
@cert.public_key = @csr.public_key
30
@cert.serial = @serial
41
# This is pretty ugly, but I'm not really sure it's even possible to do
44
@ef = OpenSSL::X509::ExtensionFactory.new
46
@ef.subject_certificate = @cert
48
if @issuer.is_a?(OpenSSL::X509::Request) # It's a self-signed cert
49
@ef.issuer_certificate = @cert
51
@ef.issuer_certificate = @issuer
54
@subject_alt_name = []
59
method = "add_#{@cert_type.to_s}_extensions"
64
raise ArgumentError, "%s is an invalid certificate type" % @cert_type
67
@extensions << @ef.create_extension("nsComment", "Puppet Ruby/OpenSSL Generated Certificate")
68
@extensions << @ef.create_extension("basicConstraints", @basic_constraint, true)
69
@extensions << @ef.create_extension("subjectKeyIdentifier", "hash")
70
@extensions << @ef.create_extension("keyUsage", @key_usage.join(",")) if @key_usage
71
@extensions << @ef.create_extension("extendedKeyUsage", @ext_key_usage.join(",")) if @ext_key_usage
72
@extensions << @ef.create_extension("subjectAltName", @subject_alt_name.join(",")) if ! @subject_alt_name.empty?
74
@cert.extensions = @extensions
76
# for some reason this _must_ be the last extension added
77
@extensions << @ef.create_extension("authorityKeyIdentifier", "keyid:always,issuer:always") if @cert_type == :ca
80
# TTL for new certificates in seconds. If config param :ca_ttl is set,
81
# use that, otherwise use :ca_days for backwards compatibility
83
ttl = Puppet.settings[:ca_ttl]
85
return ttl unless ttl.is_a?(String)
87
raise ArgumentError, "Invalid ca_ttl #{ttl}" unless ttl =~ /^(\d+)(y|d|h|s)$/
89
return $1.to_i * UNITMAP[$2]
93
# Make the certificate valid as of yesterday, because
94
# so many people's clocks are out of sync.
95
from = Time.now - (60*60*24)
96
@cert.not_before = from
97
@cert.not_after = from + ttl
101
def add_ca_extensions
102
@basic_constraint = "CA:TRUE"
103
@key_usage = %w{cRLSign keyCertSign}
106
# We're a terminal CA, probably not self-signed.
107
def add_terminalsubca_extensions
108
@basic_constraint = "CA:TRUE,pathlen:0"
109
@key_usage = %w{cRLSign keyCertSign}
112
# We're a normal server.
113
def add_server_extensions
114
@basic_constraint = "CA:FALSE"
115
dnsnames = Puppet[:certdnsnames]
116
name = @name.to_s.sub(%r{/CN=},'')
118
dnsnames.split(':').each { |d| @subject_alt_name << 'DNS:' + d }
119
@subject_alt_name << 'DNS:' + name # Add the fqdn as an alias
120
elsif name == Facter.value(:fqdn) # we're a CA server, and thus probably the server
121
@subject_alt_name << 'DNS:' + "puppet" # Add 'puppet' as an alias
122
@subject_alt_name << 'DNS:' + name # Add the fqdn as an alias
123
@subject_alt_name << 'DNS:' + name.sub(/^[^.]+./, "puppet.") # add puppet.domain as an alias
125
@key_usage = %w{digitalSignature keyEncipherment}
126
@ext_key_usage = %w{serverAuth clientAuth emailProtection}
130
def add_ocsp_extensions
131
@basic_constraint = "CA:FALSE"
132
@key_usage = %w{nonRepudiation digitalSignature}
133
@ext_key_usage = %w{serverAuth OCSPSigning}
137
def add_client_extensions
138
@basic_constraint = "CA:FALSE"
139
@key_usage = %w{nonRepudiation digitalSignature keyEncipherment}
140
@ext_key_usage = %w{clientAuth emailProtection}
142
@extensions << @ef.create_extension("nsCertType", "client,email")