Générer des certificats pour Ipsec avec Openssl

Dans un précédant article, j'ai pu configurer un serveur ipsec dans ipfire, soit mettre en place un serveur Ipsec/Strongswan et des clients Windows 7, Debian 7 ou Iphone, tout par le programme "ipsec pki" . Dans cet article, j'utilise uniquement le programme Openssl. Pour simplifier le processus de création des certificats d’autorités, client et serveur, je m'appuie sur mes scripts stockés sur Github.

A/ En résumé :

Avant de se lancer, je tiens à vous signaler qu'il ne faut surtout pas mettre des caractères avec des accents. En effet, la connexion VPN ne se fera pas à cause d'un différent sur les jeux de caractères (ANSI/UTF8). Les scripts suivants permettent de modifier les fichier de configuration à la volé et de générer tous les certificats.

# 1/ on télécharge les scripts et le fichier de configuration.
wget https://raw.githubusercontent.com/jbsky/jbsky/master/ipfire/rootfs/etc/ssl/ipsec.cnf
wget https://raw.githubusercontent.com/jbsky/jbsky/master/ipfire/rootfs/etc/ssl/openssl.cnf
wget https://raw.githubusercontent.com/jbsky/jbsky/master/ipfire/rootfs/usr/local/sbin/caroot
wget https://raw.githubusercontent.com/jbsky/jbsky/master/ipfire/rootfs/usr/local/sbin/casec
wget https://raw.githubusercontent.com/jbsky/jbsky/master/ipfire/rootfs/usr/local/sbin/secsrv
wget https://raw.githubusercontent.com/jbsky/jbsky/master/ipfire/rootfs/usr/local/sbin/secacces

# 2/ on rend les scripts exécutables et on dispatche les fichiers au bon endroit. 
chmod +x {caroot,casec,secsrv,secacces}
mv {caroot,casec,secsrv,secacces} /usr/local/sbin
mv {openssl.cnf,ipsec.cnf} /etc/ssl/

# 3/ On génère le certificat d'autorité racine.
caroot

# 5/ On génère le certificat d'autorité ipsec.
casec

# 6/ On génère le certificat serveur ipsec signé par l'autorité ipsec.
secsrv

# 7/ On génère le certificat client ipsec signé par l'autorité ipsec.
secacc

En détail

Petit rappel: Openssl s'appuie sur le fichier /etc/ssl/openssl.cnf par défaut, cependant, il est possible de spécifier un autre fichier avec l'otion [-config $FILE]. Un certificat est composé de 2 fichiers, une clé privé et une clé publique. Dans le cas d'une configuration IpSec, le client doit avoir le drapeau  clientAuth  et serverAuth et doivent être signé par la même autorité.

B/ Le fichier de configuration d'OpenSSL.

B.1/ Génération du certificat d'autorité

Dans un premier temps, le champs [ca] dans le fichier openssl.cnf désigne l'autorité par défaut, soit [ROOT].

HOME                              = .
RANDFILE                          = $ENV::HOME/.rnd

[ca]
default_ca                        = ROOT                    # The default ca section
 
[ROOT]
dir                               = /etc/ssl/root           # Where everything is kept
certs                             = $dir/certs              # Where the issued certs are kept
new_certs_dir                     = $dir/newcerts           # default place for new certs.
database                          = $dir/index.db           # database index file.
certificate                       = $dir/certs/ca.pem       # The CA certificate
serial                            = $dir/serial             # The current serial number
private_key                       = $dir/private/ca.key     # The private key
default_days                      = 3650                    # how long to certify for
default_md                        = sha256                  # which md to use.
preserve                          = no                      # keep passed DN ordering
policy                            = policy_match            # voir le champs ci dessous
 
[ROOT_CA]
nsComment                         = "Root Secure Digital Certificate Signing"
subjectKeyIdentifier              = hash
authorityKeyIdentifier            = keyid,issuer:always
basicConstraints                  = critical,CA:TRUE,pathlen:1
keyUsage                          = critical, digitalSignature, cRLSign, keyCertSign


