C# et GNU PG (GPG)

C# ET GNU PG (GPG)

Préambule

Dans ce tutoriel, nous verrons comment utiliser GPG au sein d'une application .NET. Il n'existe pas d'API ou de SDK permettant de le faire. Pour cela, nous utilisons l'exécutable de GPG ainsi que la classe Process (System.Diagnostics.Process). Une petite approche qui permet de pouvoir étendre son utilisation avec .NET.

Introduction

Les outils associés à la cryptographie et permettant le chiffrement de données sont très nombreux. Le Framework .NET fournit nativement des classes permettant de gérer bien des aspects de la cryptographie (voir System.Security.Cryptography).

Au sein d'un projet, je me suis retrouvé récemment à devoir créer un système permettant de déchiffrer des fichiers avec un algorithme de cryptographie à clé publique et clé privée (cryptographie asymétrique). J'ai opté pour GPG (logiciel libre).

Après plusieurs recherches, j'ai pu constater qu'il n'existe malheureusement pas d'API ou de SDK permettant d'utiliser GPG avec C#. Pour cela, la méthode la plus efficace reste l'utilisation de l'outil à partir de la classe Process (System.Diagnostics.Process).

J'ai donc écrit une petite classe permettant de pouvoir utiliser GPG (uniquement le déchiffrement) donc au sein d'une application .NET. C'est une première approche qui peut nettement être améliorée.

Pré-requis

Outre l'environnement Microsoft .NET ( ), il faut :

  • Avoir installé GPG
  • Disposer de la clé privée (utile pour déchiffrer...)
  • L'intégrer dans la liste de clés de GPG (avec la commande gpg --import fullpathtoyourkey)
  • ET un fichier qui contiendra le mot de passe associé à la clé privée (et qui servira de flux de données dans la commande de déchiffrement).

Afin accroître la sécurité de votre système, il convient toutefois d'être vigilant au fichier contenant le mot de passe ainsi qu'à la clé privée... ;).

La classe GPGLib

using System;
using System.Configuration;
using System.Diagnostics;
using System.IO;


namespace Utils
{
    /// <summary>
    /// Generic class providing static methods to decrypt gpg files
    /// </summary>
    public static class GPGLib
    {
    #region - Private Fields -

    private static string m_GPGFolderPath = string.Empty;
    private static string m_GPGPassPhraseFullPath = string.Empty;

    #endregion


    #region - Properties -

    /// <summary>
    /// Gets or Sets GPG Folder Path
    /// </summary>
    public static string GPGFolderPath
    {
        get {
            if (String.IsNullOrEmpty(m_GPGFolderPath)) {
                m_GPGFolderPath = ConfigurationManager.AppSettings["GPGFolderPath"];
                if (m_GPGFolderPath == null || String.IsNullOrEmpty(m_GPGFolderPath))
                    throw new ConfigurationErrorsException("GPG Folder Path is not defined !!");
            }
            return m_GPGFolderPath;
        }

        set { m_GPGFolderPath = value; }
    }

    /// <summary>
    /// Gets or Sets the GPG PassPhrase file full path
    /// </summary>
    public static string GPGPassPhraseFullPath
    {
        get {
            if (String.IsNullOrEmpty(m_GPGPassPhraseFullPath)) {
                m_GPGPassPhraseFullPath = ConfigurationManager.AppSettings["GPGPassPhraseFullPath"];
                if (m_GPGPassPhraseFullPath == null || String.IsNullOrEmpty(m_GPGPassPhraseFullPath))
                    throw new ConfigurationErrorsException("GPG Pass Phrase Path is not defined !!");
            }
            return m_GPGPassPhraseFullPath;
        }
        set { m_GPGPassPhraseFullPath = value; }
    }

    #endregion


    #region - Public Methods -


    /// <summary>
    /// File extraction .asc file only
    /// </summary>
    /// <param name="file"> file to extract </param>

    public static void Decrypt(String file)
    {
        if (!file.EndsWith(".asc"))
            throw new ArgumentException("File that could be decrypted should be .asc files");

        CheckGPG();

        if (!ExistsInPathVariables(GPGFolderPath))
            RegisterInPathVariables(GPGFolderPath);

        String outPutFile = file.Substring(0, file.Length - 3);

        Pump(file, outPutFile);
    }


