Class: Acmesmith::Certificate

Inherits:
Object
  • Object
show all
Defined in:
lib/acmesmith/certificate.rb

Defined Under Namespace

Classes: PassphraseRequired

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(certificate, chain, private_key, key_passphrase = nil, csr = nil) ⇒ Certificate

Returns a new instance of Certificate.



16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
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
61
62
63
64
65
66
67
68
69
70
71
72
73
# File 'lib/acmesmith/certificate.rb', line 16

def initialize(certificate, chain, private_key, key_passphrase = nil, csr = nil)
  @certificate = case certificate
                 when OpenSSL::X509::Certificate
                   certificate
                 when String
                   OpenSSL::X509::Certificate.new(certificate)
                 else
                    raise TypeError, 'certificate is expected to be a String or OpenSSL::X509::Certificate'
                 end
  chain = case chain
          when String
            self.class.split_pems(chain)
          when Array
            chain
          when nil
            []
          else
            raise TypeError, 'chain is expected to be an Array<String or OpenSSL::X509::Certificate> or nil'
          end

  @chain = chain.map { |cert|
    case cert
    when OpenSSL::X509::Certificate
      cert
    when String
      OpenSSL::X509::Certificate.new(cert)
    else
      raise TypeError, 'chain is expected to be an Array<String or OpenSSL::X509::Certificate> or nil'
    end
  }

  case private_key
  when String
    @raw_private_key = private_key
    if key_passphrase
      self.key_passphrase = key_passphrase 
    else
      begin
        @private_key = OpenSSL::PKey::RSA.new(@raw_private_key) { nil }
      rescue OpenSSL::PKey::RSAError
        # may be encrypted
      end
    end
  when OpenSSL::PKey::RSA
    @private_key = private_key
  else
    raise TypeError, 'private_key is expected to be a String or OpenSSL::PKey::RSA'
  end

  @csr = case csr
         when nil
           nil
         when String
           OpenSSL::X509::Request.new(csr)
         when OpenSSL::X509::Request
           csr
         end
end

Instance Attribute Details

#certificateObject (readonly)

Returns the value of attribute certificate.



75
76
77
# File 'lib/acmesmith/certificate.rb', line 75

def certificate
  @certificate
end

#chainObject (readonly)

Returns the value of attribute chain.



75
76
77
# File 'lib/acmesmith/certificate.rb', line 75

def chain
  @chain
end

#csrObject (readonly)

Returns the value of attribute csr.



75
76
77
# File 'lib/acmesmith/certificate.rb', line 75

def csr
  @csr
end

Class Method Details

.by_issuance(pem_chain, csr) ⇒ Object



11
12
13
14
# File 'lib/acmesmith/certificate.rb', line 11

def self.by_issuance(pem_chain, csr)
  pems = split_pems(pem_chain)
  new(*pems, csr.private_key, nil, csr)
end

.split_pems(pems) ⇒ Object



7
8
9
# File 'lib/acmesmith/certificate.rb', line 7

def self.split_pems(pems)
  pems.each_line.slice_before(/^-----BEGIN CERTIFICATE-----$/).map(&:join)
end

Instance Method Details

#common_nameObject



99
100
101
# File 'lib/acmesmith/certificate.rb', line 99

def common_name
  certificate.subject.to_a.assoc('CN')[1]
end

#export(passphrase, cipher: OpenSSL::Cipher.new('aes-256-cbc')) ⇒ Object



117
118
119
120
121
122
123
124
125
126
127
128
129
# File 'lib/acmesmith/certificate.rb', line 117

def export(passphrase, cipher: OpenSSL::Cipher.new('aes-256-cbc'))
  {}.tap do |h|
    h[:certificate] = certificate.to_pem
    h[:chain] = issuer_pems
    h[:fullchain] = fullchain

    h[:private_key] = if passphrase
      private_key.export(cipher, passphrase)
    else
      private_key.export
    end
  end
end

#fullchainObject



91
92
93
# File 'lib/acmesmith/certificate.rb', line 91

def fullchain
  "#{certificate.to_pem}\n#{issuer_pems}".gsub(/\n+/,?\n)
end

#issuer_pemsObject



95
96
97
# File 'lib/acmesmith/certificate.rb', line 95

def issuer_pems
  chain.map(&:to_pem).join("\n")
end

#key_passphrase=(pw) ⇒ Object



77
78
79
80
81
82
83
84
# File 'lib/acmesmith/certificate.rb', line 77

def key_passphrase=(pw)
  raise 'private_key already given' if @private_key

  @private_key = OpenSSL::PKey::RSA.new(@raw_private_key, pw)

  @raw_private_key = nil
  nil
end

#pkcs12(passphrase) ⇒ Object



113
114
115
# File 'lib/acmesmith/certificate.rb', line 113

def pkcs12(passphrase)
  OpenSSL::PKCS12.create(passphrase, common_name, private_key, certificate)
end

#private_keyObject

Raises:



86
87
88
89
# File 'lib/acmesmith/certificate.rb', line 86

def private_key
  return @private_key if @private_key
  raise PassphraseRequired, 'key_passphrase required'
end

#sansObject



103
104
105
106
107
# File 'lib/acmesmith/certificate.rb', line 103

def sans
  certificate.extensions.select { |_| _.oid == 'subjectAltName' }.flat_map do |ext|
    ext.value.split(/,\s*/).select { |_| _.start_with?('DNS:') }.map { |_| _[4..-1] }
  end
end

#versionObject



109
110
111
# File 'lib/acmesmith/certificate.rb', line 109

def version
  "#{certificate.not_before.utc.strftime('%Y%m%d-%H%M%S')}_#{certificate.serial.to_i.to_s(16)}"
end