[IPSEC_CA]
nsComment                         = "Ipsec Secure Digital Certificate Signing"
subjectKeyIdentifier              = hash
authorityKeyIdentifier            = keyid:always,issuer
basicConstraints                  = critical, CA:true, pathlen:0
keyUsage                          = critical, digitalSignature, cRLSign, keyCertSign
nsCertType                        = sslCA

[req]
default_bits                      = 2048
distinguished_name                = req_distinguished_name
string_mask                       = utf8only
default_md                        = sha256                  # SHA-2
x509_extensions                   = ROOT_CA

[req_distinguished_name]
countryName                       = Country Name (2 letter code)
countryName_min                   = 2
countryName_max                   = 2
stateOrProvinceName               = State or Province Name (full name)
localityName                      = Locality Name (eg, city)
organizationName                  = Organization Name (eg, company)
organizationalUnitName            = Organizational Unit Name (eg, section)
commonName                        = Common Name (eg, YOUR name)
commonName_max                    = 64
emailAddress                      = Email Address
emailAddress_max                  = 40
stateOrProvinceName_default       = NullPart
commonName_default                = Toto Certification Authority
organizationalUnitName_default    = Secure Digital Certificate Signing
localityName_default              = Paris
organizationName_default          = Toto
countryName_default               = FR
emailAddress_default              = toto@gamel.net

[policy_match]
countryName                       = match
stateOrProvinceName               = match
localityName                      = match
organizationName                  = match
organizationalUnitName            = optional
commonName                        = supplied
emailAddress                      = optional

  • Policy_match sont les règles définies pour autoriser la création d'un sous certificat d'autorité.
  • req_distinguished_name normalise les champs composants le certificat. Les champs terminant par _default permettent à OpenSSL de suggérer une valeur lors de la génération du certificat. Je vous invite à renseigner la localité, le mail, la ville et le nom de l'organisation, le reste diffère.
  • Bien definir pour le certificat d'autorité la variable keyUsage avec les drapeaux critical, digitalSignature, cRLSign, keyCertSign.

Le fichier openssl.cnf renseigné à ce point, il est tout à fait possible de définir son certificat d'autorité.

#!/bin/bash


while getopts ":vh:" option
do
 case $option in
  h)
   echo ""
   echo "Usage  : $0 [-vh]"
   echo ""
   echo "option :"   
   echo "          -v  verbose"
   echo "          -h  help"
   echo ""
   exit -1
   ;;
  v)
   v=1
   ;;
 esac
done
if [ -f /etc/default/ssl ];
then
	. /etc/default/ssl
else

	echo "Please enter the name of your cnf files, followed by [ENTER]:"
	read CONFIG
	echo "CONFIG=${CONFIG}" >>/etc/default/ssl
	echo "Please enter the path for your ssl files, followed by [ENTER]:"
	read PATH_SSL
	echo "PATH_SSL=${PATH_SSL}" >>/etc/default/ssl 
	echo "Please enter the IP or FQND of your Ipsec server, followed by [ENTER]:"
	read CN
	echo "CN=${CN}" >>/etc/default/ssl
	echo "Please enter the name of your Organisation, followed by [ENTER]:"
	read Org
	echo "ORGANISATION=${Org}" >>/etc/default/ssl
	echo "New Server Ipsec : `hostname` press [ENTER] to confirm."
	read SRV_IPSEC
	if [[ "$SRV_IPSEC" == "" ]];then
		echo "SRV_IPSEC=`hostname`">>/etc/default/ssl
	else
		echo "SRV_IPSEC=${SRV_IPSEC}">>/etc/default/ssl
	fi
	echo "BITS=8192">>/etc/default/ssl
fi
CONFIG=openssl.cnf
if [[ "$v" == "1" ]]; then
	echo "Mise en place des répertoires  ${PATH_SSL}/{root,web_ca,ipsec} et des fichiers ${PATH_SSL}/{root,web,ipsec}/{index.db,serial}."