    /// <summary>
    /// File extraction .asc file only
    /// </summary>
    /// <param name="file"> file to extract </param>
    /// <param name="output"></param>
    public static void Decrypt(String file, String output)
    {
        if (!file.EndsWith(".asc"))
            throw new ArgumentException("File that could be decrypted should be .asc files");

        CheckGPG();

        if (!ExistsInPathVariables(GPGFolderPath))
            RegisterInPathVariables(GPGFolderPath);

        Pump(file, output);
    }

    #endregion


    #region - Private Methods -

    /// <summary>
    /// Checks if GPG is existing on computer
    /// </summary>
    private static void CheckGPG() {
        if (!Directory.Exists(GPGFolderPath))
            throw new DirectoryNotFoundException("GPG Folder that you specified in configuration file does not exist");

        if (!File.Exists(Path.Combine(GPGFolderPath, "gpg.exe")))
            throw new FileNotFoundException(string.Format("GPG is not install in the specified directory{0}", GPGFolderPath));
    }


    /// <summary>
    /// Decrypts file in input to output
    /// </summary>
    /// <param name="input"> file to decrypt </param>
    /// <param name="output"> output file </param>
    private static void Pump(String input, String output)
    {
        using (Process p = new Process()) {
            p.StartInfo = new ProcessStartInfo("cmd.exe", @"/c gpg.exe --yes --passphrase-fd 0 < " + GPGPassPhraseFullPath + " -o \"" + output + "\" -d \"" + input + "\"");
            p.Start();
        }
    }



    /// <summary>
    /// Registers a path in PATH Environment Variable
    /// </summary>
    /// <param name="_gpgFolderPath"></param>
    private static void RegisterInPathVariables(String _gpgFolderPath)
    {
        Environment.SetEnvironmentVariable("PATH", Environment.GetEnvironmentVariable("PATH") + ";" + _gpgFolderPath);
    }



    /// <summary>
    /// Checks if a path exists in PATH Environment Variable
    /// </summary>
    /// <param name="path"></param>
    /// <returns></returns>
    private static bool ExistsInPathVariables(String path)
    {
        return (Environment.GetEnvironmentVariable("PATH").IndexOf(path) > -1);
    }

    #endregion
}

J'ai essayé de commenter au maximum cette classe que je vais expliquer très légèrement sur certains points et méthodes.

Le principe est donc de pouvoir faire appel à GPG (outil en ligne de commande) à partir de l'invite de commande. Pour faciliter l'appel à l'outil, j'ai préféré l'appeler à partir de son nom, et pas à partir de son chemin complet. J'opte donc pour l'enregistrement dans les variables d'environnement (PATH). Il y a donc une méthode permettant de vérifier si un chemin existe dans l'environnement, et une autre permettant de l'y enregistrer.

A propos de la commande de déchiffrement :

/c gpg.exe --yes --passphrase-fd 0 < " + GPGPassPhraseFullPath + " -o \"" + output + "\" -d \"" + input + "\"

1. Le paramètre /c permet d'exécuter la commande qui suit dans le prompt puis se termine.
2. Le paramètre --yes permettra à gpg de répondre oui à tout ce qui sera demandé (généralement, c'est utile si le fichier final existe déjà et que vous souhaitez pouvoir l'écraser sans avoir à confirmer manuellement)
3. Le paramètre --passphrase-fd 0 < ... permettra d'indiquer à gpg quel flux de données utiliser et celui qui contient le mot de passe de la clé privée.
4. Et pour finir -o pour le fichier de sortie, et -d pour le fichier d'entrée.

Fichier de configuration de l'application

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <appSettings>
        <add key="GPGFolderPath" value="C:\Program Files\GNU\GnuPG\" />
        <add key="GPGPassPhraseFullPath" value="C:\passphraseFile" />
    </appSettings>
</configuration>

Conclusion

Voilà, c'est tout. C'est un exemple simple et qui peut être très utile dans une application .NET (faites moi savoir s'il vous l'a été). En espérant qu'il vous serve, à bientôt.

A voir également
Ce document intitulé « C# et GNU PG (GPG) » issu de CodeS SourceS (codes-sources.commentcamarche.net) est mis à disposition sous les termes de la licence Creative Commons. Vous pouvez copier, modifier des copies de cette page, dans les conditions fixées par la licence, tant que cette note apparaît clairement.
Rejoignez-nous