# 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