fi
rm -fR ${PATH_SSL}/{root,web_ca,ipsec}
mkdir -p ${PATH_SSL}/{ipsec,root,web}/{certs,crl,newcerts,private}
chmod -R 600 ${PATH_SSL}/{root,ipsec,web}/private
touch ${PATH_SSL}/{root,web,ipsec}/{index.db,serial}
sed -i -e 's/^commonName_default.*$/commonName_default                = '${ORGANISATION}' Certification Authority/g'  ${PATH_SSL}/${CONFIG}
sed -i -e 's/^organizationalUnitName_default.*$/organizationalUnitName_default    = Secure Digital Certificate Signing/g'  ${PATH_SSL}/${CONFIG}
if [[ "$v" == "1" ]]; then
	echo "Création de la clé privée pour [ROOT]..."
	echo "openssl req -x509 -config ${PATH_SSL}/${CONFIG} -newkey rsa:${BITS} -sha256 -extensions ROOT_CA -days 3650 -keyout ${PATH_SSL}/root/private/ca.key -out ${PATH_SSL}/root/certs/ca.pem"
fi
openssl req -x509 -config ${PATH_SSL}/${CONFIG} -newkey rsa:${BITS} -sha256 -extensions ROOT_CA -days 3650 -keyout ${PATH_SSL}/root/private/ca.key -out ${PATH_SSL}/root/certs/ca.pem
chmod 400 ${PATH_SSL}/root/private/ca.key
chmod 444 ${PATH_SSL}/root/certs/ca.pem
if [[ "$v" == "1" ]]; then
	echo "Initialisation du serial pour ${PATH_SSL}/root/certs/ca.pem."
	echo "openssl x509 -serial -noout -in ${PATH_SSL}/root/certs/ca.pem | cut -d= -f2 > ${PATH_SSL}/root/serial"
fi
openssl x509 -serial -noout -in ${PATH_SSL}/root/certs/ca.pem | cut -d= -f2 > ${PATH_SSL}/root/serial


Ensuite, lançons le premier script.

caroot

B.2/ Génération d'un sous certificat d'autorité

Pour simplifier les choses,  je préfère utiliser un autre fichier de configuration pour ipsec.

HOME                              = .
RANDFILE                          = $ENV::HOME/.rnd

[ca]
default_ca                        = IPSEC                   # The default ca section
 
[IPSEC]
dir                               = /etc/ssl/ipsec          # Where everything is kept
certs                             = $dir/certs              # Where the issued certs are kept
new_certs_dir                     = $dir/newcerts           # default place for new certs.
database                          = $dir/index.db           # database index file.
certificate                       = $dir/certs/ca.pem       # The CA certificate
serial                            = $dir/serial             # The current serial number
private_key                       = $dir/private/ca.key     # The private key
default_days                      = 3650                    # how long to certify for
default_md                        = sha256                  # which md to use.
preserve                          = no                      # keep passed DN ordering
policy                            = policy_match            # voir le champs ci dessous
 
[IPSEC_CA]
nsComment                         = "IPSEC_CA"
subjectKeyIdentifier              = hash
authorityKeyIdentifier            = keyid,issuer:always
basicConstraints                  = critical,CA:TRUE,pathlen:0
keyUsage                          = critical, digitalSignature, cRLSign, keyCertSign

[req]
default_bits                      = 2048
distinguished_name                = req_distinguished_name
string_mask                       = utf8only
default_md                        = sha256                  # SHA-2
x509_extensions                   = IPSEC_CA                # Extension to add when the -x509 option is used.


[req_distinguished_name]
countryName                       = Country Name (2 letter code)
countryName_min                   = 2
countryName_max                   = 2
stateOrProvinceName               = State or Province Name (full name)
localityName                      = Locality Name (eg, city)
organizationName                  = Organization Name (eg, company)
organizationalUnitName            = Organizational Unit Name (eg, section)
commonName                        = Common Name (eg, YOUR name)
commonName_max                    = 64
emailAddress                      = Email Address
emailAddress_max                  = 40
stateOrProvinceName_default       = NullPart
commonName_default                = windows.172.16.254.1
organizationalUnitName_default    = Certificat Acces IPSec
localityName_default              = Paris
organizationName_default          = Toto
countryName_default               = FR
emailAddress_default              = toto@gamel.net


