Come creare un certificato digitale
(pubblicato il 30 gennaio 2023) Fonte: Foto di skylarvision da Pixabay
A volte, per ragioni di test o per uso domestico (configurazione di una vpn con openvpn per es. o di un server stunnel), mi càpita di avere bisogno di un certificato digitale.
Finché il certificato è self-signed, l'operazione richiede veramente pochissimo sforzo. Ma quando devo far riferimento ad una CA interna, la questione si complica.
Proverò adesso a descrivere le operazioni che compio quando devo creare:
- CA + certificati server + certificati client
- CA + Intermediate CA + certificati server + certificati client
Indice
- Cos’è un certificato digitale
- OpenSSL
- Struttura del “laboratorio”
- Caso 1: Creazione delle CA e dei certificati client e server
- Caso 2: Creazione di CA, Intermediate CA e dei certificati client e server
Cos'è un certificato digitale
I certificati digitali sono degli oggetti utilizzati nelle comunicazioni per fornire un canale sicuro su un mezzo intrinsecamente insicuro (internet) e vengono costruiti secondo lo standard X.509, un protocollo che definisce il formato dei certificati e l'insieme degli elementi di contorno come elementi autoritativi, elenchi di revoca e modalità di convalida.
Un certificato digitale è un oggetto crittografico costituito da una parte pubblica (chiave pubblica + informazioni personali del possessore del certificato) e una parte privata (la chiave privata).
La parte pubblica viene firmata digitalmente per garantire autenticazione e integrità e può essere eseguita:
- da se stesso (i cosiddetti certificati self-signed, una sorta di autocertificazione)
- da una CA, un'autorità superiore che garantisce che le informazioni del certificato sono di chi dice di essere.
OpenSSL
Per la costruzione di un certificato digitale, uso OpenSSL che è sia una libreria crittografica che implementa i protocolli TLS e SSL (deprecato), sia un toolkit professionale per l'utilizzo di funzionalità di crittografia.,
OpenSSL ha una cli molto complessa che può essere in parte configurata, soprattutto nella parte di definizione dei certificati, da un file di configurazione, openssl.cnf, un file .ini diviso in sezioni, contenenti una lista di coppie (chiave,valore).
Nel proseguio, invece che usare un unico file omnicomprensivo, ne verrà usato solo un frammento in base al contesto (certificato o csr di ca, intermediate, server o client). Ho trovato questo approccio più flessibile e più chiaro.
Negli esempi, userò questi file che avranno dei nomi “parlanti” in base a ciò che dovrò definire. Non mi dilungherò nella spiegazione delle singole opzioni perché:
- non le conosco tutte
- approfondire ogni singola direttiva di openssl.cnf richiederebbe un blog dedicato.
Si potrebbe ancora dissertare sulle versioni di openssl da usare, sui suoi fork ma non è questo il momento, forse in altri post (che sono in fase di preparazione.)
Struttura del “laboratorio”
Data la destinazione dei certificati (test o uso strettamente interno), i certificati vengono generati nella maniera più compatta possibile.
- Per le root CA, gli unici certificati self-signed, viene generato il csr e il crt in un'unica soluzione.
- Le intermediate CA vengono generate, come ogni altro certificato server / client, producendo prima un csr che viene firmato dalla root CA per produrre il crt.
- Anche i certificati server e client, come detto, vengono generati come l'intermediate CA, producendo prima il csr che verrà firmato da una CA.
- Ogni fase (per “fase” intendo produzione di un csr o di un crt) sarà “guidata” dal frammento di file di configurazione avente quel famoso nome “parlante”.
- Prenderò in considerazione due casistiche. In una i certificati server e client verranno validati da una root CA. Nell'altro, la root CA delegherà ad una intermediate CA la validazione di certificati server / client.
- Nella prima casistica, per rendere la soluzione più generica, supporrò di avere una CA distinta per i certificati client e una per i server.
⚠️ ⚠️ ⚠️ SUPER WARNING⚠️ ⚠️ ⚠️ Non è questo il modo formalmente corretto per la creazione di un certificato, almeno non quello da usare per un ambiente di produzione.
Non farò nessuna assunzione sulle modalità di cifratura, né sul tuning di openssl.
Il mio obiettivo qui è: voglio ottenere un certificato nella maniera più rapida possibile (e per rapido intendo senza troppi sbattimenti nelle configurazioni perché, alla fine, tutto può essere scriptabile e quindi veloce).
I file di configurazione saranno composti da massimo due sezioni contenenti le extensions X.509 necessarie per la creazione dei certificati. Il resto dei parametri sarà passato nella cli piuttosto che nel file di configurazione.
Mi rendo conto che per una buona comprensione occorrerebbe avere un po' di confidenza con:
- i concetti di cifratura asimmetrica e di certificato digitale
- configurazione e utilizzo base di openssl
- conoscenza di openssl.cnf e extensions X.509
Caso 1: Creazione delle CA e dei certificati client e server
La creazione della CA consisterà in un certificato self-signed ottenuto con un unico comando. Per i certificati invece verrà prodotto prima il csr che poi sarà firmato dalla CA al fine di ottenere il certificato vero e proprio.
Piccola nota: gli attributi basicConstraints, nsCertType, extendedKeyUsage saranno determinanti per battezzare una CA piuttosto che un certificato client/server
Creazione della CA per i certificati server
La creazione del certificato sarà one-shot.
Configurazione CA server
Il file di configurazione sarà ca_cert.cnf
###############
# ca_cert.cnf #
###############
[ v3_ca ]
basicConstraints = critical,CA:true
keyUsage = critical, cRLSign, keyCertSign
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always,issuer
nsCertType = sslCA
nsComment = "OpenSSL Generated CA Root Certificate"
Creazione CA server
OUTPUT_CERT=<path_output_cert>
CONF_CERT=<path_file_cnf>
### CA SERVER ###
openssl req \
-config $CONF_CERT/ca_cert.cnf \
-extensions v3_ca \
-x509 \
-days 1825 -sha512 \
-nodes -newkey rsa:4096 \
-subj "/C=IT/ST=Italia/CN=GlobalSign Test RootCA" \
-keyout $OUTPUT_CERT/ca_server.key \
-out $OUTPUT_CERT/ca_server.crt
- openssl req : crea un csr
- -config $CONFCERT/cacert.cnf : path del file di configurazione
- -extensions v3_ca : sezione del file da prendere come riferimento (in questo caso per le sole extensions X.509)
- -x509 : fa in modo che l'output sia direttamente un certificato piuttosto che un csr
- -days 1825 -sha512 : fissa la scadenza a 5 anni e setta sha512 come algoritmo di digest.
- -nodes -newkey rsa:4096 : crea una chiave rsa non cifrata a 4096 bit
- -subj “/C=IT/ST=Roma”/CN=GlobalSign Test RootCA” : distinguished_name passato da cli
- -keyout $OUTPUTCERT/caserver.key : path di destinazione della chiave
- -out $OUTPUTCERT/caserver.crt : path di destinazione del certificato CA.
Creazione certificato server
Verrà generato prima il csr (certificate signing request) che sarà firmato dalla CA per generare il certificato effettivo.
Configurazione csr server
Il file di configurazione per il csr sarà server_req.cnf, diviso in 3 sezioni.
La prima, req, è quella usata da openssl req (in assenza della quale verrà sollevata un'eccezione) e contiene solo il rimando alle extensions X.509 da usare.
Come prima, le extensions X.509 (v3serverreq) definiscono i giusti attributi per questo tipo di certificato.
N.B. L'ultima sezione è quella degli alternatives_name, la cui assenza non comporta problemi nella creazione del certificato ma potrebbe far fallire i check effettuati da alcuni prodotti, ad es. i webserver che pur riconoscendo nel Common Name il nome del servizio (es. la url) scatenano ugualmente l'eccezione BADCERTDOMAIN
##################
# server_req.cnf #
##################
[ req ]
req_extensions = v3_server_req
[ v3_server_req ]
basicConstraints = CA:FALSE
subjectKeyIdentifier = hash
keyUsage = nonRepudiation, digitalSignature, keyEncipherment, dataEncipherment
extendedKeyUsage = serverAuth
nsCertType = server
nsComment = "OpenSSL Generated Server Request Certificate"
subjectAltName = @alt_names
[ alt_names ]
DNS.1 = server.web.local
Creazione csr server
La sintassi è sostanzialmente uguale a quanto visto per la CA con la sola eccezione del parametro -x509 (che creava un crt invece che un csr). Il csr creerà il certificato e la coppia di chiavi pubblica e privata.
OUTPUT_CERT=<path_output_cert>
CONF_CERT=<path_file_cnf>
### CSR Server ###
openssl req \
-config $CONF_CERT/server_req.cnf \
-nodes -newkey rsa:2048 \
-subj "/C=IT/ST=Italia/L=Roma/O=My Company Ltd/OU=Divisione IT web/CN=server.web.local/emailAddress=it@web.local" \
-keyout $OUTPUT_CERT/server.key \
-out $OUTPUT_CERT/server.csr
- openssl req : crea un csr
- -config $CONFCERT/serverreq.cnf : path del file di configurazione
- -nodes -newkey rsa:2048 : crea una chiave rsa non cifrata a 2048 bit
- -subj “/C=IT/ST=Italia/L=Roma/O=My Company Ltd/OU=Divisione IT web/CN=server.web.local/emailAddress=it@web.local” : distinguished_name passato da cli
- -keyout $OUTPUT_CERT/server.key : path di destinazione della chiave
- -out $OUTPUT_CERT/server.csr : path di destinazione del csr
Configurazione crt server
A differenza di prima, non ho un file di configurazioni vero e proprio. Non usando il file generico openssl.cnf, l'unico file che si può invocare dal comando openssl x509
è un elenco di attributi che il certificato dovrà possedere e questo ci è sufficiente per creare certificati client o server.
Il file di configurazione per il crt sarà server_cert.cnf.
###################
# server_cert.cnf #
###################
basicConstraints = CA:FALSE
subjectKeyIdentifier = hash
keyUsage = nonRepudiation, digitalSignature, keyEncipherment, dataEncipherment
extendedKeyUsage = serverAuth
nsCertType = server
nsComment = "OpenSSL Generated Server Certificate"
subjectAltName = @alt_names
[ alt_names ]
DNS.1 = server.web.local
Creazione crt server
Di fatto, le uniche operazione che compio sono quelle di firmare con la CA il certificato, di fissare una scadenza e l'algoritmo di digest (altrimenti verrà applicato un default). Il resto, lo eredito dal csr (potrei sovrascrivere alcune cose come il subj) o dalle estensioni presenti nel file.
OUTPUT_CERT=<path_output_cert>
CONF_CERT=<path_file_cnf>
### CRT Server ###
openssl x509 \
-extfile $CONF_CERT/server_cert.cnf \
-req -in $OUTPUT_CERT/server.csr \
-days 365 -sha384 \
-CA $OUTPUT_CERT/ca_server.crt \
-CAkey $OUTPUT_CERT/ca_server.key \
-CAcreateserial \
-out $OUTPUT_CERT/server.crt
- openssl x509 : crea un certificato x509
- -extfile $CONFCERT/servercert.cnf : path del file di estensioni
- -req : acquisizione del file csr da far firmare
- -CA $OUTPUTCERT/caserver.crt : acquisizione del certificato della CA
- -CAkey $OUTPUTCERT/caserver.key : acquisizione della chiave privata della CA che firmerà il certificato
- -CAcreateserial : crea un seriale randomico per il certificato da creare. Normalmente una CA crea dei seriali progressivi memorizzandoli in un database interno.
- -days 365 -sha384 : fissa ad un anno la scadenza del certificato e usa sha384 come algoritmo di digest
- -out $OUTPUT_CERT/server.crt : path di destinazione del certificato server.
Creazione della CA per i certificati client
È praticamente la stessa cosa. La differenza è solo formale visto che, tecnicamente, i comandi che andrò ad eseguire saranno praticamente gli stessi (l'unica differenza di rilievo riguarderà i certificati client a cui corrisponderanno estensioni X.509 leggermente differenti)
Configurazione della CA client
È lo stesso file visto prima.
Creazione della CA client
OUTPUT_CERT=<path_output_cert>
CONF_CERT=<path_file_cnf>
### CA Client ###
openssl req \
-config $CONF_CERT/ca_cert.cnf \
-extensions v3_ca \
-x509 \
-days 1825 -sha512 \
-nodes -newkey rsa:4096 \
-subj "/C=IT/ST=Italia/CN=Digicert Test RootCA" \
-keyout $OUTPUT_CERT/ca_client.key \
-out $OUTPUT_CERT/ca_client.crt
C'è poco da dire. L'unica variazione degna di nota è il subj, per il resto è identico alla creazione della ca server.
Creazione certificato client
Come fatto per il server, verrà generato prima il csr che sarà firmato dalla CA per generare il certificato effettivo.
Configurazione csr client
Come per i certificati server, extendedKeyUsage e nsCertType stabiliscono come viene battezzato il certificato.
##################
# client_req.cnf #
##################
[ req ]
req_extensions = v3_client_req
[ v3_client_req ]
basicConstraints = CA:FALSE
subjectKeyIdentifier = hash
keyUsage = nonRepudiation, digitalSignature, keyEncipherment, dataEncipherment
extendedKeyUsage = clientAuth
nsCertType = client
nsComment = "OpenSSL Generated Client Request Certificate"
Creazione csr client
Nel csr, viene creata la chiave, rsa a 2048 bit, e viene settato il subj.
OUTPUT_CERT=<path_output_cert>
CONF_CERT=<path_file_cnf>
### CSR Client ###
openssl req \
-config $CONF_CERT/client_req.cnf \
-nodes -newkey rsa:2048 \
-subj "/C=IT/ST=Italia/L=Roma/O=Another Company Ltd/OU=User WebApp/CN=John Doe/emailAddress=john.doe@unknown.local" \
-keyout $OUTPUT_CERT/johndoe.key \
-out $OUTPUT_CERT/johndoe.csr
Configurazione crt client
Il file di configurazione raccoglie solo le estensioni con cui il certificato viene istanziato
###################
# client_cert.cnf #
###################
basicConstraints = CA:FALSE
subjectKeyIdentifier = hash
keyUsage = nonRepudiation, digitalSignature, keyEncipherment, dataEncipherment
extendedKeyUsage = clientAuth
nsCertType = client
nsComment = "OpenSSL Generated Client Certificate"
Creazione crt client
Come per il certificato server, il certificato client verrà creato in seguito alla firma del csr dalla CA e aver settato scadenza a algoritmo di digest (altrimenti verrà applicato un default). Essendo un certificato client, viene mostrato anche come costruire un p12, utile nei casi in cui l'autenticazione del client viene fatta dal browser.
OUTPUT_CERT=<path_output_cert>
CONF_CERT=<path_file_cnf>
### CRT Client ###
openssl x509 \
-extfile $CONF_CERT/client_cert.cnf \
-req -in $OUTPUT_CERT/johndoe.csr \
-days 365 -sha384 \
-CA $OUTPUT_CERT/ca_client.crt \
-CAkey $OUTPUT_CERT/ca_client.key \
-CAcreateserial \
-out $OUTPUT_CERT/johndoe.crt
### Creazione p12 ###
openssl pkcs12 -export -inkey $OUTPUT_CERT/johndoe.key -in $OUTPUT_CERT/johndoe.crt -out $OUTPUT_CERT/johndoe.p12
Riassunto
Di seguito, la raccolta di quanto visto finora, allo scopo di rendere un colpo d'occhio immediato e di dare una base per rendere scriptabile il processo che già così, tenendo presente i file di configurazione così composti e raccogliendo in uno script i comandi proposti, crea 2 CA, un certificato server e uno client (a meno dei subj di fantasia)
File di configurazione:
###############
# ca_cert.cnf #
###############
[ v3_ca ]
basicConstraints = critical,CA:true
keyUsage = critical, cRLSign, keyCertSign
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always,issuer
nsCertType = sslCA
nsComment = "OpenSSL Generated CA Root Certificate"
##################
# server_req.cnf #
##################
[ req ]
req_extensions = v3_server_req
[ v3_server_req ]
basicConstraints = CA:FALSE
subjectKeyIdentifier = hash
keyUsage = nonRepudiation, digitalSignature, keyEncipherment, dataEncipherment
extendedKeyUsage = serverAuth
nsCertType = server
nsComment = "OpenSSL Generated Server Request Certificate"
subjectAltName = @alt_names
[ alt_names ]
DNS.1 = server.web.local
###################
# server_cert.cnf #
###################
basicConstraints = CA:FALSE
subjectKeyIdentifier = hash
keyUsage = nonRepudiation, digitalSignature, keyEncipherment, dataEncipherment
extendedKeyUsage = serverAuth
nsCertType = server
nsComment = "OpenSSL Generated Server Certificate"
subjectAltName = @alt_names
[ alt_names ]
DNS.1 = server.web.local
##################
# client_req.cnf #
##################
[ req ]
req_extensions = v3_client_req
[ v3_client_req ]
basicConstraints = CA:FALSE
subjectKeyIdentifier = hash
keyUsage = nonRepudiation, digitalSignature, keyEncipherment, dataEncipherment
extendedKeyUsage = clientAuth
nsCertType = client
nsComment = "OpenSSL Generated Client Request Certificate"
###################
# client_cert.cnf #
###################
basicConstraints = CA:FALSE
subjectKeyIdentifier = hash
keyUsage = nonRepudiation, digitalSignature, keyEncipherment, dataEncipherment
extendedKeyUsage = clientAuth
nsCertType = client
nsComment = "OpenSSL Generated Client Certificate"
Script di creazione certificati:
OUTPUT_CERT=<path_output_cert>
CONF_CERT=<path_file_cnf>
#############
# CA SERVER #
#############
openssl req -config $CONF_CERT/ca_cert.cnf -extensions v3_ca -x509 -days 1825 -sha512 -nodes -newkey rsa:4096 -subj "/C=IT/ST=Italia/CN=GlobalSign Test RootCA" -keyout $OUTPUT_CERT/ca_server.key -out $OUTPUT_CERT/ca_server.crt
##########
# Server #
##########
# csr
openssl req -config $CONF_CERT/server_req.cnf -nodes -newkey rsa:2048 -subj "/C=IT/ST=Italia/L=Roma/O=My Company Ltd/OU=Divisione IT web/CN=server.web.local/emailAddress=it@web.local" -keyout $OUTPUT_CERT/server.key -out $OUTPUT_CERT/server.csr
# crt
openssl x509 -extfile $CONF_CERT/server_cert.cnf
-req -in $OUTPUT_CERT/server.csr -days 365 -sha384 -CA $OUTPUT_CERT/ca_server.crt -CAkey $OUTPUT_CERT/ca_server.key -CAcreateserial -out $OUTPUT_CERT/server.crt
#############
# CA Client #
#############
openssl req -config $CONF_CERT/ca_cert.cnf -extensions v3_ca -x509 -days 1825 -sha512 -nodes -newkey rsa:4096 -subj "/C=IT/ST=Italia/CN=Digicert Test RootCA" -keyout $OUTPUT_CERT/ca_client.key -out $OUTPUT_CERT/ca_client.crt
##########
# Client #
##########
# csr
openssl req -config $CONF_CERT/client_req.cnf -nodes -newkey rsa:2048 -subj "/C=IT/ST=Italia/L=Roma/O=Another Company Ltd/OU=User WebApp/CN=John Doe/emailAddress=john.doe@unknown.local" -keyout $OUTPUT_CERT/johndoe.key -out $OUTPUT_CERT/johndoe.csr
# crt
openssl x509 -extfile $CONF_CERT/client_cert.cnf -req -in $OUTPUT_CERT/johndoe.csr -days 365 -sha384 -CA $OUTPUT_CERT/ca_client.crt -CAkey $OUTPUT_CERT/ca_client.key -CAcreateserial -out $OUTPUT_CERT/johndoe.crt
# p12
openssl pkcs12 -export -inkey $OUTPUT_CERT/johndoe.key -in $OUTPUT_CERT/johndoe.crt -out $OUTPUT_CERT/johndoe.p12
Caso 2. Creazione di CA, Intermediate CA e dei certificati client e server
Gli esempi che seguiranno saranno presentati sullo stile del riassunto perché fondamentalmente molto simili al caso 1. Si aggiunge un punto di intermediazione in più dato dal fatto che la CA delega ad una CA “subalterna” l'incarico di verificare le paternità di coloro i quali richiedono certificati.
Quindi avremo:
- una root CA, certificato self-signed
- un'Intermediate CA, validata dalla root CA (csr + crt firmato dalla root CA) n certificati client/server validati dall'Intermediate CA (csr + crt firmato dall'Intermediate CA)
Creazione certificati
OUTPUT_CERT=$HOME/stunnel/build-cert
CONF_CERT=$HOME/stunnel/conf.d
###########
# Root CA #
###########
openssl req -config $CONF_CERT/ca_cert.cnf -extensions v3_ca -x509 -days 1825 -sha512 -nodes -newkey rsa:4096 -subj "/C=IT/ST=Italia/CN=GlobalSign Test RootCA" -keyout $OUTPUT_CERT/rootca.key -out $OUTPUT_CERT/rootca.crt
###################
# INTERMEDIATE CA #
###################
# csr
openssl req -config $CONF_CERT/intermediate_req.cnf -nodes -newkey rsa:2048 -subj "/C=IT/ST=Italia/CN=GlobalSign Test IntermediateCA" -keyout $OUTPUT_CERT/intermediate.key -out $OUTPUT_CERT/intermediate.csr
# crt
openssl x509 -extfile $CONF_CERT/intermediate_cert.cnf -req -in $OUTPUT_CERT/intermediate.csr -days 365 -sha384 -CA $OUTPUT_CERT/rootca.crt -CAkey $OUTPUT_CERT/rootca.key -CAcreateserial -out $OUTPUT_CERT/intermediate.crt
##########
# Server #
##########
# csr
openssl req -config $CONF_CERT/server_req.cnf -nodes -newkey rsa:2048 -subj "/C=IT/ST=Italia/L=Roma/O=My Company Ltd/OU=Divisione IT web/CN=server.web.local/emailAddress=it@web.local" -keyout $OUTPUT_CERT/server.key -out $OUTPUT_CERT/server.csr
# crt
openssl x509 -extfile $CONF_CERT/server_cert.cnf
-req -in $OUTPUT_CERT/server.csr -CA $OUTPUT_CERT/intermediate.crt -CAkey $OUTPUT_CERT/intermediate.key -CAcreateserial -days 365 -sha384 -out $OUTPUT_CERT/server.crt
##########
# Client #
##########
# csr
openssl req -config $CONF_CERT/client_req.cnf -nodes -newkey rsa:2048 -subj "/C=IT/ST=Italia/L=Roma/O=Another Company Ltd/OU=User WebApp/CN=John Doe/emailAddress=john.doe@unknown.local" -keyout $OUTPUT_CERT/johndoe.key -out $OUTPUT_CERT/johndoe.csr
# crt
openssl x509 -extfile $CONF_CERT/client_cert.cnf -req -in $OUTPUT_CERT/johndoe.csr -days 365 -sha384 -CA $OUTPUT_CERT/intermediate.crt -CAkey $OUTPUT_CERT/intermediate.key -CAcreateserial -out $OUTPUT_CERT/johndoe.crt
# p12
openssl pkcs12 -export -inkey $OUTPUT_CERT/johndoe.key -in $OUTPUT_CERT/legs.crt -out $OUTPUT_CERT/johndoe.p12
#DigitalCertificate #x509 #csr #openssl #ca #cryptography #AsymmetricEncryption #SymmetricEncryption #DigitalSignature