Différences

Ci-dessous, les différences entre deux révisions de la page.

Lien vers cette vue comparative

Les deux révisions précédentes Révision précédente
Prochaine révision
Révision précédente
ejbca_tls [2024/03/21 10:27] – [Création du modèle de serveur] nekanejbca_tls [2024/03/25 16:07] (Version actuelle) – [Par appel API] nekan
Ligne 80: Ligne 80:
 <image shape="thumbnail">{{ :linux:ejbca:ejbca-057.png |}}</image> <image shape="thumbnail">{{ :linux:ejbca:ejbca-057.png |}}</image>
  
-<callout type="success" title="Opérationnel" icon="true">Voilà, notre autorité de certification est configurée et opérationnelle pour la délivrance de certificat TLS pour nos serveurs webs internes. Il ne reste plus qu'à créer ces fameux certificats. Pour cela, nous avons </callout>+<callout type="success" title="Opérationnel" icon="true">Voilà, notre autorité de certification est configurée et opérationnelle pour la délivrance de certificat TLS pour nos serveurs webs internes. Il ne reste plus qu'à créer ces fameux certificats. Pour cela, nous avons deux possibilités : 
 +  * Via l'interface web 
 +  * Via des appels API 
 +</callout> 
 + 
 +===== Création d'un certificat TLS ===== 
 + 
 +==== Via l'interface ==== 
 + 
 +  * On commence en ligne de commande par créer une configuration pour openssl : 
 +<sxh bash>nano /tmp/openssl.conf</sxh> 
 +<file>[ req ] 
 +default_md = sha256 
 +prompt = no 
 +distinguished_name = dn 
 + 
 +[ dn ] 
 +CN = <FQDN du serveur pour lequel on souhaite créer un certificat> 
 +O = <Mon organisation> 
 +C = <Pays de mon organisation></file> 
 + 
 +  * On crée un clé privée : 
 +<sxh bash>openssl ecparam -genkey -name prime256v1 -out <fqdn>.key</sxh> 
 +  * On crée une requête de certification : 
 +<sxh bash>openssl req -new -key <fqdn>.key -config /tmp/openssl.conf</sxh> 
 +  * On copie cette requête et on va sur l'interface de gestion, soit via l'interface d'administration en cliquant sur le menu ''RA Web'' (tout en bas des menus), soit directement via l'adresse ''https:%%//%%<ejbca-address>/ejbca/ra''
 +  * Une fois sur l'interface, on clique sur ''Make New Request''
 +<image shape="thumbnail">{{ :linux:ejbca:ejbca-003.png |}}</image> 
 +  * On sélectionne : 
 +    * Notre profil de certificat TLS 
 +    * Notre profil de certification pour du TLS 
 +    * On choisit ''Provided by user'' 
 +    * On colle notre requête créée en ligne commande dans ''Upload CSR'' 
 +    * On clique sur ''Upload CSR'' 
 +<image shape="thumbnail">{{ :linux:ejbca:ejbca-058.png |}}</image> 
 +  * On trouve alors le résumé de notre requête. On ajoutera dans le champs ''Username'' de la section ''Provide User Credentials'', le ''FQDN'' principale pour ce certificat puis on pourra le télécharger en cliquant sur un des boutons de téléchargement situé en bas de page, chacun correspondant à un type précis de certificat (selon les besoin de votre serveur web donc) : 
 +<image shape="thumbnail">{{ :linux:ejbca:ejbca-059.png |}}</image> 
 + 
 +==== Par appel API ==== 
 + 
 +  * Pour faciliter la création/interrogation/révocation par appel API, j'ai créé un script ''manage-certificate.sh''
 +<sxh bash>#!/bin/bash 
 + 
 +######################################### 
 +#                                       # 
 +#   Script de gestion des certificats   # 
 +#                                       # 
 +######################################### 
 + 
 +#===========# 
 +# Variables # 
 +#===========# 
 + 
 +# Dossier de travail 
 +WorkingDirectory=/usr/local/batch/certmgr 
 + 
 +# Authentification pour le script 
 +ScriptAuthentication="$WorkingDirectory/script.p12:123456" 
 + 
 +# Adresse de l'autorité de certification 
 +AuthorityAddress="localhost" 
 + 
 +# Profil de certificat 
 +CertificateProfileName="TLS-SERVER" 
 + 
 +# Profil de serveur 
 +EndEntityProfileName="TlsServer" 
 + 
 +# Nom de l'autorité de certification 
 +CertificationAuthorityName="SubCa" 
 + 
 +# Fichier de configuration pour openssl 
 +OpensslConfig="/tmp/cert.conf" 
 + 
 +# Modèle de requête API 
 +ApiTemplate='{"certificate_request":$ApiCsr, "certificate_profile_name":$ApiCp, "end_entity_profile_name":$ApiEep, "certificate_authority_name":$ApiCa, "username":$ApiUser, "password":$ApiPwd}' 
 + 
 +# Dossier racine pour les certificats 
 +CertificateRoorDirectory="$WorkingDirectory/issued" 
 + 
 +# Utilisateur pour l'enregistremet 
 +EnrollmentUser="exploit" 
 + 
 +# Mot de passe pour l'enregistrement 
 +EnrollmentPassword="123456" 
 + 
 +# Fichier de réponse de l'API 
 +ApiResponse="/tmp/api.json" 
 + 
 +# Certificat racine 
 +RootCertificate="$WorkingDirectory/issued/RootCa.pem" 
 + 
 +# Certificat intermédiaire 
 +SubCertificate="$WorkingDirectory/issued/SubCa.pem" 
 + 
 +# Fichier de log 
 +ManageLog="/var/log/manage-certificate.log" 
 + 
 +# Raison de révocation par défaut 
 +RevokeReason="CESSATION_OF_OPERATION" 
 + 
 +# Organisation 
 +Organization="ShyrkaSystem" 
 + 
 +# Pays 
 +Country="FR" 
 + 
 +#===========# 
 +# Fonctions # 
 +#===========# 
 + 
 +# Demande d'aide 
 +display-help() { 
 +        echo "Script de gestion des certificats" 
 +        echo "---------------------------------" 
 +        echo "" 
 +        echo "Usage : ./manage-certificate.sh [add|new|create] <fqdn>" 
 +        echo "                                [renew] <fqdn>" 
 +        echo "                                [revoke|delete] <fqdn> <reason>" 
 +        echo "                                [status] <fqdn>" 
 +        echo "                                [help]" 
 +        echo "" 
 +        echo "Arguments :" 
 +        echo "" 
 +        echo "add                   Crée un certificat pour le domaine <fqdn>." 
 +        echo "create                Identique à add." 
 +        echo "delete                Révoque et supprime le certificat pour le domaine <fqdn>." 
 +        echo "help                  Affiche l'aide." 
 +        echo "new                   Identique à add." 
 +        echo "renew                 Renouvelle un certificat existant (création si inexistant)." 
 +        echo "revoke                Identique à delete." 
 +        echo "status                Affiche le status du certificat pour le domaine <fqdn>." 
 +        echo "" 
 +        echo "<fqdn>                Obligatoire pour toute action et sous la forme d'un FQDN (ex : www.google.fr)." 
 +        echo "<reason>              Facultatif. Indique la raison de la révocation du certificat." 
 +        echo "                      Par défaut, la raison sera CESSATION_OF_OPERATION." 
 +        echo "                      Les raisons disponibles sont :" 
 +        echo "                        - KEY_COMPROMISE" 
 +        echo "                        - CA_COMPROMISE" 
 +        echo "                        - AFFILIATION_CHANGED" 
 +        echo "                        - SUPERSEDED" 
 +        echo "                        - CESSATION_OF_OPERATION" 
 +        echo "                        - CERTIFICATE_HOLD" 
 +        echo "                        - PRIVILEGES_WITHDRAW" 
 +        echo "                        - AA_COMPROMISE" 
 +        echo "" 
 +
 + 
 +# Identifiant d'un certificat 
 +get-id() { 
 +        openssl x509 -noout -serial -in $CertificateRoorDirectory/$1/fullchain.pem | cut -d'=' -f2 | sed 's/://g' 
 +
 + 
 +# Status d'un certificat 
 +get-status() { 
 +        curl -X GET -k -s \ 
 +                --cert-type P12 \ 
 +                --cert "$ScriptAuthentication"
 +                --header "accept: application/json"
 +                "https://${AuthorityAddress}/ejbca/ejbca-rest-api/v1/certificate/CN%3D${1}%2CO%3D${2//[^[:alnum:]]/}%2CC%3DFR/${3}/revocationstatus"
 +                | jq . 
 +
 + 
 +# Révocation d'un certificat 
 +revoke-certificate() { 
 +        curl -X PUT -k -s \ 
 +                --cert-type P12 \ 
 +                --cert "$ScriptAuthentication"
 +                --header "accept: application/json"
 +                "https://${AuthorityAddress}/ejbca/ejbca-rest-api/v1/certificate/CN%3D${1}%2CO%3D${2//[^[:alnum:]]/}%2CC%3DFR/${3}/revoke?reason=${4}"
 +                | jq . 
 +
 + 
 +# Création du certificat 
 +create-certificate() { 
 +        # Création du JSON pour la requête API 
 +        Json=$(jq -n \ 
 +                --arg ApiCsr "$1"
 +                --arg ApiCp "$2"
 +                --arg ApiEep "$3"
 +                --arg ApiCa "$4"
 +                --arg ApiUser "$5"
 +                --arg ApiPwd "$6"
 +                "$7"
 + 
 +        # Envoi de la requête 
 +        curl -X POST -s -k \ 
 +                --cert-type P12 \ 
 +                --cert "$ScriptAuthentication"
 +                --header 'Content-Type: application/json'
 +                --data "$Json"
 +                "https://${AuthorityAddress}/ejbca/ejbca-rest-api/v1/certificate/pkcs10enroll"
 +                | jq . 
 +
 + 
 +#===========# 
 +# Arguments # 
 +#===========# 
 + 
 +# Initialisation 
 +action="null" 
 +fqdn="null" 
 + 
 +case $1 in 
 +        "help"
 +                display-help 
 +                exit 0 
 +                ;; 
 +        "add"|"new"|"create"|"revoke"|"delete"|"status"|"renew"
 +                action="$1" 
 +                fqdn="$2" 
 +                reason="$3" 
 +                ;; 
 +        *) 
 +                echo "Erreur : Argument $1 inconnu" 
 +                echo "" 
 +                display-help 
 +                exit 2 
 +                ;; 
 +esac 
 + 
 +# Vérification 
 +if [ "$action" == "null" ]; then 
 +        # Aucune action, affichage de l'aide 
 +        display-help 
 +        exit 1 
 +else 
 +        # Vérification du fqdn 
 +        if [ "$fqdn" == "null" ]; then 
 +                echo "$(date '+%F %X %Z') - WARN - un nom de domaine est obligatoire." | tee -a $ManageLog 
 +                echo "" 
 +                display-help 
 +                exit 1 
 +        elif [[ $fqdn =~ ^([a-z0-9]+([a-z0-9]|\-)*\.)+[a-z]{2,} ]]; then 
 +                true 
 +        else 
 +                echo "$(date '+%F %X %Z') - CRIT - le domaine $fqdn n'est pas valide." | tee -a $ManageLog 
 +                exit 2 
 +        fi 
 +fi 
 + 
 +# Log 
 +echo "$(date '+%F %X %Z') - INFO - Démarrage (Action: $action | Domaine: $fqdn)" | tee -a $ManageLog 
 + 
 +#============# 
 +# Traitement # 
 +#============# 
 + 
 +if [ "$action" == "add" -o "$action" == "create" -o "$action" == "new" -o "$action" == "renew" ]; then 
 + 
 +        # Vérification de l'état actuel 
 +        if [ "$action" == "renew" ]; then 
 +                # Renouvellement demandé 
 +                if [ ! -d $CertificateRoorDirectory/$fqdn ]; then 
 +                        # Pas de certificat existant, création du dossier et de la clé privée 
 +                        echo "$(date '+%F %X %Z') - WARN - Certificat inexistant. Création du certificat au lieu du renouvellement." | tee -a $ManageLog 
 +                        mkdir -p $CertificateRoorDirectory/$fqdn 
 +                        echo "$(date '+%F %X %Z') - INFO - Création du dossier $CertificateRoorDirectory/$fqdn" | tee -a $ManageLog 
 +                        openssl ecparam -genkey -name prime256v1 -out $CertificateRoorDirectory/$fqdn/private.key 
 +                        echo "$(date '+%F %X %Z') - INFO - Création de la clé privée $CertificateRoorDirectory/$fqdn/private.key" | tee -a $ManageLog 
 +                elif [ ! -f $CertificateRoorDirectory/$fqdn/private.key ]; then 
 +                        # Pas de clé privée à utiliser 
 +                        echo "$(date '+%F %X %Z') - WARN - Clé privée absente. Création d'une nouvelle clé." | tee -a $ManageLog 
 +                        openssl ecparam -genkey -name prime256v1 -out $CertificateRoorDirectory/$fqdn/private.key 
 +                else 
 +                        # Vérification de la clé privée 
 +                        CertificateChecksum=$(openssl x509 -noout -pubkey -in $CertificateRoorDirectory/$fqdn/fullchain.pem 2>&1 | sha256sum | awk '{print $1}'
 +                        PrivateKeyChecksum=$(openssl pkey -pubout -in $CertificateRoorDirectory/$fqdn/private.key 2>&1 | sha256sum | awk '{print $1}'
 +                        if [ "$CertificateChecksum" == "$PrivateKeyChecksum" ]; then 
 +                                # Clé valide 
 +                                echo "$(date '+%F %X %Z') - INFO - Clé privée valide." | tee -a $ManageLog 
 +                        else 
 +                                # Clé invalide 
 +                                echo "$(date '+%F %X %Z') - WARN - Clé privée invalide. Création d'une nouvelle clé." | tee -a $ManageLog 
 +                                openssl ecparam -genkey -name prime256v1 -out $CertificateRoorDirectory/$fqdn/private.key 
 +                        fi 
 +                fi 
 +        else 
 +                # Création demandée 
 +                if [ -f $CertificateRoorDirectory/$fqdn/fullchain.pem ]; then 
 +                        # Certificat déjà existant 
 +                        echo "$(date '+%F %X %Z') - WARN - Un certificat pour $fqdn existe déjà." | tee -a $ManageLog 
 +                        echo "$(date '+%F %X %Z') - INFO - Vous pouvez le renouveller à la place sinon il faudra le révoquer au préalable." | tee -a $ManageLog 
 +                        echo "$(date '+%F %X %Z') - INFO - Si vous êtes sûr que ce certificat est déjà révoqué, il est nécessaire de supprimer le dossier $CertificateRoorDirectory/$fqdn." | tee -a $ManageLog 
 +                        exit 1 
 +                elif [ ! -d $CertificateRoorDirectory/$fqdn ]; then 
 +                        # Création du répertoire pour le certificat 
 +                        mkdir -p $CertificateRoorDirectory/$fqdn 
 +                        echo "$(date '+%F %X %Z') - INFO - Création du dossier $CertificateRoorDirectory/$fqdn" | tee -a $ManageLog 
 + 
 +                        # Création de la clé privée 
 +                        openssl ecparam -genkey -name prime256v1 -out $CertificateRoorDirectory/$fqdn/private.key 
 +                        echo "$(date '+%F %X %Z') - INFO - Création de la clé privée $CertificateRoorDirectory/$fqdn/private.key" | tee -a $ManageLog 
 +                else 
 +                        # Création de la clé privée 
 +                        openssl ecparam -genkey -name prime256v1 -out $CertificateRoorDirectory/$fqdn/private.key 
 +                        echo "$(date '+%F %X %Z') - INFO - Création de la clé privée $CertificateRoorDirectory/$fqdn/private.key" | tee -a $ManageLog 
 +                fi 
 +        fi 
 + 
 +        # Création de la configuration pour openssl 
 +        if [ -e $OpensslConfig  ]; then 
 +                rm -f $OpensslConfig 
 +        fi 
 + 
 +        echo "[ req ]" > $OpensslConfig 
 +        echo "default_md = sha256" >> $OpensslConfig 
 +        echo "prompt = no" >> $OpensslConfig 
 +        echo "distinguished_name = dn" >> $OpensslConfig 
 +        echo "" >> $OpensslConfig 
 +        echo "[ dn ]" >> $OpensslConfig 
 +        echo "CN = $fqdn" >> $OpensslConfig 
 +        echo "O = $Organization" >> $OpensslConfig 
 +        echo "C = $Country" >> $OpensslConfig 
 + 
 +        # Création de la requête 
 +        CsrContent=$(openssl req -new -key $CertificateRoorDirectory/$fqdn/private.key -config $OpensslConfig) 
 + 
 +        # Envoi de la requête 
 +        CreateRequest=$(create-certificate "$CsrContent" "$CertificateProfileName" "$EndEntityProfileName" "$CertificationAuthorityName" "$EnrollmentUser" "$EnrollmentPassword" "$ApiTemplate"
 + 
 +        # Extraction du certificat 
 +        echo $CreateRequest | jq -r .certificate > $CertificateRoorDirectory/$fqdn/cert.b64 
 + 
 +        # Conversion du DER en binaire 
 +        openssl base64 -d -A -in $CertificateRoorDirectory/$fqdn/cert.b64 -out $CertificateRoorDirectory/$fqdn/cert.bin 
 + 
 +        # Conversion du binaire en PEM 
 +        openssl x509 -inform der -in $CertificateRoorDirectory/$fqdn/cert.bin -out $CertificateRoorDirectory/$fqdn/fullchain.pem 
 + 
 +        # Ajout des certificats racines et intermédiaires 
 +        cat $SubCertificate >> $CertificateRoorDirectory/$fqdn/fullchain.pem 
 +        cat $RootCertificate >> $CertificateRoorDirectory/$fqdn/fullchain.pem 
 + 
 +        # Log 
 +        echo "$(date '+%F %X %Z') - INFO - Création du certificat $CertificateRoorDirectory/$fqdn/fullchain.pem" | tee -a $ManageLog 
 +        exit 0 
 + 
 +elif [ "$action" == "status" ]; then 
 + 
 +        # Recherche du n° de série du dernier certificat 
 +        CertificateSerialNumber=$(get-id $fqdn) 
 + 
 +        # Demande du status 
 +        CertificateStatusRequest=$(get-status $CertificationAuthorityName $Organization $CertificateSerialNumber) 
 + 
 +        # Extraction des données 
 +        CertificateExtractedStatus=$(echo $CertificateStatusRequest | jq .revoked) 
 +        if [ "$CertificateExtractedStatus" == "false" ]; then 
 +                CertificateStatus="actif" 
 +        else 
 +                CertificateStatus="révoqué" 
 +        fi 
 +        CertificateDetails=$(echo $CertificateStatusRequest | jq .revocation_reason) 
 + 
 +        # Log 
 +        echo "$(date '+%F %X %Z') - INFO - Certificat $fqdn (S/N: $CertificateSerialNumber) en état : $CertificateStatus (Détails: $CertificateDetails)" | tee -a $ManageLog 
 +        exit 0 
 + 
 +elif [ "$action" == "delete" -o "$action" == "revoke" ]; then 
 + 
 +        # Validation de la raison de la révocation 
 +        if [ -z "$reason" ]; then 
 +                echo "$(date '+%F %X %Z') - INFO - Aucune raison de révocation indiqué, utilisation de la raison par défaut ($RevokeReason)." | tee -a $ManageLog 
 +        else 
 +                case ${reason^^} in 
 +                        "KEY_COMPROMISE"|"CA_COMPROMISE"|"AFFILIATION_CHANGED"|"SUPERSEDED"|"CESSATION_OF_OPERATION"|"CERTIFICATE_HOLD"|"PRIVILEGES_WITHDRAW"|"AA_COMPROMISE"
 +                                RevokeReason=${reason^^} 
 +                                echo "$(date '+%F %X %Z') - INFO - Raison de la révocation : $RevokeReason" | tee -a $ManageLog 
 +                                ;; 
 +                        *) 
 +                                echo "$(date '+%F %X %Z') - WARN - Raison de révocation indiquée ($reason) indisponible, utilisation de la raison par défaut ($RevokeReason)." | tee -a $ManageLog 
 +                                ;; 
 +                esac 
 +        fi 
 + 
 +        # Recherche du n° de série du dernier certificat 
 +        CertificateSerialNumber=$(get-id $fqdn) 
 + 
 +        # Demande du status 
 +        CertificateStatusRequest=$(get-status $CertificationAuthorityName $Organization $CertificateSerialNumber) 
 + 
 +        # Extraction des données 
 +        CertificateExtractedStatus=$(echo $CertificateStatusRequest | jq .revoked) 
 + 
 +        # Vérification 
 +        if [ "$CertificateExtractedStatus" == "false" ]; then 
 + 
 +                # Révocation 
 +                RevokeRequest=$(revoke-certificate $CertificationAuthorityName $Organization $CertificateSerialNumber $RevokeReason) 
 + 
 +                # Extraction des données 
 +                RevokeStatus=$(echo $RevokeRequest | jq .revoked) 
 +                RevokeMessage=$(echo $RevokeRequest | jq .message) 
 + 
 +                # Log 
 +                if [ "$RevokeStatus" == "true" ]; then 
 +                        /usr/bin/rm -rf $CertificateRoorDirectory/$fqdn 
 +                        echo "$(date '+%F %X %Z') - INFO - Certificat $fqdn révoqué et supprimé (API Message: $RevokeMessage)" | tee -a $ManageLog 
 +                        exit 0 
 +                else 
 +                        echo "$(date '+%F %X %Z') - CRIT - Echec de la révocation pour $fqdn (API Message: $RevokeMessage)" | tee -a $ManageLog 
 +                        echo "$(date '+%F %X %Z') - INFO - Fichiers conservés dans $CertificateRoorDirectory/$fqdn" 
 +                        exit 2 
 +                fi 
 +        else 
 +                # Log 
 +                echo "$(date '+%F %X %Z') - WARN - Certificat $fqdn déjà révoqué)" | tee -a $ManageLog 
 +                exit 1 
 +        fi 
 +else 
 +        # Action inconnue 
 +        echo "$(date '+%F %X %Z') - UNKN - L'action $action n'est pas prévue" | tee -a $ManageLog 
 +        exit 3 
 +fi 
 +</sxh> 
 + 
 +  * Il suffit de modifier les variables : 
 +    * ''WorkingDirectory'' : Le dossier dans lesquels sont stockés les certificats créés, le certificat du script, etc... 
 +    * ''AuthorityAddress'' : L'adresse de votre autorité de certification. 
 +    * ''CertificateProfileName'' : Le nom de votre profil de certificat. 
 +    * ''EndEntityProfileName'' : Le nom de votre profil de serveur. 
 +    * ''CertificationAuthorityName'' : Le nom de votre autorité de certification. 
 +    * ''EnrollmentUser'' : Un nom de compte à associer aux certificats créés. Si il n'existe pas, il sera créé automatiquement. 
 +    * ''EnrollmentPassword'' : Le mot de passe du compte précédent. 
 +    * ''Organization'' : Le nom de votre organisation. 
 +    * ''Country'' : Votre pays. 
 +  * On place le certificat du script dans ''$WorkingDirectory'' et les certificats racine et intermédiaire au format ''PEM'' dans ''$WorkingDirectory/issued/''
 +  * Il ne reste plus qu'à lancer le script. 
  
-{{ :underconstruction-copy-300x150.jpg |}} 
  
 ~~DISCUSSION~~ ~~DISCUSSION~~
  • ejbca_tls.1711013243.txt.gz
  • Dernière modification : 2024/03/21 10:27
  • de nekan