Class: R509::CertificateAuthority::Signer

Inherits:
Object
  • Object
show all
Defined in:
lib/r509/certificate_authority/signer.rb

Overview

Contains the certification authority signing operation methods

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(config) ⇒ Signer

Returns a new instance of Signer

Parameters:



12
13
14
15
16
17
18
19
20
21
# File 'lib/r509/certificate_authority/signer.rb', line 12

def initialize(config)
  @config = config

  if @config && !@config.is_a?(R509::Config::CAConfig)
    raise R509::R509Error, "config must be a kind of R509::Config::CAConfig"
  end
  if @config && !@config.ca_cert.has_private_key?
    raise R509::R509Error, "You must have a private key associated with your CA certificate to issue"
  end
end

Class Method Details

.build_cert(options) ⇒ Object



119
120
121
122
123
124
125
126
127
128
129
130
# File 'lib/r509/certificate_authority/signer.rb', line 119

def self.build_cert(options)
  cert = OpenSSL::X509::Certificate.new

  cert.subject = options[:subject]
  cert.issuer = options[:issuer]
  cert.not_before = calculate_not_before(options[:not_before])
  cert.not_after = calculate_not_after(options[:not_after], cert.not_before)
  cert.public_key = options[:public_key]
  cert.serial = create_serial(options[:serial])
  cert.version = 2 # 2 means v3
  cert
end

.check_options(options) ⇒ Object



107
108
109
110
111
112
113
114
115
116
117
# File 'lib/r509/certificate_authority/signer.rb', line 107

def self.check_options(options)
  if options.key?(:csr) && options.key?(:spki)
    raise ArgumentError, "You can't pass both :csr and :spki"
  elsif !options.key?(:csr) && !options.key?(:spki)
    raise ArgumentError, "You must supply either :csr or :spki"
  elsif options.key?(:csr) && !options[:csr].is_a?(R509::CSR)
    raise ArgumentError, "You must pass an R509::CSR object for :csr"
  elsif options.key?(:spki) && !options[:spki].is_a?(R509::SPKI)
    raise ArgumentError, "You must pass an R509::SPKI object for :spki"
  end
end

.extract_public_key_subject(options) ⇒ Object



172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
# File 'lib/r509/certificate_authority/signer.rb', line 172

def self.extract_public_key_subject(options)
  if options.key?(:csr)
    subject = (options.key?(:subject)) ? R509::Subject.new(options[:subject]) : options[:csr].subject
    public_key = options[:csr].public_key
  else
    # spki
    unless options.key?(:subject)
      raise ArgumentError, "You must supply :subject when passing :spki"
    end
    public_key = options[:spki].public_key
    subject = R509::Subject.new(options[:subject])
  end

  [subject, public_key]
end

.selfsign(options) ⇒ R509::Cert

Self-signs a CSR

Parameters:

  • options (Hash)

    a customizable set of options

Options Hash (options):

  • :csr (R509::CSR)
  • :message_digest (String)

    the message digest to use for this certificate (defaults to R509::MessageDigest::DEFAULT_MD)

  • :serial (String) — default: random serial

    the serial number you want to issue the certificate with

  • :extensions (Array)

    An array of R509::Cert::Extensions::* objects that represent the extensions you want to embed in the final certificate

  • :not_before (Time) — default: Time.now - 6 hours

    the notBefore for the certificate

  • :not_after (Time) — default: Time.now + 365 days

    the notAfter for the certificate

Returns:



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
101
102
103
104
105
# File 'lib/r509/certificate_authority/signer.rb', line 70

def self.selfsign(options)
  unless options.is_a?(Hash)
    raise ArgumentError, "You must pass a hash of options consisting of at minimum :csr"
  end
  csr = options[:csr]
  if csr.key.nil?
    raise ArgumentError, 'CSR must also have a private key to self sign'
  end

  subject, public_key = R509::CertificateAuthority::Signer.extract_public_key_subject(options)

  cert = self.build_cert(
    :subject => subject.name,
    :issuer => subject.name,
    :not_before => options[:not_before],
    :not_after => options[:not_after],
    :public_key => public_key,
    :serial => options[:serial]
  )

  cert.extensions = options[:extensions] || [
    R509::Cert::Extensions::BasicConstraints.new(:ca => true),
    R509::Cert::Extensions::SubjectKeyIdentifier.new(:public_key => public_key),
    R509::Cert::Extensions::AuthorityKeyIdentifier.new(:public_key => public_key)
  ]

  if options.key?(:message_digest)
    message_digest = R509::MessageDigest.new(options[:message_digest])
  else
    message_digest = R509::MessageDigest.new(R509::MessageDigest::DEFAULT_MD)
  end

  cert.sign(csr.key.key, message_digest.digest)

  R509::Cert.new(:cert => cert, :key => csr.key)
end

Instance Method Details

#sign(options) ⇒ R509::Cert

Signs a CSR

Parameters:

  • options (Hash)

    a customizable set of options

Options Hash (options):

  • :csr (R509::CSR)
  • :spki (R509::SPKI)
  • :subject (R509::Subject, OpenSSL::X509::Subject, Array)

    This is optional when passing a :csr but required for :spki

  • :message_digest (String)

    the message digest to use for this certificate instead of the default (see R509::MessageDigest::DEFAULT_MD).

  • :serial (String) — default: random serial

    the serial number you want to issue the certificate with

  • :extensions (Array)

    An array of R509::Cert::Extensions::* objects that represent the extensions you want to embed in the final certificate

  • :not_before (Time) — default: Time.now - 6 hours

    the notBefore for the certificate

  • :not_after (Time) — default: Time.now + 365 days

    the notAfter for the certificate

Returns:



33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
# File 'lib/r509/certificate_authority/signer.rb', line 33

def sign(options)
  R509::CertificateAuthority::Signer.check_options(options)

  message_digest = R509::MessageDigest.new(options[:message_digest])

  subject, public_key = R509::CertificateAuthority::Signer.extract_public_key_subject(options)

  cert = R509::CertificateAuthority::Signer.build_cert(
    :subject => subject.name,
    :issuer => @config.ca_cert.subject.name,
    :not_before => options[:not_before],
    :not_after => options[:not_after],
    :public_key => public_key,
    :serial => options[:serial]
  )

  cert.extensions = options[:extensions] || [
    R509::Cert::Extensions::SubjectKeyIdentifier.new(:public_key => public_key),
    R509::Cert::Extensions::AuthorityKeyIdentifier.new(:public_key => @config.ca_cert.public_key)
  ]

  # @config.ca_cert.key.key ... ugly. ca_cert returns R509::Cert
  # #key returns R509::PrivateKey and #key on that returns OpenSSL object we need
  cert.sign(@config.ca_cert.key.key, message_digest.digest)
  cert_opts = { :cert => cert }
  cert_opts[:key] = options[:csr].key if options[:csr] && options[:csr].key
  R509::Cert.new(cert_opts)
end