diff options
Diffstat (limited to 'src/analysis')
-rwxr-xr-x | src/analysis/db/Makefile.am | 1 | ||||
-rw-r--r-- | src/analysis/db/certs.c | 678 | ||||
-rw-r--r-- | src/analysis/db/certs.h | 59 |
3 files changed, 738 insertions, 0 deletions
diff --git a/src/analysis/db/Makefile.am b/src/analysis/db/Makefile.am index 7e9f177..9dee122 100755 --- a/src/analysis/db/Makefile.am +++ b/src/analysis/db/Makefile.am @@ -4,6 +4,7 @@ noinst_LTLIBRARIES = libanalysisdb.la libanalysiskeys.la libanalysisdb_la_SOURCES = \ cdb.h cdb.c \ + certs.h certs.c \ client.h client.c \ collection-int.h \ collection.h collection.c \ diff --git a/src/analysis/db/certs.c b/src/analysis/db/certs.c new file mode 100644 index 0000000..ac2ff39 --- /dev/null +++ b/src/analysis/db/certs.c @@ -0,0 +1,678 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * certs.h - prototypes pour la gestion des certificats des échanges + * + * Copyright (C) 2017 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Foobar. If not, see <http://www.gnu.org/licenses/>. + */ + + +#include "certs.h" + + +#include <malloc.h> +#include <stdio.h> +#include <openssl/err.h> +#include <openssl/evp.h> +#include <openssl/pem.h> +#include <openssl/rsa.h> +#include <openssl/x509.h> +#include <openssl/x509v3.h> + + +#include <i18n.h> + + +#include "../../gui/panels/log.h" + + + +/* Ajoute une extension à un certificat. */ +static bool add_extension_to_cert(X509 *, X509 *, /*const */char *, /*const */char *); + +/* Ajoute une extension à une requête de signature. */ +static bool add_extension_to_req(STACK_OF(X509_EXTENSION) *, int, /*const */char *); + + + +/****************************************************************************** +* * +* Paramètres : entries = éléments d'identité à supprimer de la mémoire. * +* * +* Description : Libère la mémoire occupée par une définition d'identité. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void free_x509_entries(x509_entries *entries) +{ + if (entries->country != NULL) + free(entries->country); + + if (entries->state != NULL) + free(entries->state); + + if (entries->locality != NULL) + free(entries->locality); + + if (entries->organisation != NULL) + free(entries->organisation); + + if (entries->organisational_unit != NULL) + free(entries->organisational_unit); + + if (entries->common_name != NULL) + free(entries->common_name); + +} + + +/****************************************************************************** +* * +* Paramètres : issuer = certificat de l'autorité émettrice. * +* subj = certificat à la reception. * +* name = nom de l'extension. * +* value = valeur portée par l'extension. * +* * +* Description : Ajoute une extension à un certificat. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static bool add_extension_to_cert(X509 *issuer, X509 *subj, /*const */char *name, /*const */char *value) +{ + bool result; /* Bilan à retourner */ + X509V3_CTX ctx; /* Contexte à conserver */ + X509_EXTENSION *ext; /* Définition d'une extension */ + int ret; /* Bilan d'un ajout */ + + result = false; + + X509V3_set_ctx_nodb(&ctx); + X509V3_set_ctx(&ctx, issuer, subj, NULL, NULL, 0); + + ext = X509V3_EXT_conf(NULL, &ctx, name, value); + + if (ext != NULL) + { + ret = X509_add_ext(subj, ext, -1); + + result = (ret != 0); + + X509_EXTENSION_free(ext); + + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : dir = répertoire d'enregistrement de la création. * +* label = étiquette à coller au certificat produit. * +* valid = durée de validité en secondes. * +* entries = éléments de l'identité à constituer. * +* * +* Description : Crée un certificat de signature racine. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool make_ca(const char *dir, const char *label, unsigned long valid, const x509_entries *entries) +{ + RSA *rsa; /* Clef RSA pour le certificat */ + EVP_PKEY *pk; /* Enveloppe pour clef publique*/ + int ret; /* Bilan d'un appel */ + X509 *x509; /* Certificat X509 à définir */ + X509_NAME *name; /* Désignation du certificat */ + char *filename; /* Chemin d'accès à un fichier */ + FILE *stream; /* Flux ouvert en écriture */ + + rsa = RSA_generate_key(4096, 17, NULL, NULL); + if (rsa == NULL) + { + log_variadic_message(LMT_ERROR, _("Unable to generate RSA key (error=%lu)"), ERR_get_error()); + goto rsa_failed; + } + + pk = EVP_PKEY_new(); + if (pk == NULL) goto pk_failed; + + ret = EVP_PKEY_assign_RSA(pk, rsa); + if (ret != 1) goto asign_failed; + + x509 = X509_new(); + if (x509 == NULL) goto x509_failed; + + ret = X509_set_pubkey(x509, pk); + if (ret != 1) goto ca_failed; + + ret = X509_set_version(x509, 2); + if (ret != 1) goto ca_failed; + + ret = ASN1_INTEGER_set(X509_get_serialNumber(x509), 1); + if (ret != 1) goto ca_failed; + + X509_gmtime_adj(X509_get_notBefore(x509), 0); + X509_gmtime_adj(X509_get_notAfter(x509), valid); + + /* Etablissement d'une identité */ + + name = X509_get_subject_name(x509); + +#define SET_NAME_ENTRY(key, value) \ + do \ + { \ + if (entries->value != NULL) \ + { \ + ret = X509_NAME_add_entry_by_txt(name, key, MBSTRING_UTF8, \ + (unsigned char *)entries->value, -1, -1, 0); \ + if (ret != 1) goto ca_failed; \ + } \ + } \ + while (0) + + SET_NAME_ENTRY("C", country); + + SET_NAME_ENTRY("ST", state); + + SET_NAME_ENTRY("L", locality); + + SET_NAME_ENTRY("O", organisation); + + SET_NAME_ENTRY("OU", organisational_unit); + + SET_NAME_ENTRY("CN", common_name); + +#undef SET_NAME_ENTRY + + ret = X509_set_issuer_name(x509, name); + if (ret != 1) goto ca_failed; + + /* Extensions */ + + if (!add_extension_to_cert(x509, x509, "basicConstraints", "CA:TRUE")) + goto ca_failed; + + if (!add_extension_to_cert(x509, x509, "keyUsage", "critical,keyCertSign,cRLSign")) + goto ca_failed; + + if (!add_extension_to_cert(x509, x509, "subjectKeyIdentifier", "hash")) + goto ca_failed; + + if (!add_extension_to_cert(x509, x509, "nsComment", "\"OpenSSL Generated Certificate\"")) + goto ca_failed; + + /* Signature */ + + ret = X509_sign(x509, pk, EVP_sha256()); + if (ret == 0) goto ca_failed; + + /* Ecriture dans des fichiers */ + + asprintf(&filename, "%s%c%s-key.pem", dir, G_DIR_SEPARATOR, label); + + stream = fopen(filename, "wb"); + if (stream == NULL) goto ca_failed; + + ret = PEM_write_PrivateKey(stream, pk, NULL, NULL, 0, NULL, NULL); + + if (ret != 1) + log_variadic_message(LMT_ERROR, _("Unable to write the CA key into '%s'"), filename); + + fclose(stream); + + free(filename); + + if (ret != 1) + goto ca_failed; + + asprintf(&filename, "%s%c%s-cert.pem", dir, G_DIR_SEPARATOR, label); + + stream = fopen(filename, "wb"); + if (stream == NULL) goto ca_failed; + + ret = PEM_write_X509(stream, x509); + + if (ret != 1) + log_variadic_message(LMT_ERROR, _("Unable to write the CA certificate into '%s'"), filename); + + fclose(stream); + + free(filename); + + if (ret != 1) + goto ca_failed; + + /* Libérations finales */ + + X509_free(x509); + EVP_PKEY_free(pk); + + return true; + + ca_failed: + + X509_free(x509); + + x509_failed: + + EVP_PKEY_free(pk); + + return false; + + asign_failed: + + EVP_PKEY_free(pk); + + pk_failed: + + RSA_free(rsa); + + rsa_failed: + + return false; + +} + + +/****************************************************************************** +* * +* Paramètres : sk = pile d'extension à agrandir. * +* nid = identifiant de l'extension à apporter. * +* value = valeur portée par l'extension. * +* * +* Description : Ajoute une extension à une requête de signature. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static bool add_extension_to_req(STACK_OF(X509_EXTENSION) *sk, int nid, /*const */char *value) +{ + bool result; /* Bilan à retourner */ + X509_EXTENSION *ext; /* Définition d'une extension */ + int ret; /* Bilan d'un ajout */ + + result = false; + + ext = X509V3_EXT_conf_nid(NULL, NULL, nid, value); + + if (ext != NULL) + { + ret = sk_X509_EXTENSION_push(sk, ext); + result = (ret == 1); + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : dir = répertoire d'enregistrement de la création. * +* label = étiquette à coller au certificat produit. * +* entries = éléments de l'identité à constituer. * +* * +* Description : Crée un certificat pour application. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool make_request(const char *dir, const char *label, const x509_entries *entries) +{ + RSA *rsa; /* Clef RSA pour le certificat */ + EVP_PKEY *pk; /* Enveloppe pour clef publique*/ + int ret; /* Bilan d'un appel */ + X509_REQ *x509; /* Certificat X509 à définir */ + X509_NAME *name; /* Désignation du certificat */ + STACK_OF(X509_EXTENSION) *exts; /* Extensions du certificat */ + char *filename; /* Chemin d'accès à un fichier */ + FILE *stream; /* Flux ouvert en écriture */ + + rsa = RSA_generate_key(2048, 17, NULL, NULL); + if (rsa == NULL) + { + log_variadic_message(LMT_ERROR, _("Unable to generate RSA key (error=%lu)"), ERR_get_error()); + goto rsa_failed; + } + + pk = EVP_PKEY_new(); + if (pk == NULL) goto pk_failed; + + ret = EVP_PKEY_assign_RSA(pk, rsa); + if (ret != 1) goto asign_failed; + + x509 = X509_REQ_new(); + if (x509 == NULL) goto x509_failed; + + ret = X509_REQ_set_pubkey(x509, pk); + if (ret != 1) goto asign_failed; + + /* Etablissement d'une identité */ + + name = X509_REQ_get_subject_name(x509); + +#define SET_NAME_ENTRY(key, value) \ + do \ + { \ + if (entries->value != NULL) \ + { \ + ret = X509_NAME_add_entry_by_txt(name, key, MBSTRING_UTF8, \ + (unsigned char *)entries->value, -1, -1, 0); \ + if (ret != 1) goto req_failed; \ + } \ + } \ + while (0) + + SET_NAME_ENTRY("C", country); + + SET_NAME_ENTRY("ST", state); + + SET_NAME_ENTRY("L", locality); + + SET_NAME_ENTRY("O", organisation); + + SET_NAME_ENTRY("OU", organisational_unit); + + SET_NAME_ENTRY("CN", common_name); + +#undef SET_NAME_ENTRY + + /* Extensions */ + + exts = sk_X509_EXTENSION_new_null(); + if (exts == NULL) goto req_failed; + + if (!add_extension_to_req(exts, NID_key_usage, "critical,digitalSignature,keyEncipherment")) + goto exts_failed; + + ret = X509_REQ_add_extensions(x509, exts); + if (ret != 1) goto exts_failed; + + sk_X509_EXTENSION_pop_free(exts, X509_EXTENSION_free); + + /* Signature */ + + ret = X509_REQ_sign(x509, pk, EVP_sha1()); + if (ret == 0) goto req_failed_2; + + ret = X509_REQ_verify(x509, pk); + if (ret != 1) goto req_failed_2; + + /* Ecriture dans des fichiers */ + + asprintf(&filename, "%s%c%s-key.pem", dir, G_DIR_SEPARATOR, label); + + stream = fopen(filename, "wb"); + if (stream == NULL) goto req_failed_2; + + ret = PEM_write_PrivateKey(stream, pk, NULL, NULL, 0, NULL, NULL); + + if (ret != 1) + log_variadic_message(LMT_ERROR, _("Unable to write the CA key into '%s'"), filename); + + fclose(stream); + + free(filename); + + if (ret != 1) + goto req_failed_2; + + asprintf(&filename, "%s%c%s-csr.pem", dir, G_DIR_SEPARATOR, label); + + stream = fopen(filename, "wb"); + if (stream == NULL) goto req_failed_2; + + ret = PEM_write_X509_REQ(stream, x509); + + if (ret != 1) + log_variadic_message(LMT_ERROR, _("Unable to write the CA certificate into '%s'"), filename); + + fclose(stream); + + free(filename); + + if (ret != 1) + goto req_failed_2; + + return true; + + req_failed_2: + + exts_failed: + + sk_X509_EXTENSION_free(exts); + + req_failed: + + X509_REQ_free(x509); + + x509_failed: + + EVP_PKEY_free(pk); + + return false; + + asign_failed: + + EVP_PKEY_free(pk); + + pk_failed: + + RSA_free(rsa); + + rsa_failed: + + return false; + +} + + +/****************************************************************************** +* * +* Paramètres : csr = fichier contenant le certificat à signer. * +* cacert = fichier contenant le certificat de l'autorité. * +* cakey = fichier contenant la clef privée du CA. * +* cert = fichier contenant le certificat signé. * +* valid = durée de validité en secondes. * +* * +* Description : Signe un certificat pour application. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool sign_cert(const char *csr, const char *cacert, const char *cakey, const char *cert, unsigned long valid) +{ + FILE *stream; /* Flux ouvert en écriture */ + X509_REQ *req; /* Certificat X509 à signer */ + EVP_PKEY *pk; /* Enveloppe pour clef publique*/ + X509 *ca_cert; /* Certificat de l'autorité */ + EVP_PKEY *ca_pk; /* Enveloppe pour clef privée */ + X509 *x509; /* Certificat X509 à définir */ + int ret; /* Bilan d'un appel */ + X509_NAME *name; /* Désignation du certificat */ + + /* Chargement de la requête */ + + stream = fopen(csr, "rb"); + if (stream == NULL) goto csr_read_failed; + + req = PEM_read_X509_REQ(stream, NULL, NULL, NULL); + + fclose(stream); + + if (req == NULL) + { + log_variadic_message(LMT_ERROR, _("Unable to read the certificate signing request from '%s'"), cert); + goto csr_read_failed; + } + + pk = X509_REQ_get_pubkey(req); + if (pk == NULL) goto csr_no_pk; + + ret = X509_REQ_verify(req, pk); + if (ret != 1) goto csr_bad_pk; + + /* Chargement des éléments de l'autorité */ + + stream = fopen(cacert, "rb"); + if (stream == NULL) goto cacert_read_failed; + + ca_cert = PEM_read_X509(stream, NULL, NULL, NULL); + + fclose(stream); + + if (ca_cert == NULL) + { + log_variadic_message(LMT_ERROR, _("Unable to read the certificate from '%s'"), cert); + goto cacert_read_failed; + } + + stream = fopen(cakey, "rb"); + if (stream == NULL) goto cakey_read_failed; + + ca_pk = PEM_read_PrivateKey(stream, NULL, NULL, NULL); + + fclose(stream); + + if (ca_pk == NULL) + { + log_variadic_message(LMT_ERROR, _("Unable to read the CA private key from %s"), cakey); + goto cakey_read_failed; + } + + /* Création d'un nouveau certificat */ + + x509 = X509_new(); + if (x509 == NULL) goto x509_failed; + + ret = X509_set_version(x509, 2); + if (ret != 1) goto signing_failed; + + ret = ASN1_INTEGER_set(X509_get_serialNumber(x509), 1); + if (ret != 1) goto signing_failed; + + X509_gmtime_adj(X509_get_notBefore(x509), 0); + X509_gmtime_adj(X509_get_notAfter(x509), valid); + + /* Transfert des informations existantes */ + + ret = X509_set_pubkey(x509, pk); + if (ret != 1) goto signing_failed; + + name = X509_REQ_get_subject_name(req); + + ret = X509_set_subject_name(x509, name); + if (ret != 1) goto signing_failed; + + name = X509_get_subject_name(ca_cert); + + ret = X509_set_issuer_name(x509, name); + if (ret != 1) goto signing_failed; + + /* Extensions */ + + if (!add_extension_to_cert(ca_cert, x509, "basicConstraints", "CA:FALSE")) + goto signing_failed; + + if (!add_extension_to_cert(ca_cert, x509, "keyUsage", "nonRepudiation,digitalSignature,keyEncipherment")) + goto signing_failed; + + if (!add_extension_to_cert(ca_cert, x509, "subjectKeyIdentifier", "hash")) + goto signing_failed; + + if (!add_extension_to_cert(ca_cert, x509, "authorityKeyIdentifier", "keyid,issuer:always")) + goto signing_failed; + + if (!add_extension_to_cert(ca_cert, x509, "nsComment", "\"OpenSSL Generated Certificate\"")) + goto signing_failed; + + /* Signature */ + + ret = X509_sign(x509, ca_pk, EVP_sha256()); + if (ret == 0) goto signing_failed; + + /* Ecriture dans un fichier */ + + stream = fopen(cert, "wb"); + if (stream == NULL) goto signing_failed; + + ret = PEM_write_X509(stream, x509); + + if (ret != 1) + log_variadic_message(LMT_ERROR, _("Unable to write the signed certificate into '%s'"), cert); + + fclose(stream); + + /* Libérations finales */ + + X509_free(x509); + EVP_PKEY_free(ca_pk); + X509_free(ca_cert); + EVP_PKEY_free(pk); + X509_REQ_free(req); + + return true; + + signing_failed: + + X509_free(x509); + + x509_failed: + + EVP_PKEY_free(ca_pk); + + cakey_read_failed: + + X509_free(ca_cert); + + cacert_read_failed: + + csr_bad_pk: + + EVP_PKEY_free(pk); + + csr_no_pk: + + X509_REQ_free(req); + + csr_read_failed: + + return false; + +} diff --git a/src/analysis/db/certs.h b/src/analysis/db/certs.h new file mode 100644 index 0000000..0f7c51d --- /dev/null +++ b/src/analysis/db/certs.h @@ -0,0 +1,59 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * certs.h - prototypes pour la gestion des certificats des échanges + * + * Copyright (C) 2017 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Foobar. If not, see <http://www.gnu.org/licenses/>. + */ + + +#ifndef _ANALYSIS_DB_CERTS_H +#define _ANALYSIS_DB_CERTS_H + + +#include <stdbool.h> + + + +/* Informations pour les certificats X509 */ +typedef struct _x509_entries +{ + char *country; /* Pays */ + char *state; /* Etat */ + char *locality; /* Localité */ + char *organisation; /* Organisation */ + char *organisational_unit; /* Département */ + char *common_name; /* Désignation commune */ + +} x509_entries; + + +/* Libère la mémoire occupée par une définition d'identité. */ +void free_x509_entries(x509_entries *); + +/* Crée un certificat de signature racine. */ +bool make_ca(const char *, const char *, unsigned long, const x509_entries *); + +/* Crée un certificat pour application. */ +bool make_request(const char *, const char *, const x509_entries *); + +/* Signe un certificat pour application. */ +bool sign_cert(const char *, const char *, const char *, const char *, unsigned long); + + + +#endif /* _ANALYSIS_DB_CERTS_H */ |