Class: Puppet::SSL::CertificateRequest
- Defined in:
- lib/puppet/ssl/certificate_request.rb
Overview
This class creates and manages X509 certificate signing requests.
## CSR attributes
CSRs may contain a set of attributes that includes supplementary information about the CSR or information for the signed certificate.
PKCS#9/RFC 2985 section 5.4 formally defines the “Challenge password”, “Extension request”, and “Extended-certificate attributes”, but this implementation only handles the “Extension request” attribute. Other attributes may be defined on a CSR, but the RFC doesn’t define behavior for any other attributes so we treat them as only informational.
## CSR Extension request attribute
CSRs may contain an optional set of extension requests, which allow CSRs to include additional information that may be included in the signed certificate. Any additional information that should be copied from the CSR to the signed certificate MUST be included in this attribute.
This behavior is dictated by PKCS#9/RFC 2985 section 5.4.2.
Constant Summary
Constants inherited from Base
Base::SEPARATOR, Base::VALID_CERTNAME
Instance Attribute Summary
Attributes inherited from Base
Class Method Summary collapse
-
.supported_formats ⇒ Object
Because of how the format handler class is included, this can’t be in the base class.
Instance Method Summary collapse
-
#custom_attributes ⇒ Hash<String, String>
Return all user specified attributes attached to this CSR as a hash.
- #ext_value_to_ruby_value(asn1_arr) ⇒ Object
- #extension_factory ⇒ Object
-
#generate(key, options = {}) ⇒ OpenSSL::X509::Request
Create a certificate request with our system settings.
-
#request_extensions ⇒ Array<Hash{String => String}>
Return the set of extensions requested on this CSR, in a form designed to be useful to Ruby: an array of hashes.
- #subject_alt_names ⇒ Object
Methods inherited from Base
#digest, #digest_algorithm, #fingerprint, from_instance, from_multiple_s, from_s, #initialize, name_from_subject, #read, #to_data_hash, 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
.supported_formats ⇒ Object
Because of how the format handler class is included, this can’t be in the base class.
35 36 37 |
# File 'lib/puppet/ssl/certificate_request.rb', line 35 def self.supported_formats [:s] end |
Instance Method Details
#custom_attributes ⇒ Hash<String, String>
Return all user specified attributes attached to this CSR as a hash. IF an OID has a single value it is returned as a string, otherwise all values are returned as an array.
The format of CSR attributes is specified in PKCS#10/RFC 2986
193 194 195 196 197 198 199 200 201 |
# File 'lib/puppet/ssl/certificate_request.rb', line 193 def custom_attributes x509_attributes = @content.attributes.reject do |attr| PRIVATE_CSR_ATTRIBUTES.include? attr.oid end x509_attributes.map do |attr| { "oid" => attr.oid, "value" => attr.value.value.first.value } end end |
#ext_value_to_ruby_value(asn1_arr) ⇒ Object
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 |
# File 'lib/puppet/ssl/certificate_request.rb', line 102 def ext_value_to_ruby_value(asn1_arr) # A list of ASN1 types than can't be directly converted to a Ruby type @non_convertible ||= [OpenSSL::ASN1::EndOfContent, OpenSSL::ASN1::BitString, OpenSSL::ASN1::Null, OpenSSL::ASN1::Enumerated, OpenSSL::ASN1::UTCTime, OpenSSL::ASN1::GeneralizedTime, OpenSSL::ASN1::Sequence, OpenSSL::ASN1::Set] begin # Attempt to decode the extension's DER data located in the original OctetString asn1_val = OpenSSL::ASN1.decode(asn1_arr.last.value) rescue OpenSSL::ASN1::ASN1Error # This is to allow supporting the old-style of not DER encoding trusted facts return asn1_arr.last.value end # If the extension value can not be directly converted to an atomic Ruby # type, use the original ASN1 value. This is needed to work around a bug # in Ruby's OpenSSL library which doesn't convert the value of unknown # extension OIDs properly. See PUP-3560 if @non_convertible.include?(asn1_val.class) then # Allows OpenSSL to take the ASN1 value and turn it into something Ruby understands OpenSSL::X509::Extension.new(asn1_arr.first.value, asn1_val.to_der).value else asn1_val.value end end |
#extension_factory ⇒ Object
39 40 41 42 43 |
# File 'lib/puppet/ssl/certificate_request.rb', line 39 def extension_factory # rubocop:disable Naming/MemoizedInstanceVariableName @ef ||= OpenSSL::X509::ExtensionFactory.new # rubocop:enable Naming/MemoizedInstanceVariableName end |
#generate(key, options = {}) ⇒ OpenSSL::X509::Request
Create a certificate request with our system settings.
60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 |
# File 'lib/puppet/ssl/certificate_request.rb', line 60 def generate(key, = {}) Puppet.info _("Creating a new SSL certificate request for %{name}") % { name: name } # 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 = if key.is_a?(OpenSSL::PKey::EC) # EC#public_key doesn't follow the PKey API, # see https://github.com/ruby/openssl/issues/29 key else key.public_key end if [:csr_attributes] add_csr_attributes(csr, [:csr_attributes]) end if (ext_req_attribute = extension_request_attribute()) csr.add_attribute(ext_req_attribute) 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") % { name: name } unless csr.verify(csr.public_key) @content = csr # we won't be able to get the digest on jruby if @content.signature_algorithm Puppet.info _("Certificate Request fingerprint (%{digest}): %{hex_digest}") % { digest: digest.name, hex_digest: digest.to_hex } end @content end |
#request_extensions ⇒ Array<Hash{String => String}>
Return the set of extensions requested on this CSR, in a form designed to be useful to Ruby: an array of hashes. Which, not coincidentally, you can pass successfully to the OpenSSL constructor later, if you want.
hashes, with key/value pairs for the extension’s oid, its value, and optionally its critical state.
140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 |
# File 'lib/puppet/ssl/certificate_request.rb', line 140 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. attribute = @content.attributes.find { |x| x.oid == "extReq" } attribute ||= @content.attributes.find { |x| x.oid == "msExtReq" } return [] unless attribute extensions = unpack_extension_request(attribute) index = -1 extensions.map do |ext_values| index += 1 value = ext_value_to_ruby_value(ext_values) # 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 ext_values.length when 2 { "oid" => ext_values[0].value, "value" => value } when 3 { "oid" => ext_values[0].value, "value" => value, "critical" => ext_values[1].value } else raise Puppet::Error, _("In %{attr}, expected extension record %{index} to have two or three items, but found %{count}") % { attr: attribute.oid, index: index, count: ext_values.length } end end end |
#subject_alt_names ⇒ Object
173 174 175 176 177 178 179 180 |
# File 'lib/puppet/ssl/certificate_request.rb', line 173 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 |