SSLKeyAndCSRCreator

Problem this snippet solves:

This is a script will generate a private key and output a certificate signing request. This is all done from a workstation command line and communicated to the BIG-IP via iControl.

How to use this snippet:

Requirements

Installation Steps

  1. Install Ruby, Ruby's ""OpenSSL"" library, Ruby Gems, and the Ruby iControl libraries
  2. Copy this code to /usr/local/bin and chmod +x to make the script executable
  3. Run it!
    • minimal options: ssl-key-and-csr-creator.rb -b 192.168.1.245 -u admin -i test-key-001
    • full options: ssl-key-and-csr-creator.rb -b 192.168.1.245 -u admin -p admin -i test-key-001 -t RSA -l 4096 -s fips --common-name=www.example.com --country=US --state=Washington --locality=Seattle --organization="Example Company, Inc." --division="Information Technology" -o /my-ssl-directory/ -k -c

Code :

#!/usr/bin/ruby

require "rubygems"
require "f5-icontrol"
require "getoptlong"

options = GetoptLong.new(
  [ "--bigip-address",    "-b",   GetoptLong::REQUIRED_ARGUMENT ],
  [ "--bigip-user",       "-u",   GetoptLong::REQUIRED_ARGUMENT ],
  [ "--bigip-pass",       "-p",   GetoptLong::REQUIRED_ARGUMENT ],
  [ "--key-id",           "-i",   GetoptLong::REQUIRED_ARGUMENT ],
  [ "--key-type",         "-t",   GetoptLong::OPTIONAL_ARGUMENT ],
  [ "--key-bit-length",   "-l",   GetoptLong::OPTIONAL_ARGUMENT ],
  [ "--key-security",     "-s",   GetoptLong::OPTIONAL_ARGUMENT ],
  [ "--common-name",              GetoptLong::OPTIONAL_ARGUMENT ],
  [ "--country",                  GetoptLong::OPTIONAL_ARGUMENT ],
  [ "--state",                    GetoptLong::OPTIONAL_ARGUMENT ],
  [ "--locality",                 GetoptLong::OPTIONAL_ARGUMENT ],
  [ "--organization",             GetoptLong::OPTIONAL_ARGUMENT ],
  [ "--division",                 GetoptLong::OPTIONAL_ARGUMENT ],
  [ "--output-dir",       "-o",   GetoptLong::OPTIONAL_ARGUMENT ],
  [ "--key-output",       "-k",   GetoptLong::NO_ARGUMENT ],
  [ "--csr-output",       "-c",   GetoptLong::NO_ARGUMENT ],
  [ "--help",             "-h",   GetoptLong::NO_ARGUMENT ]
)

def usage 
  puts $0 + " -b  -u  -i "
  puts
  puts "BIG-IP connection parameters"
  puts "-" * 20
  puts "  -b  (--bigip-address)   BIG-IP management-accessible address"
  puts "  -u  (--bigip-user)      BIG-IP username"
  puts "  -p  (--bigip-pass)      BIG-IP password (will prompt if left blank"
  puts
  puts "Private key parameters"
  puts "-" * 20
  puts "  -i  (--key-id)          key ID: must be unique and should be indicative of the purpose (required)"
  puts "  -t  (--key-type)        key type: [RSA|DSA] (default is 'RSA')"
  puts "  -l  (--key-bit-length)  key bit length: should be a minimum of 1024-bit (default is 2048; most CAs won't sign weaker keys)"
  puts "  -s  (--key-security)    key security: [normal|fips|password] (default is 'normal' with no passphrase)"
  puts
  puts "X.509 data parameters (if blank, you'll be prompted for the answers)"
  puts "-" * 20
  puts "      (--common-name)     common name: FQDN for virtual server (www.example.com)"
  puts "      (--country)         country: two letter country abbreviation (US, CN, etc.)"
  puts "      (--state)           state: two letter state abbreviation (WA, OR, CA, etc.)"
  puts "      (--locality)        locality: locality or city name (Seattle, Portland, etc.)"
  puts "      (--organization)    organization: organization or company name (F5 Networks, Company XYZ, etc.)"
  puts "      (--division)        division: department or division name (IT, HR, Finance, etc.)"
  puts
  puts "Output options"
  puts "-" * 20
  puts "  -o  (--output-dir)      CSR/key output directory: location to output private key and CSR files (defaults to current working directory)"
  puts "  -k  (--key-output)      key output: save private key to a local file (saved as key_id.key)"
  puts "  -c  (--csr-output)      CSR output: save certificate signing request to a local file (saved as key_id.csr)"
  puts
  puts "Help and usage"
  puts "-" * 20
  puts "  -h  (--help)            shows this help/usage dialog"
  puts

  exit
end

# set STDOUT buffer to synchronous
STDOUT.sync = true

# global variables
KEY_TYPES = { "RSA" => "KTYPE_RSA_PRIVATE", "DSA" => "KTYPE_DSA_PRIVATE" }
KEY_SECURITIES = { "normal" => "STYPE_NORMAL", "fips" => "STYPE_FIPS", "password" => "STYPE_PASSWORD" }

# initial parameter values
overwrite_key = false

