class Acme::Client::CertificateRequest

Constants

DEFAULT_DIGEST
DEFAULT_KEY_LENGTH
SUBJECT_KEYS
SUBJECT_TYPES

Attributes

common_name[R]
names[R]
private_key[R]
subject[R]

Public Class Methods

new(common_name: nil, names: [], private_key: generate_private_key, subject: {}, digest: DEFAULT_DIGEST.new) click to toggle source
# File lib/acme/client/certificate_request.rb, line 28
def initialize(common_name: nil, names: [], private_key: generate_private_key, subject: {}, digest: DEFAULT_DIGEST.new)
  @digest = digest
  @private_key = private_key
  @subject = normalize_subject(subject)
  @common_name = common_name || @subject[SUBJECT_KEYS[:common_name]] || @subject[:common_name]
  @names = names.to_a.dup
  normalize_names
  @subject[SUBJECT_KEYS[:common_name]] ||= @common_name
  validate_subject
end

Public Instance Methods

csr() click to toggle source
# File lib/acme/client/certificate_request.rb, line 39
def csr
  @csr ||= generate
end

Private Instance Methods

add_extension(csr) click to toggle source
# File lib/acme/client/certificate_request.rb, line 106
def add_extension(csr)
  extension = OpenSSL::X509::ExtensionFactory.new.create_extension(
    'subjectAltName', @names.map { |name| "DNS:#{name}" }.join(', '), false
  )
  csr.add_attribute(
    OpenSSL::X509::Attribute.new(
      'extReq',
      OpenSSL::ASN1::Set.new([OpenSSL::ASN1::Sequence.new([extension])])
    )
  )
end
generate() click to toggle source
# File lib/acme/client/certificate_request.rb, line 80
def generate
  OpenSSL::X509::Request.new.tap do |csr|
    if @private_key.is_a?(OpenSSL::PKey::EC) && RbConfig::CONFIG['MAJOR'] == '2' &&
       RbConfig::CONFIG['MINOR'].to_i < 4
      # OpenSSL::PKey::EC does not respect classic PKey interface (as defined by
      # PKey::RSA and PKey::DSA) until ruby 2.4.
      # Supporting this interface needs monkey patching of OpenSSL:PKey::EC, or
      # subclassing it. Here, use a subclass.
      @private_key = ECKeyPatch.new(@private_key)
    end
    csr.public_key = @private_key
    csr.subject = generate_subject
    csr.version = 0
    add_extension(csr)
    csr.sign @private_key, @digest
  end
end
generate_private_key() click to toggle source
# File lib/acme/client/certificate_request.rb, line 45
def generate_private_key
  OpenSSL::PKey::RSA.new(DEFAULT_KEY_LENGTH)
end
generate_subject() click to toggle source
# File lib/acme/client/certificate_request.rb, line 98
def generate_subject
  OpenSSL::X509::Name.new(
    @subject.map {|name, value|
      [name, value, SUBJECT_TYPES[name]]
    }
  )
end
normalize_names() click to toggle source
# File lib/acme/client/certificate_request.rb, line 55
def normalize_names
  if @common_name
    @names.unshift(@common_name) unless @names.include?(@common_name)
  else
    raise ArgumentError, 'No common name and no list of names given' if @names.empty?
    @common_name = @names.first
  end
end
normalize_subject(subject) click to toggle source
# File lib/acme/client/certificate_request.rb, line 49
def normalize_subject(subject)
  @subject = subject.each_with_object({}) do |(key, value), hash|
    hash[SUBJECT_KEYS.fetch(key, key)] = value.to_s
  end
end
validate_subject() click to toggle source
# File lib/acme/client/certificate_request.rb, line 64
def validate_subject
  validate_subject_attributes
  validate_subject_common_name
end
validate_subject_attributes() click to toggle source
# File lib/acme/client/certificate_request.rb, line 69
def validate_subject_attributes
  extra_keys = @subject.keys - SUBJECT_KEYS.keys - SUBJECT_KEYS.values
  return if extra_keys.empty?
  raise ArgumentError, "Unexpected subject attributes given: #{extra_keys.inspect}"
end
validate_subject_common_name() click to toggle source
# File lib/acme/client/certificate_request.rb, line 75
def validate_subject_common_name
  return if @common_name == @subject[SUBJECT_KEYS[:common_name]]
  raise ArgumentError, 'Conflicting common name given in arguments and subject'
end