Credit where credit is due: The final steps that worked were based on this message from the openssl-users list and the command to generate the certificate from this page in the Linode Library.
First, save the following in a config file, for this example I'll call it example.conf and it will be for various similar domains:
[ ca ]
default_ca = CA_default
[ CA_default ]
dir = .
serial = $dir/serial
database = $dir/index.txt
new_certs_dir = $dir/newcerts
certs = $dir/certs
certificate = $certs/cacert.pem
private_key = $dir/private/cakey.pem
default_days = 365
default_md = sha1
preserve = no
email_in_dn = no
nameopt = default_ca
certopt = default_ca
policy = policy_match
copy_extensions = copy
[ policy_match ]
countryName = match
stateOrProvinceName = match
organizationName = match
organizationalUnitName = optional
commonName = supplied
emailAddress = optional
[ req ]
default_bits = 2048 # Size of keys
default_keyfile = example.key # name of generated keys
default_md = sha1 # message digest algorithm
string_mask = nombstr # permitted characters
distinguished_name = req_distinguished_name
req_extensions = v3_req
x509_extensions = v3_req
[ req_distinguished_name ]
# Variable name Prompt string
0.organizationName = Organization Name (company)
organizationalUnitName = Organizational Unit Name (department, division)
emailAddress = Email Address
emailAddress_max = 40
localityName = Locality Name (city, district)
stateOrProvinceName = State or Province Name (full name)
countryName = Country Name (2 letter code)
countryName_min = 2
countryName_max = 2
commonName = Common Name (hostname, IP, or your name)
commonName_max = 64
# Default values for the above, for consistency and less typing.
# Variable name Value
commonName_default = www.example.com
0.organizationName_default = Example Company
localityName_default = Honolulu
stateOrProvinceName_default = Hawaii
countryName_default = US
emailAddress_default = email@example.com
[ v3_ca ]
basicConstraints = CA:TRUE
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always,issuer:always
[ v3_req ]
# Extensions to add to a certificate request
basicConstraints = CA:FALSE
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
# Some CAs do not yet support subjectAltName in CSRs.
# Instead the additional names are form entries on web
# pages where one requests the certificate...
subjectAltName = @alt_names
DNS.1 = www.example.com
DNS.2 = www2.example.com
DNS.3 = www.example.net
DNS.4 = example.com
[ server ]
# Make a cert with nsCertType set to "server"
nsCertType = server
nsComment = "OpenSSL Generated Server Certificate"
[ client ]
# Make a cert with nsCertType set to "client"
nsCertType = client
nsComment = "OpenSSL Generated Client Certificate"
You may also want to change the default keyfile names and other defaults to save yourself some typing later, especially if you're only going to be generating one certificate.
Once you have the configuration file the way you like it, you're ready to generate the key and certificate. We'll be doing it in just one step, without saving the intermediate certificate signing request:
$ openssl req -new -x509 -days 365 -nodes -out example.crt -keyout example.key -config example.conf
You'll be promoted for various certificate parameters; if you didn't change the defaults to what you want, you will need to enter them when prompted, otherwise you can just hit Enter at each prompt to accept the default specified in the configuration file. The certificate will be good for one year, if you want to change it, you can alter the number of days specified in the command line (or change the default in the config file).
Once this is done, you'll see two new files: example.key (which contains the private key) and example.crt (which contains the public certificate). Do whatever it is to need to do with them for your application.