[policy_match]
countryName                       = match
stateOrProvinceName               = match
localityName                      = match
organizationName                  = match
organizationalUnitName            = optional
commonName                        = supplied
emailAddress                      = optional

[HOTE]
nsComment                         = "IPSec Secure Digital Certificate Hote"
subjectKeyIdentifier              = hash
authorityKeyIdentifier            = keyid,issuer:always
issuerAltName                     = issuer:copy
subjectAltName                    = @alt_names
basicConstraints                  = critical,CA:FALSE
keyUsage                          = digitalSignature, nonRepudiation, keyEncipherment
nsCertType                        = server
extendedKeyUsage                  = serverAuth
 
[alt_names]
DNS.1                             = 172.16.254.1

[ACCES]
nsComment                         = "IPSec Secure Digital Certificate Access"
subjectKeyIdentifier              = hash
authorityKeyIdentifier            = keyid,issuer:always
issuerAltName                     = issuer:copy
basicConstraints                  = critical,CA:FALSE
keyUsage                          = digitalSignature, nonRepudiation, keyEncipherment
nsCertType                        = server
extendedKeyUsage                  = clientAuth

Script pour générer un sous-certificat d'autorité.

#!/bin/bash
while getopts ":vh:" option
do
 case $option in
  h)
   echo ""
   echo "Usage  : $0 [-vh]"
   echo ""
   echo "option :"   
   echo "          -v  verbose"
   echo "          -h  help"
   echo ""
   exit -1
   ;;
  v)
   v=1
   ;;
 esac
done
if [ -f /etc/default/ssl ];
then
	. /etc/default/ssl
else
	echo "File $FILE does not exist."
	echo "error exit 1"
	return 1
fi
CONFIG=ipsec.cnf
ROOTCONFIG=openssl.cnf
sed -i -e 's/^commonName_default.*$/commonName_default                = '${ORGANISATION}' Certification Authority for IPSec Service/g'  ${PATH_SSL}/${CONFIG}
sed -i -e 's/^organizationalUnitName_default.*$/organizationalUnitName_default    = Secure Digital Certificate Signing/g'  ${PATH_SSL}/${CONFIG}
if [[ "$v" == "1" ]]; then
	echo "Génération de la clé privé crypté pour [IPSEC_CA]..."
	echo "openssl req -newkey rsa:${BITS} -sha256 -keyout ${PATH_SSL}/ipsec/private/ca.key -out ${PATH_SSL}/ipsec/ca.req  -config ${PATH_SSL}/${CONFIG}"
fi
openssl req -newkey rsa:${BITS} -sha256 -keyout ${PATH_SSL}/ipsec/private/ca.key -out ${PATH_SSL}/ipsec/ca.req  -config ${PATH_SSL}/${CONFIG}

if [[ "$v" == "1" ]]; then
	echo "Signature du certificat d'autorité par [ROOT_CA]."
	echo "openssl ca -extensions IPSEC_CA -in ${PATH_SSL}/ipsec/ca.req -out ${PATH_SSL}/ipsec/ca.pem -config ${PATH_SSL}/${CONFIG}"
fi
openssl ca -extensions IPSEC_CA -in ${PATH_SSL}/ipsec/ca.req -out ${PATH_SSL}/ipsec/certs/ca.pem -config ${PATH_SSL}/${ROOTCONFIG}
cat  ${PATH_SSL}/root/certs/ca.pem > ${PATH_SSL}/ipsec/certs/ca-chain.pem
cat  ${PATH_SSL}/ipsec/certs/ca.pem >> ${PATH_SSL}/ipsec/certs/ca-chain.pem
chmod 444  ${PATH_SSL}/ipsec/certs/ca-chain.pem
chmod 400 ${PATH_SSL}/ipsec/private/ca.key
chmod 444 ${PATH_SSL}/ipsec/certs/ca.pem
if [[ "$v" == "1" ]]; then
	echo "Génèration du serial pour ${PATH_SSL}/ipsec/ca.pem."
	echo "openssl x509 -serial -noout -in ${PATH_SSL}/ipsec/ca.pem | cut -d= -f2 > ${PATH_SSL}/ipsec/serial"
