Class: Chef::Knife::WindowsCertGenerate

Inherits:
Chef::Knife show all
Defined in:
lib/chef/knife/windows_cert_generate.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#hostnameObject

Returns the value of attribute hostname.



25
26
27
# File 'lib/chef/knife/windows_cert_generate.rb', line 25

def hostname
  @hostname
end

#thumbprintObject

Returns the value of attribute thumbprint.



25
26
27
# File 'lib/chef/knife/windows_cert_generate.rb', line 25

def thumbprint
  @thumbprint
end

Instance Method Details

#certificates_already_exist?(file_path) ⇒ Boolean

Returns:

  • (Boolean)


114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
# File 'lib/chef/knife/windows_cert_generate.rb', line 114

def certificates_already_exist?(file_path)
  certs_exists = false
  %w{pem pfx b64}.each do |extn|
    unless Dir.glob("#{file_path}.*#{extn}").empty?
      certs_exists = true
      break
    end
  end

  if certs_exists
    begin
      confirm("Do you really want to overwrite existing certificates")
    rescue SystemExit # Need to handle this as confirming with N/n raises SystemExit exception
      exit!
    end
  end
end

#generate_certificate(rsa_key) ⇒ Object



82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
# File 'lib/chef/knife/windows_cert_generate.rb', line 82

def generate_certificate(rsa_key)
  @hostname = config[:hostname] if config[:hostname]

  # Create a self-signed X509 certificate from the rsa_key (unencrypted)
  cert = OpenSSL::X509::Certificate.new
  cert.version = 2
  cert.serial = Random.rand(65534) + 1 # 2 digit byte range random number for better security aspect

  cert.subject = OpenSSL::X509::Name.parse "/CN=#{@hostname}"
  cert.issuer = cert.subject
  cert.public_key = rsa_key.public_key
  cert.not_before = Time.now
  cert.not_after = cert.not_before + 2 * 365 * config[:cert_validity].to_i * 60 * 60 # 2 years validity
  ef = OpenSSL::X509::ExtensionFactory.new
  ef.subject_certificate = cert
  ef.issuer_certificate = cert
  cert.add_extension(ef.create_extension("subjectKeyIdentifier", "hash", false))
  cert.add_extension(ef.create_extension("authorityKeyIdentifier", "keyid:always", false))
  cert.add_extension(ef.create_extension("extendedKeyUsage", "1.3.6.1.5.5.7.3.1", false))
  cert.sign(rsa_key, OpenSSL::Digest.new("SHA1"))
  @thumbprint = OpenSSL::Digest::SHA1.new(cert.to_der)
  cert
end

#generate_keypairObject



64
65
66
# File 'lib/chef/knife/windows_cert_generate.rb', line 64

def generate_keypair
  OpenSSL::PKey::RSA.new(config[:key_length].to_i)
end

#prompt_for_passphraseObject



68
69
70
71
72
73
74
75
76
77
78
79
80
# File 'lib/chef/knife/windows_cert_generate.rb', line 68

def prompt_for_passphrase
  passphrase = ""
  begin
    print "Passphrases do not match.  Try again.\n" unless passphrase.empty?
    print "Enter certificate passphrase (empty for no passphrase):"
    passphrase = STDIN.gets
    return passphrase.strip if passphrase == "\n"

    print "Enter same passphrase again:"
    confirm_passphrase = STDIN.gets
  end until passphrase == confirm_passphrase
  passphrase.strip
end

#runObject



132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
# File 'lib/chef/knife/windows_cert_generate.rb', line 132

def run
  STDOUT.sync = STDERR.sync = true

  # takes user specified first cli value as a destination file path for generated cert.
  file_path = @name_args.empty? ? config[:output_file].sub(/\.(\w+)$/, "") : @name_args.first

  # check if certs already exists at given file path
  certificates_already_exist? file_path

  begin
    filename = File.basename(file_path)
    rsa_key = generate_keypair
    cert = generate_certificate rsa_key
    write_certificate_to_file cert, file_path, rsa_key
    ui.info "Generated Certificates:"
    ui.info "- #{filename}.pfx - PKCS12 format key pair. Contains public and private keys, can be used with an SSL server."
    ui.info "- #{filename}.b64 - Base64 encoded PKCS12 key pair. Contains public and private keys, used by some cloud provider API's to configure SSL servers."
    ui.info "- #{filename}.pem - Base64 encoded public certificate only. Required by the client to connect to the server."
    ui.info "Certificate Thumbprint: #{@thumbprint.to_s.upcase}"
  rescue => e
    puts "ERROR: + #{e}"
  end
end

#write_certificate_to_file(cert, file_path, rsa_key) ⇒ Object



106
107
108
109
110
111
112
# File 'lib/chef/knife/windows_cert_generate.rb', line 106

def write_certificate_to_file(cert, file_path, rsa_key)
  File.open(file_path + ".pem", "wb") { |f| f.print cert.to_pem }
  config[:cert_passphrase] = prompt_for_passphrase unless config[:cert_passphrase]
  pfx = OpenSSL::PKCS12.create("#{config[:cert_passphrase]}", "winrmcert", rsa_key, cert)
  File.open(file_path + ".pfx", "wb") { |f| f.print pfx.to_der }
  File.open(file_path + ".b64", "wb") { |f| f.print Base64.strict_encode64(pfx.to_der) }
end