I'm using the following script to create certificates using my own internal CA(Work in progress):
#!/bin/bash
while [[ "$#" < 1 ]];do
echo "Usage: cert-admin.sh [COMMAND] [OPTIONS]\n"
echo " Commands:"
echo " newpair - Creates a key and signed certificate"
echo " revoke - Revokes a certificate and re-creates the intermediate CRL"
echo " newcrl - Re-creates the intermediate CRL"
echo ""
echo " Examples: cert-admin.sh newpair server example.mysite.com"
echo " cert-admin.sh revoke example.mysite.com"
echo ""
echo " Types: server"
exit 1
done
command=$1
function newCRL {
cd /root/ca
rm -f intermediate/crl/intermediate.crl.pem
openssl ca -config intermediate/openssl.cnf \
-gencrl -out intermediate/crl/intermediate.crl.pem
openssl crl -in intermediate/crl/intermediate.crl.pem -noout -text
}
while [[ $command == 'newpair' ]];do
type=$2
fqdn=$3
while [[ $type == 'server' ]];do
cd /root/ca
openssl genrsa -out intermediate/private/$fqdn.key.pem 2048
openssl req -reqexts SAN \
-config <(cat intermediate/openssl.cnf \
<(printf "\n[SAN]\nsubjectAltName=DNS:$fqdn\n\n")) \
-key intermediate/private/$fqdn.key.pem \
-new -sha256 -out intermediate/csr/$fqdn.csr.pem
openssl ca -config intermediate/openssl.cnf \
-extensions server_cert -days 375 -notext -md sha256 \
-in intermediate/csr/$fqdn.csr.pem \
-out intermediate/certs/$fqdn.cert.pem
chmod 444 intermediate/private/$fqdn.key.pem
chmod 444 intermediate/certs/$fqdn.cert.pem
openssl x509 -noout -text \
-in intermediate/certs/$fqdn.cert.pem
openssl verify -CAfile intermediate/certs/ca-chain.cert.pem \
intermediate/certs/$fqdn.cert.pem
echo "ca-chain: /root/ca/intermediate/certs/ca-chain.cert.pem"
echo "key: /root/ca/intermediate/private/$fqdn.key.pem"
echo "cert: /root/ca/intermediate/certs/$fqdn.cert.pem"
break;
done
break;
done
while [[ $command == 'revoke' ]];do
cert=$2
cd /root/ca
openssl ca -config intermediate/openssl.cnf \
-revoke intermediate/certs/$cert.cert.pem
newCRL
break;
done
while [[ $command == 'newcrl' ]];do
newCRL
while [[ $2 != 'cron' ]];do
echo "CRL should be re-created by cron every 30 days. Example:"
echo "30 12 2 * * /root/ca/cert-admin.sh newcrl cron"
break;
done
break;
done
intermediate/openssl.cnf
[ ca ]
default_ca = CA_default
[ CA_default ]
dir = /root/ca/intermediate
certs = $dir/certs
crl_dir = $dir/crl
new_certs_dir = $dir/newcerts
database = $dir/index.txt
serial = $dir/serial
RANDFILE = $dir/private/.rand
private_key = $dir/private/intermediate.key.pem
certificate = $dir/certs/intermediate.cert.pem
crlnumber = $dir/crlnumber
crl = $dir/crl/intermediate.crl.pem
crl_extensions = crl_ext
default_crl_days = 30
default_md = sha256
name_opt = ca_default
cert_opt = ca_default
default_days = 375
preserve = no
policy = policy_loose
[ policy_strict ]
countryName = match
stateOrProvinceName = match
organizationName = match
organizationalUnitName = optional
commonName = supplied
emailAddress = optional
[ policy_loose ]
countryName = optional
stateOrProvinceName = optional
localityName = optional
organizationName = optional
organizationalUnitName = optional
commonName = supplied
emailAddress = optional
[ req ]
default_bits = 4096
distinguished_name = req_distinguished_name
string_mask = utf8only
default_md = sha256
x509_extensions = v3_ca
[ req_distinguished_name ]
countryName = Country Name (2 letter code)
stateOrProvinceName = State or Province Name
localityName = Locality Name
0.organizationName = Organization Name
organizationalUnitName = Organizational Unit Name
commonName = Common Name
emailAddress = Email Address
countryName_default = US
stateOrProvinceName_default = Denial
localityName_default = Springfield
0.organizationName_default = MyOrg
organizationalUnitName_default = Research
emailAddress_default = mail@example.com
[ v3_ca ]
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always,issuer
basicConstraints = critical, CA:true
keyUsage = critical, digitalSignature, cRLSign, keyCertSign
[ v3_intermediate_ca ]
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always,issuer
basicConstraints = critical, CA:true, pathlen:0
keyUsage = critical, digitalSignature, cRLSign, keyCertSign
[ usr_cert ]
basicConstraints = CA:FALSE
nsCertType = client, email
nsComment = "OpenSSL Generated Client Certificate"
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid,issuer
keyUsage = critical, nonRepudiation, digitalSignature, keyEncipherment
extendedKeyUsage = clientAuth, emailProtection
[ server_cert ]
basicConstraints = CA:FALSE
nsCertType = server
nsComment = "OpenSSL Generated Server Certificate"
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid,issuer:always
keyUsage = critical, digitalSignature, keyEncipherment
extendedKeyUsage = serverAuth
[ crl_ext ]
authorityKeyIdentifier=keyid:always
[ ocsp ]
basicConstraints = CA:FALSE
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid,issuer
keyUsage = critical, digitalSignature
extendedKeyUsage = critical, OCSPSigning
With the intermediate CA trusted in Arch Linux, the certificate doesn't throw any errors.
However, on Android I get errors. I think it has something to do with SAN
as the Bitwarden Android app shows subjectAltNames[]
and Kiwi browser(Chrome Desktop for Android) shows ERR_CERT_COMMON_NAME_INVALID
.
Using -addext "subjectAltName = DNS:www.example.com"
with -config intermediate/openssl.cnf
doesn't work either.
Creating a self signed cert using:
openssl req -new -newkey rsa:4096 -days 365 -nodes -x509 \
-addext "subjectAltName = DNS:www.example.com" \
-subj "/C=US/ST=Denial/L=Springfield/O=MyOrg/CN=www.example.com" \
-keyout web.key -out web.crt
works fine.
EDIT: Common Name
is set to the FQDN when prompted.
EDIT2: To clarify, this is after installing the CA chain cert on the Android device. Running Android 10.
EDIT3: I've narrowed the issue down to SAN not being added due to -config <(cat intermediate/openssl.cnf <(printf "\n[SAN]\nsubjectAltName=DNS:$fqdn\n\n"))
not working. Is there another way to include SAN while specifying openssl.cnf
? It's not working for me...
My script above is at fault, I never ended up fixing it. If you are looking for a solution, odds are you are having issues with your openssl commands and or openssl config. I ended up using EJBCA in docker to create and manage my CA and certificates. So far EJBCA is working flawlessly (I suggest keeping it offline for security reasons). Certificates work well with Android 10 to Android 14 (14 being the latest tested as of writing this answer).