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 11:25] nekanejbca_tls [2024/03/25 16:07] (Version actuelle) – [Par appel API] nekan
Ligne 119: Ligne 119:
  
 ==== Par appel API ==== ==== 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.1711016725.txt.gz
  • Dernière modification : 2024/03/21 11:25
  • de nekan