From 7778a0c082c4969ed6184883b2d96d8a851def99 Mon Sep 17 00:00:00 2001
From: Cyrille Bagard <nocbos@gmail.com>
Date: Tue, 7 Feb 2017 23:41:07 +0100
Subject: Provided a way to create SSL certificates.

---
 ChangeLog                                |  23 ++
 plugins/pychrysa/analysis/db/Makefile.am |   1 +
 plugins/pychrysa/analysis/db/certs.c     | 327 +++++++++++++++
 plugins/pychrysa/analysis/db/certs.h     |  42 ++
 plugins/pychrysa/analysis/db/module.c    |   2 +
 src/analysis/db/Makefile.am              |   1 +
 src/analysis/db/certs.c                  | 678 +++++++++++++++++++++++++++++++
 src/analysis/db/certs.h                  |  59 +++
 tests/analysis/db/__init__.py            |   0
 tests/analysis/db/certs.py               | 112 +++++
 10 files changed, 1245 insertions(+)
 create mode 100644 plugins/pychrysa/analysis/db/certs.c
 create mode 100644 plugins/pychrysa/analysis/db/certs.h
 create mode 100644 src/analysis/db/certs.c
 create mode 100644 src/analysis/db/certs.h
 create mode 100644 tests/analysis/db/__init__.py
 create mode 100644 tests/analysis/db/certs.py

diff --git a/ChangeLog b/ChangeLog
index babdb96..95add10 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,26 @@
+17-02-07  Cyrille Bagard <nocbos@gmail.com>
+
+	* plugins/pychrysa/analysis/db/Makefile.am:
+	Add the 'certs.[ch]' files to libpychrysaanalysisdb_la_SOURCES.
+
+	* plugins/pychrysa/analysis/db/certs.c:
+	* plugins/pychrysa/analysis/db/certs.h:
+	New entries: add some Python bindings for creating certificates.
+
+	* plugins/pychrysa/analysis/db/module.c:
+	Update code.
+
+	* src/analysis/db/Makefile.am:
+	Add the 'certs.[ch]' files to libanalysisdb_la_SOURCES.
+
+	* src/analysis/db/certs.c:
+	* src/analysis/db/certs.h:
+	New entries: provide a way to create SSL certificates.
+
+	* tests/analysis/db/__init__.py:
+	* tests/analysis/db/certs.py:
+	New entries: provide some tests.
+
 17-01-31  Cyrille Bagard <nocbos@gmail.com>
 
 	* plugins/pychrysa/analysis/db/items/comment.c:
diff --git a/plugins/pychrysa/analysis/db/Makefile.am b/plugins/pychrysa/analysis/db/Makefile.am
index 2de2a16..a6bb701 100644
--- a/plugins/pychrysa/analysis/db/Makefile.am
+++ b/plugins/pychrysa/analysis/db/Makefile.am
@@ -2,6 +2,7 @@
 noinst_LTLIBRARIES = libpychrysaanalysisdb.la
 
 libpychrysaanalysisdb_la_SOURCES =		\
+	certs.h certs.c						\
 	collection.h collection.c			\
 	item.h item.c						\
 	module.h module.c
