class CertificateRequest

Manage certificate requests.

Public Class Methods

supported_formats() click to toggle source

Because of how the format handler class is included, this can’t be in the base class.

# File lib/puppet/ssl/certificate_request.rb, line 26
def self.supported_formats
  [:s]
end

Public Instance Methods

extension_factory() click to toggle source
# File lib/puppet/ssl/certificate_request.rb, line 30
def extension_factory
  @ef ||= OpenSSL::X509::ExtensionFactory.new
end
generate(key, options = {}) click to toggle source

How to create a certificate request with our system defaults.

# File lib/puppet/ssl/certificate_request.rb, line 35
def generate(key, options = {})
  Puppet.info "Creating a new SSL certificate request for #{name}"

  # Support either an actual SSL key, or a Puppet key.
  key = key.content if key.is_a?(Puppet::SSL::Key)

  # If we're a CSR for the CA, then use the real ca_name, rather than the
  # fake 'ca' name.  This is mostly for backward compatibility with 0.24.x,
  # but it's also just a good idea.
  common_name = name == Puppet::SSL::CA_NAME ? Puppet.settings[:ca_name] : name

  csr = OpenSSL::X509::Request.new
  csr.version = 0
  csr.subject = OpenSSL::X509::Name.new([["CN", common_name]])
  csr.public_key = key.public_key

  if options[:dns_alt_names] then
    names = options[:dns_alt_names].split(/\s*,\s*/).map(&:strip) + [name]
    names = names.sort.uniq.map {|name| "DNS:#{name}" }.join(", ")
    names = extension_factory.create_extension("subjectAltName", names, false)

    extReq = OpenSSL::ASN1::Set([OpenSSL::ASN1::Sequence([names])])

    # We only support the standard request extensions.  If you really need
    # msExtReq support, let us know and we can restore them. --daniel 2011-10-10
    csr.add_attribute(OpenSSL::X509::Attribute.new("extReq", extReq))
  end

  signer = Puppet::SSL::CertificateSigner.new
  signer.sign(csr, key)

  raise Puppet::Error, "CSR sign verification failed; you need to clean the certificate request for #{name} on the server" unless csr.verify(key.public_key)

  @content = csr
  Puppet.info "Certificate Request fingerprint (#{digest.name}): #{digest.to_hex}"
  @content
end
request_extensions() click to toggle source

Return the set of extensions requested on this CSR, in a form designed to be useful to Ruby: a hash. Which, not coincidentally, you can pass successfully to the OpenSSL constructor later, if you want.

# File lib/puppet/ssl/certificate_request.rb, line 76
def request_extensions
  raise Puppet::Error, "CSR needs content to extract fields" unless @content

  # Prefer the standard extReq, but accept the Microsoft specific version as
  # a fallback, if the standard version isn't found.
  ext = @content.attributes.find {|x| x.oid == "extReq" } or
    @content.attributes.find {|x| x.oid == "msExtReq" }
  return [] unless ext

  # Assert the structure and extract the names into an array of arrays.
  unless ext.value.is_a? OpenSSL::ASN1::Set
    raise Puppet::Error, "In #{ext.oid}, expected Set but found #{ext.value.class}"
  end

  unless ext.value.value.is_a? Array
    raise Puppet::Error, "In #{ext.oid}, expected Set[Array] but found #{ext.value.value.class}"
  end

  unless ext.value.value.length == 1
    raise Puppet::Error, "In #{ext.oid}, expected Set[Array[...]], but found #{ext.value.value.length} items in the array"
  end

  san = ext.value.value.first
  unless san.is_a? OpenSSL::ASN1::Sequence
    raise Puppet::Error, "In #{ext.oid}, expected Set[Array[Sequence[...]]], but found #{san.class}"
  end
  san = san.value

  # OK, now san should be the array of items, validate that...
  index = -1
  san.map do |name|
    index += 1

    unless name.is_a? OpenSSL::ASN1::Sequence
      raise Puppet::Error, "In #{ext.oid}, expected request extension record #{index} to be a Sequence, but found #{name.class}"
    end
    name = name.value

    # OK, turn that into an extension, to unpack the content.  Lovely that
    # we have to swap the order of arguments to the underlying method, or
    # perhaps that the ASN.1 representation chose to pack them in a
    # strange order where the optional component comes *earlier* than the
    # fixed component in the sequence.
    case name.length
    when 2
      ev = OpenSSL::X509::Extension.new(name[0].value, name[1].value)
      { "oid" => ev.oid, "value" => ev.value }

    when 3
      ev = OpenSSL::X509::Extension.new(name[0].value, name[2].value, name[1].value)
      { "oid" => ev.oid, "value" => ev.value, "critical" => ev.critical? }

    else
      raise Puppet::Error, "In #{ext.oid}, expected extension record #{index} to have two or three items, but found #{name.length}"
    end
  end.flatten
end
subject_alt_names() click to toggle source
# File lib/puppet/ssl/certificate_request.rb, line 134
def subject_alt_names
  @subject_alt_names ||= request_extensions.
    select {|x| x["oid"] = "subjectAltName" }.
    map {|x| x["value"].split(/\s*,\s*/) }.
    flatten.
    sort.
    uniq
end