fi
openssl x509 -serial -noout -in ${PATH_SSL}/ipsec/certs/ca.pem | cut -d= -f2 > ${PATH_SSL}/ipsec/serial
if [[ "$v" == "1" ]]; then
	echo "TODO"
	echo
	echo
	echo "On peut maintenant créer des certificats client/serveur et les signer avec notre autorité intermédiaire."
	echo # FIN IPSEC_CA
fi

Pour générer le certificat d'autorité pour Ipsec, lançons le deuxième script.

casec

B.3/ Génération du certificat serveur.

DNS.1 est l'étiquette qui doit correspondre absolument à l’adresse IP ou FQDN du serveur dans le fichier ipsec.cnf.

  • extentedKeyUsage prend la valeur serverAuth, soit l'option pour que le certificat soit exploitable par un serveur ipsec.
  • Il est possible de générer plusieurs certicats pour autant de serveur.

Puis, pour générer le certificat serveur:

#!/bin/bash

CONFIG=ipsec.cnf

while getopts ":vh:" option
do
	case $option in
		h)
			echo ""
			echo "Usage  : $0 [-vh]"
			echo ""
			echo "option :"   
			echo "          -v  verbose"
			echo "          -h  help"
			echo ""
			exit -1
			;;
		v)
			v=1
			;;
	esac
done

if [ -f /etc/default/ssl ];
then
	. /etc/default/ssl
else
	echo "File $FILE does not exist."
	echo "error exit 1"
	return 1
fi


sed -i -e 's/^DNS.1.*$/DNS.1                             = '${CN}'/g'  ${PATH_SSL}/ipsec.cnf
sed -i -e 's/^commonName_default.*$/commonName_default                = '${CN}'/g'  ${PATH_SSL}/${CONFIG}
sed -i -e 's/^organizationalUnitName_default.*$/organizationalUnitName_default    = Certificat Hote IPSec/g'  ${PATH_SSL}/${CONFIG}

if [[ "$v" == "1" ]]; then
	echo "Génération de la clé privé crypté pour ${SRV_IPSEC}..."
	echo "openssl genrsa  -out ${PATH_SSL}/ipsec/private/${SRV_IPSEC}.key 4096  -config ${PATH_SSL}/${CONFIG}"
fi
openssl genrsa  -out ${PATH_SSL}/ipsec/private/${SRV_IPSEC}.key 4096  -config ${PATH_SSL}/${CONFIG}

if [[ "$v" == "1" ]]; then
	echo "Création de la clé publique..."
	echo "openssl req -key ${PATH_SSL}/ipsec/private/${SRV_IPSEC}.key -sha256 -out ${PATH_SSL}/ipsec/${SRV_IPSEC}.req -new -nodes -config ${PATH_SSL}/${CONFIG}"
fi

openssl req -key ${PATH_SSL}/ipsec/private/${SRV_IPSEC}.key -sha256 -out ${PATH_SSL}/ipsec/${SRV_IPSEC}.req -new -nodes -config ${PATH_SSL}/${CONFIG}

if [[ "$v" == "1" ]]; then
	echo "Signature du certificat [HOTE] par l'autorité [IPSEC]." 
	echo "Notre certificat Hôte aura l'extentions serverAuth signalés dans la section [HOTE]."	
	echo "openssl ca -name IPSEC -extensions HOTE -in ${PATH_SSL}/ipsec/${SRV_IPSEC}.req -out ${PATH_SSL}/ipsec/certs/${SRV_IPSEC}.pem -config  ${PATH_SSL}/${CONFIG}"
fi

openssl ca -name IPSEC -extensions HOTE -in ${PATH_SSL}/ipsec/${SRV_IPSEC}.req -out ${PATH_SSL}/ipsec/certs/${SRV_IPSEC}.pem -config  ${PATH_SSL}/${CONFIG}

chmod 400 ${PATH_SSL}/ipsec/private/${SRV_IPSEC}.key
chmod 444 ${PATH_SSL}/ipsec/certs/${SRV_IPSEC}.pem


Puis, lançons le troisième script.

[bash]secsrv[/bash]