# key/CSR default output file values
key_output = false
csr_output = false
output_dir = Dir.pwd

# BIG-IP connection parameters
bigip = {}
bigip['address'] = ''
bigip['user'] = ''
bigip['pass'] = ''

# private key parameters
key_data = {}
key_data['id'] = ''
key_data['key_type'] = KEY_TYPES["RSA"]
key_data['bit_length'] = 2048
key_data['security'] = KEY_SECURITIES["normal"]

# X.509 data parameters
x509_data = {}
x509_data['common_name'] = ''
x509_data['country_name'] = ''
x509_data['state_name'] = ''
x509_data['locality_name'] = ''
x509_data['organization_name'] = ''
x509_data['division_name'] = ''

# loop through command line options
options.each do |option, arg|
  case option
    when "--bigip-address"
      bigip['address'] = arg
    when "--bigip-user"
      bigip['user'] = arg
    when "--bigip-pass"
      bigip['pass'] = arg
    when "--key-id"
      key_data['id'] = arg
    when "--key-type"
      if KEY_TYPES.keys.include? arg.upcase
        key_data['key_type'] = KEY_TYPES[arg.upcase]
      else
        puts "Error: Invalid key type. Exiting."
        exit 1
      end
    when "--key-bit-length"
      key_data['bit_length'] = arg.to_i
    when "--key-security"
      if KEY_SECURITIES.keys.include? arg.downcase
        key_data['security'] = KEY_SECURITIES[arg.downcase]
      else
        puts "Error: Invalid key security type. Exiting."
        exit 1
      end
    when "--common-name"
      x509_data['common_name'] = arg
    when "--country"
      if arg =~ /[a-z]{2}/i
        x509_data['country_name'] = arg.upcase
      else
        puts "Error: Use exactly two letters for the country code. Exiting."
        exit 1
      end
    when "--state"
      x509_data['state_name'] = arg
    when "--locality"
      x509_data['locality_name'] = arg
    when "--organization"
      x509_data['organization_name'] = arg
    when "--division"
      x509_data['division_name'] = arg
    when "--output-dir"
      if File.directory? arg
        output_dir = arg
      else
        puts "Error: Invalid directory for output. Exiting."
      end
    when "--key-output"
      key_output = true
    when "--csr-output"
      csr_output = true
    when "--help"
      usage
  end
end

# we need at least the BIG-IP's address, user, and a key ID to proceed

usage if bigip['address'].empty? or bigip['user'].empty? or key_data['id'].empty?

if bigip['pass'].empty?
  puts "Please enter the BIG-IPs password..."
  print "Password: "
  system("stty", "-echo")
  bigip['pass'] = gets.chomp
  system("stty", "echo")
  puts
end

# set up connection to BIG-IP and Management.KeyCertificate interface

bigip = F5::IControl.new(bigip['address'], bigip['user'], bigip['pass'], ["Management.KeyCertificate"]).get_interfaces

#grab a list of existing keys and confirm overwrite if a conflict exists

existing_keys = bigip["Management.KeyCertificate"].get_key_list('MANAGEMENT_MODE_DEFAULT').collect { |key| key["key_info"]["id"] }

if existing_keys.include? key_data['id']
  print "A key with an ID of '#{key_data['id']}' already exists. Overwrite it? (yes/no) "
  answer = gets.chomp

  if answer !~ /^yes$/i
    puts "Will not overwrite existing key. Exiting."
    exit
  else
    overwrite_key = true
  end
end

# time to play 20 questions with the X.509 data

if x509_data.values.delete_if { |value| !value.empty? }.size > 0
  puts "Please fill in the following X.509 data parameters..."
end

x509_data.sort.each do |key, value| 
  if value.empty?
    print key.capitalize.gsub('_', ' ') + "? "
    x509_data[key] = gets.chomp
  end
end

bigip["Management.KeyCertificate"].key_generate('MANAGEMENT_MODE_DEFAULT', [key_data], [x509_data], true, overwrite_key)

# write private key to local file if specified by user

if key_output
  key_output_file = output_dir + "/" + key_data['id'] + ".key"
  key = bigip["Management.KeyCertificate"].key_export_to_pem('MANAGEMENT_MODE_DEFAULT', [key_data['id']])[0]
  File.open(key_output_file, 'w') { |file| file.write(key) }
end

# display subject information for CSR as well as the CSR

puts "Certificate Request"
puts "-" * 20
puts "Subject: C=#{x509_data['country_name']}, ST=#{x509_data['state_name']}, L=#{x509_data['locality_name']}, O=#{x509_data['organization_name']}, OU=#{x509_data['division_name']}, CN=#{x509_data['common_name']}"

csr = bigip["Management.KeyCertificate"].certificate_request_export_to_pem('MANAGEMENT_MODE_DEFAULT', [key_data['id']])

# write csr key to local file if specified by user

if csr_output
  csr_output_file = output_dir + "/" + key_data['id'] + ".csr"
  File.open(csr_output_file, 'w') { |file| file.write(csr) }
end

puts
puts csr
Published Mar 09, 2015
Version 1.0

Was this article helpful?

No CommentsBe the first to comment