Class: Puppet::SSL::CertificateRequest

Inherits:
Base show all
Extended by:
Indirector
Defined in:
lib/vendor/puppet/ssl/certificate_request.rb

Overview

Manage certificate requests.

Defined Under Namespace

Modules: AutoSigner Classes: Ca, DisabledCa, File, Rest

Constant Summary

Constants included from Indirector

Indirector::BadNameRegexp

Constants inherited from Base

Base::SEPARATOR, Base::VALID_CERTNAME

Instance Attribute Summary

Attributes inherited from Base

#content, #name

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Indirector

configure_routes, indirects

Methods inherited from Base

#ca?, #fingerprint, from_multiple_s, #initialize, #read, to_multiple_s, #to_s, #to_text, validate_certname, wrapped_class, wraps

Constructor Details

This class inherits a constructor from Puppet::SSL::Base

Class Method Details

.from_s(string) ⇒ Object

Convert a string into an instance.



24
25
26
27
28
29
30
# File 'lib/vendor/puppet/ssl/certificate_request.rb', line 24

def self.from_s(string)
  instance = wrapped_class.new(string)
  name = instance.subject.to_s.sub(/\/CN=/i, '').downcase
  result = new(name)
  result.content = instance
  result
end

.supported_formatsObject

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



34
35
36
# File 'lib/vendor/puppet/ssl/certificate_request.rb', line 34

def self.supported_formats
  [:s]
end

Instance Method Details

#extension_factoryObject



38
39
40
# File 'lib/vendor/puppet/ssl/certificate_request.rb', line 38

def extension_factory
  @ef ||= OpenSSL::X509::ExtensionFactory.new
end

#generate(key, options = {}) ⇒ Object

How to create a certificate request with our system defaults.

Raises:



43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
# File 'lib/vendor/puppet/ssl/certificate_request.rb', line 43

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

  csr.sign(key, OpenSSL::Digest::MD5.new)

  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 (md5): #{fingerprint}"
  @content
end

#request_extensionsObject

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.

Raises:



83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
# File 'lib/vendor/puppet/ssl/certificate_request.rb', line 83

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_namesObject



141
142
143
144
145
146
147
148
# File 'lib/vendor/puppet/ssl/certificate_request.rb', line 141

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