B.4/ Génération du certificat client.

Dans le fichier ipsec.cnf, à noté que extentedKey prend la valeur clientAuth, soit l'option pour que le certificat soit exploitable par un client ipsec.

Il est possible de générer plusieurs certificats serveurs pour le même client sous la même autorité.
Donc, pour générer le certificat client:

#!/bin/bash
while getopts ":vh:" option
do
 case $option in
  h)
   echo ""
   echo "Usage  : $0 [-vh]"
   echo ""
   echo "option :"   
   echo "          -v  verbose"
   echo "          -h  help"
   echo ""
   exit -1
   ;;
  v)
   v=1
   ;;
 esac
done
if [ -f /etc/default/ssl ];
then
	. /etc/default/ssl
else
	echo "File $FILE does not exist."
	echo "error exit 1"
	return 1
fi
CONFIG=ipsec.cnf

echo "Nom du client:"
read CLIENT
sed -i -e 's/^commonName_default.*$/commonName_default                = '${CLIENT}.${CN}'/g'  ${PATH_SSL}/${CONFIG}
sed -i -e 's/^organizationalUnitName_default.*$/organizationalUnitName_default    = Certificat Acces IPSec/g'  ${PATH_SSL}/${CONFIG}
echo
if [[ "$v" == "1" ]]; then
	echo "Génération de la clé privé crypté..."
	echo "openssl genrsa  -out ${PATH_SSL}/ipsec/private/${CLIENT}.key 4096 -config ${PATH_SSL}/${CONFIG}"

fi
openssl genrsa  -out ${PATH_SSL}/ipsec/private/${CLIENT}.key 4096 -config ${PATH_SSL}/${CONFIG}
if [[ "$v" == "1" ]]; then
	echo "Création de la clé publique..."
	echo "openssl req -key ${PATH_SSL}/ipsec/private/${CLIENT}.key -sha256 -out ${PATH_SSL}/ipsec/${CLIENT}.req -new -nodes -config ${PATH_SSL}/${CONFIG}"

fi
openssl req -key ${PATH_SSL}/ipsec/private/${CLIENT}.key -sha256 -out ${PATH_SSL}/ipsec/${CLIENT}.req -new -nodes -config ${PATH_SSL}/${CONFIG}
if [[ "$v" == "1" ]]; then
	echo "Signature du certificat [ACCESS] par l'autorité [IPSEC]." 
	echo "openssl ca -name IPSEC -extensions ACCES -in ${PATH_SSL}/ipsec/${CLIENT}.req -out ${PATH_SSL}/ipsec/certs/${CLIENT}.pem -config ${PATH_SSL}/${CONFIG}"

fi
openssl ca -name IPSEC -extensions ACCES -in ${PATH_SSL}/ipsec/${CLIENT}.req -out ${PATH_SSL}/ipsec/certs/${CLIENT}.pem -config ${PATH_SSL}/${CONFIG}
if [[ "$v" == "1" ]]; then
	echo "Portage du jeu de certificat vers un format p12."
	echo "openssl pkcs12 -export -inkey ${PATH_SSL}/ipsec/private/${CLIENT}.key -in ${PATH_SSL}/ipsec/certs/${CLIENT}.pem -name "${CLIENT}.${CN}" -certfile ${PATH_SSL}/ipsec/certs/ca.pem -caname "${CN}" -out ${PATH_SSL}/ipsec/${CLIENT}.p12" 

fi
openssl pkcs12 -export -inkey ${PATH_SSL}/ipsec/private/${CLIENT}.key -in ${PATH_SSL}/ipsec/certs/${CLIENT}.pem -name "${CLIENT}.${CN}" -certfile ${PATH_SSL}/ipsec/certs/ca.pem -caname "${CN}" -out ${PATH_SSL}/ipsec/${CLIENT}.p12 
chmod 400 ${PATH_SSL}/ipsec/private/${CLIENT}.key
chmod 444 ${PATH_SSL}/ipsec/certs/${CLIENT}.pem


Enfin, lançons le dernier script.

[bash]secacces[/bash]

Comments

So empty here ... leave a comment!

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.

Sidebar