diff --git a/plugins/pychrysa/analysis/db/certs.c b/plugins/pychrysa/analysis/db/certs.c
new file mode 100644
index 0000000..e0358d1
--- /dev/null
+++ b/plugins/pychrysa/analysis/db/certs.c
@@ -0,0 +1,327 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * certs.c - équivalent Python du fichier "analysis/db/certs.c"
+ *
+ * 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 this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+
+#include "certs.h"
+
+
+#include <pygobject.h>
+#include <string.h>
+
+
+#include <i18n.h>
+#include <analysis/db/certs.h>
+
+
+#include "../../helpers.h"
+
+
+
+/* Traduit en version native une identité de certificat. */
+static bool py_certs_fill_x509_entries(PyObject *, x509_entries *);
+
+/* Crée un certificat de signature racine. */
+static PyObject *py_certs_make_ca(PyObject *, PyObject *);
+
+/* Crée un certificat pour application. */
+static PyObject *py_certs_make_request(PyObject *, PyObject *);
+
+/* Signe un certificat pour application. */
+static PyObject *py_certs_sign_cert(PyObject *, PyObject *);
+
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : dict = ensemble de propriétés renseignées.                   *
+*                out  = résumé des entrées regroupées. [OUT]                  *
+*                                                                             *
+*  Description : Traduit en version native une identité de certificat.        *
+*                                                                             *
+*  Retour      : Bilan de l'opération.                                        *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static bool py_certs_fill_x509_entries(PyObject *dict, x509_entries *out)
+{
+    bool result;                            /* Bilan à retourner           */
+    PyObject *value;                        /* Valeur au format Python     */
+
+#define TRANSLATE_ENTRY(name, dest)                                                             \
+    do                                                                                          \
+    {                                                                                           \
+        value = PyDict_GetItemString(dict, name);                                               \
+        if (value != NULL)                                                                      \
+        {                                                                                       \
+            result = PyUnicode_Check(value);                                                    \
+            if (result)                                                                         \
+                out->dest = strdup((char *)PyUnicode_DATA(value));                              \
+            else                                                                                \
+                PyErr_Format(PyExc_TypeError, _("The %s property must be a string."), name);    \
+        }                                                                                       \
+    }                                                                                           \
+    while (0)
+
+    result = true;
+
+    memset(out, 0, sizeof(x509_entries));
+
+    TRANSLATE_ENTRY("C", country);
+
+    if (result)
+        TRANSLATE_ENTRY("ST", state);
+
+    if (result)
+        TRANSLATE_ENTRY("L", locality);
+
+    if (result)
+        TRANSLATE_ENTRY("O", organisation);
+
+    if (result)
+        TRANSLATE_ENTRY("OU", organisational_unit);
+
+    if (result)
+        TRANSLATE_ENTRY("CN", common_name);
+
+    if (!result)
+        free_x509_entries(out);
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : self = NULL car méthode statique.                            *
+*                args = paramètres à transmettre à l'appel natif.             *
+*                                                                             *
+*  Description : Crée un certificat de signature racine.                      *
+*                                                                             *
+*  Retour      : Bilan de l'opération.                                        *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static PyObject *py_certs_make_ca(PyObject *self, PyObject *args)
+{
+    PyObject *result;                       /* Désignation à retourner     */
+    const char *dir;                        /* Répertoire de sortie        */
+    const char *label;                      /* Nom principal du certificat */
+    unsigned long valid;                    /* Durée de validité en sec.   */
+    PyObject *dict;                         /* Détails identitaires        */
+    int ret;                                /* Bilan de lecture des args.  */
+    x509_entries entries;                   /* Définition d'une identité   */
+    bool status;                            /* Bilan d'une constitution    */
+
+    ret = PyArg_ParseTuple(args, "sskO!", &dir, &label, &valid, &PyDict_Type, &dict);
+    if (!ret) return NULL;
+
+    status = py_certs_fill_x509_entries(dict, &entries);
+    if (!status) return NULL;
+
+    status = make_ca(dir, label, valid, &entries);
+
+    free_x509_entries(&entries);
+
+    result = status ? Py_True : Py_False;
+
+    Py_INCREF(result);
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : self = NULL car méthode statique.                            *
+*                args = paramètres à transmettre à l'appel natif.             *
+*                                                                             *
+*  Description : Crée un certificat pour application.                         *
+*                                                                             *
+*  Retour      : Bilan de l'opération.                                        *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static PyObject *py_certs_make_request(PyObject *self, PyObject *args)
+{
+    PyObject *result;                       /* Désignation à retourner     */
+    const char *dir;                        /* Répertoire de sortie        */
+    const char *label;                      /* Nom principal du certificat */
+    PyObject *dict;                         /* Détails identitaires        */
+    int ret;                                /* Bilan de lecture des args.  */
+    x509_entries entries;                   /* Définition d'une identité   */
+    bool status;                            /* Bilan d'une constitution    */
+
+    ret = PyArg_ParseTuple(args, "ssO!", &dir, &label, &PyDict_Type, &dict);
+    if (!ret) return NULL;
+
+    status = py_certs_fill_x509_entries(dict, &entries);
+    if (!status) return NULL;
+
+    status = make_request(dir, label, &entries);
+
+    free_x509_entries(&entries);
+
+    result = status ? Py_True : Py_False;
+
+    Py_INCREF(result);
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : self = NULL car méthode statique.                            *
+*                args = paramètres à transmettre à l'appel natif.             *
+*                                                                             *
+*  Description : Signe un certificat pour application.                        *
+*                                                                             *
+*  Retour      : Bilan de l'opération.                                        *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static PyObject *py_certs_sign_cert(PyObject *self, PyObject *args)
+{
+    PyObject *result;                       /* Désignation à retourner     */
+    const char *csr;                        /* Requête à satisfaire        */
+    const char *cacert;                     /* Certificat de confiance     */
+    const char *cakey;                      /* Clef de ce certificat       */
+    const char *cert;                       /* Certificat en sortie        */
+    unsigned long valid;                    /* Durée de validité en sec.   */
+    int ret;                                /* Bilan de lecture des args.  */
+    bool status;                            /* Bilan de l'opération        */
+
+    ret = PyArg_ParseTuple(args, "ssssk", &csr, &cacert, &cakey, &cert, &valid);
+    if (!ret) return NULL;
+
+    status = sign_cert(csr, cacert, cakey, cert, valid);
+
+    result = status ? Py_True : Py_False;
+
+    Py_INCREF(result);
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : -                                                            *
+*                                                                             *
+*  Description : Fournit un accès à une définition de type à diffuser.        *
+*                                                                             *
+*  Retour      : Définition d'objet pour Python.                              *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+PyTypeObject *get_python_certs_type(void)
+{
+    static PyMethodDef py_certs_methods[] = {
+
+        { "make_ca", py_certs_make_ca,
+          METH_VARARGS | METH_STATIC,
+          "make_ca(dir, label, valid, entries, /)\n--\n\nCreate a certificate authority."
+        },
+        { "make_request", py_certs_make_request,
+          METH_VARARGS | METH_STATIC,
+          "make_request(dir, label, entries, /)\n--\n\nCreate a certificate sign request."
+        },
+        { "sign_cert", py_certs_sign_cert,
+          METH_VARARGS | METH_STATIC,
+          "sign_cert(csr, cacert, cakey, cert, valid, /)\n--\n\nSign a certificate sign request.."
+        },
+        { NULL }
+
+    };
+
+    static PyGetSetDef py_certs_getseters[] = {
+
+        { NULL }
+
+    };
+
+    static PyTypeObject py_certs_type = {
+
+        PyVarObject_HEAD_INIT(NULL, 0)
+
+        .tp_name        = "pychrysalide.analysis.db.certs",
+        .tp_basicsize   = sizeof(PyGObject),
+
+        .tp_flags       = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
+
+        .tp_doc         = "PyChrysalide support for DataBase certicates",
+
+        .tp_methods     = py_certs_methods,
+        .tp_getset      = py_certs_getseters,
+
+    };
+
+    return &py_certs_type;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : module = module dont la définition est à compléter.          *
+*                                                                             *
+*  Description : Prend en charge l'objet 'pychrysalide....db.certs'.          *
+*                                                                             *
+*  Retour      : Bilan de l'opération.                                        *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+bool register_python_certs(PyObject *module)
+{
+    PyTypeObject *py_certs_type;            /* Type Python pour 'certs'    */
+    int ret;                                /* Bilan d'un appel            */
+
+    py_certs_type = get_python_certs_type();
+
+    py_certs_type->tp_new = PyType_GenericNew;
+
+    if (PyType_Ready(py_certs_type) != 0)
+        return false;
+
+    Py_INCREF(py_certs_type);
+    ret = PyModule_AddObject(module, "certs", (PyObject *)py_certs_type);
+
+    return (ret == 0);
+
+}
diff --git a/plugins/pychrysa/analysis/db/certs.h b/plugins/pychrysa/analysis/db/certs.h
new file mode 100644
index 0000000..f7537e5
--- /dev/null
+++ b/plugins/pychrysa/analysis/db/certs.h
@@ -0,0 +1,42 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * certs.h - prototypes pour l'équivalent Python du fichier "analysis/db/certs.h"
+ *
+ * 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 this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+
+#ifndef _PLUGINS_PYCHRYSA_ANALYSIS_DB_CERTS_H
+#define _PLUGINS_PYCHRYSA_ANALYSIS_DB_CERTS_H
+
+
+#include <Python.h>
+#include <stdbool.h>
+
+
+
+/* Fournit un accès à une définition de type à diffuser. */
+PyTypeObject *get_python_certs_type(void);
+
+/* Prend en charge l'objet 'pychrysalide.analysis.db.certs'. */
+bool register_python_certs(PyObject *);
+
+
+
+#endif  /* _PLUGINS_PYCHRYSA_ANALYSIS_DB_CERTSS_H */
diff --git a/plugins/pychrysa/analysis/db/module.c b/plugins/pychrysa/analysis/db/module.c
index 0ae6dda..eac3641 100644
--- a/plugins/pychrysa/analysis/db/module.c
+++ b/plugins/pychrysa/analysis/db/module.c
@@ -28,6 +28,7 @@
 #include <assert.h>
 
 
+#include "certs.h"
 #include "collection.h"
 #include "item.h"
 #include "items/module.h"
@@ -80,6 +81,7 @@ bool add_analysis_db_module_to_python_module(PyObject *super)
 
     result = true;
 
+    result &= register_python_certs(module);
     result &= register_python_db_collection(module);
     result &= register_python_db_item(module);
 
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 */
diff --git a/tests/analysis/db/__init__.py b/tests/analysis/db/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/tests/analysis/db/certs.py b/tests/analysis/db/certs.py
new file mode 100644
index 0000000..c4dfa32
--- /dev/null
+++ b/tests/analysis/db/certs.py
@@ -0,0 +1,112 @@
+#!/usr/bin/python3-dbg
+# -*- coding: utf-8 -*-
+
+
+# Tests validant la génération de certificats
+
+
+from chrysacase import ChrysalideTestCase
+from pychrysalide.analysis.db import certs
+import shutil
+import subprocess
+import tempfile
+
+
+class TestRestrictedContent(ChrysalideTestCase):
+    """TestCase for analysis.db.certs."""
+
+    @classmethod
+    def setUpClass(cls):
+
+        super(TestRestrictedContent, cls).setUpClass()
+
+        cls._tmppath = tempfile.mkdtemp()
+
+        cls.log('Using temporary directory "%s"' % cls._tmppath)
+
+
+    @classmethod
+    def tearDownClass(cls):
+
+        super(TestRestrictedContent, cls).tearDownClass()
+
+        cls.log('Delete directory "%s"' % cls._tmppath)
+
+        shutil.rmtree(cls._tmppath)
+
+
+    def checkOutput(self, cmd, expected):
+        """Run a command and check its output."""
+
+        output = ''
+
+        try:
+            output = subprocess.check_output(cmd, stderr=subprocess.STDOUT, shell=True)
+        except:
+            pass
+
+        self.assertEqual(output, expected)
+
+
+    def testMakeCA(self):
+        """Check for building a valid CA."""
+
+        identity = {
+
+            'C': 'UK',
+            'CN': 'OpenSSL Group'
+
+        }
+
+        ret = certs.make_ca(self._tmppath, 'ca', 3650 * 24 * 60 * 60, identity)
+        self.assertTrue(ret)
+
+        cmd = 'openssl x509 -in %s/ca-cert.pem -subject -noout' % self._tmppath
+
+        expected = b'subject= /C=UK/CN=OpenSSL Group\n'
+
+        self.checkOutput(cmd, expected)
+
+        cmd = 'openssl verify -CApath %s -CAfile %s/ca-cert.pem %s/ca-cert.pem' \
+              % (self._tmppath, self._tmppath, self._tmppath)
+
+        expected = bytes('%s/ca-cert.pem: OK\n' % self._tmppath, 'utf-8')
+
+        self.checkOutput(cmd, expected)
+
+
+    def testMakeCSR(self):
+        """Check for requesting a valid signing request."""
+
+        identity = {
+
+            'C': 'UK',
+            'CN': 'OpenSSL Group'
+
+        }
+
+        ret = certs.make_request(self._tmppath, 'server', identity);
+        self.assertTrue(ret)
+
+
+    def testSignCert(self):
+        """Check for properly signing a certificate."""
+
+        ret = certs.sign_cert('%s/server-csr.pem' % self._tmppath, '%s/ca-cert.pem' % self._tmppath, \
+                              '%s/ca-key.pem' % self._tmppath, '%s/server-cert.pem' % self._tmppath, \
+                              3650 * 24 * 60 * 60)
+        self.assertTrue(ret)
+
+        cmd = 'openssl x509 -in %s/server-cert.pem -subject -noout' % self._tmppath
+
+        expected = b'subject= /C=UK/CN=OpenSSL Group\n'
+
+        self.checkOutput(cmd, expected)
+
+        cmd = 'openssl verify -CApath %s -CAfile %s/ca-cert.pem %s/server-cert.pem' \
+              % (self._tmppath, self._tmppath, self._tmppath)
+
+        expected = bytes('%s/server-cert.pem: OK\n' % self._tmppath, 'utf-8')
+
+        self.checkOutput(cmd, expected)
+
-- 
cgit v0.11.2-87-g4458