Come creare un certificato digitale

(pubblicato il 30 gennaio 2023) certificati digitali 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:

  1. CA + certificati server + certificati client
  2. CA + Intermediate CA + certificati server + certificati client

Indice

  1. Cos’è un certificato digitale
  2. OpenSSL
  3. Struttura del “laboratorio”
  4. Caso 1: Creazione delle CA e dei certificati client e server
    1. Creazione della CA per i certificati server
      1. Configurazione CA server
      2. Creazione CA server
    2. Creazione del certificato server
      1. Configurazione csr server
      2. Creazione csr server
      3. Configurazione crt server
      4. Creazione crt server
    3. Creazione della CA per i certificati client
      1. Configurazione CA client
      2. Creazione CA client
    4. Creazione del certificato client
      1. Configurazione csr client
      2. Creazione csr client
      3. Configurazione crt client
      4. Creazione crt client
    5. Riassunto
  5. 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:

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é:

  1. non le conosco tutte
  2. 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.

⚠️ ⚠️ ⚠️ 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:

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

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

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

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:

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