Sur l'Internet, les données qui circulent peuvent être modifiées ou écoutées à tout moment. Il est donc nécessaire d'utiliser la cryptographie pour protéger ces données. Mais cette technique est difficile et pleine de pièges. Il vaut donc mieux utiliser une bibliothèque comme GPGME, qui permet au programmeur non spécialiste de cryptographie d'ajouter des fonctions de cryptographie à ses programmes.
Dans un réseau ouvert tel que l'
GPGME (
GPGME fonctionne en lançant l'exécutable
GPGME fournit par défaut une
Que doit faire le programmeur qui veut, par exemple, signer un
texte pour en prouver l'
gpgme_op_sign(context, clear_text, signed_text,
GPGME_SIG_MODE_NORMAL);
nbytes = gpgme_data_read(signed_text, buffer, MAXLEN);
buffer[nbytes] = '\0';
printf("Signed text (%i bytes):\n%s\n", (int)nbytes, buffer);
En fait, comme souvent en C, c'est bien plus compliqué. Il y a des
fonctions cryptographiques supplémentaires (ici, il faut récupérer la
Il faut d'abord initialiser GPGME et créer un
gpgme_ctx_t context;
...
/* Initializes gpgme */
gpgme_check_version (NULL);
error = gpgme_new(&context);
fail_if_err(error);
/* Initializes the context */
error = gpgme_ctx_set_engine_info (context, GPGME_PROTOCOL_OpenPGP, NULL,
KEYRING_DIR);
fail_if_err(error);
La macro
On peut maintenant chercher la clé avec laquelle on signera :
gpgme_key_t signing_key;
...
/* Retrieve the key for "Jean Dupont" */
error = gpgme_op_keylist_start(context, "Jean Dupont", 1);
fail_if_err(error);
error = gpgme_op_keylist_next(context, &signing_key);
fail_if_err(error);
error = gpgme_op_keylist_end(context);
fail_if_err(error);
error = gpgme_signers_add(context, signing_key);
fail_if_err(error);
Il faut maintenant préparer les tampons de données. La plupart des
opérations de GPGME portent sur ces tampons, de type
gpgme_data_t clear_text, signed_text;
...
/* Prepare the data buffers */
error = gpgme_data_new_from_mem(&clear_text, SENTENCE, strlen(SENTENCE), 1);
fail_if_err(error);
error = gpgme_data_new(&signed_text);
fail_if_err(error);
Que reste t-il à faire pour signer ? Ah oui, trouver la
gpgme_error_t passphrase_cb (void *opaque, const char *uid_hint,
const char *passphrase_info,
int last_was_bad, int fd)
{
write (fd, PASSPHRASE, strlen(PASSPHRASE));
return 0;
}
...
gpgme_set_passphrase_cb (context, passphrase_cb, NULL);
Maintenant, on peut appeler
gpgme_sign_result_t result;
...
error = gpgme_op_sign(context, clear_text, signed_text, GPGME_SIG_MODE_NORMAL);
fail_if_err(error);
result = gpgme_op_sign_result(context);
if (result->invalid_signers) {
fprintf (stderr, "Invalid signer found: %s\n",
result->invalid_signers->fpr);
exit (1);
}
Une fois la signature effectuée, pour lire le tampon de données, il
faut ramener le curseur à l'origine (ces tampons s'utilisent comme des
fichiers plutôt que comme des tableaux), avec
error = gpgme_data_seek (signed_text, 0, SEEK_SET);
fail_if_err(error);
buffer = malloc(MAXLEN);
nbytes = gpgme_data_read(signed_text, buffer, MAXLEN);
if (nbytes == -1) {
fprintf (stderr, "%s:%d: %s\n",
__FILE__, __LINE__, "Error in data read");
exit (1);
}
buffer[nbytes] = '\0';
printf("Signed text (%i bytes):\n%s\n", (int)nbytes, buffer);
Un source complet de toute cette opération de recherche de clé et
de signature est disponible en
% gcc -Wall -o gpgme-sign $(gpgme-config --cflags) \
$(gpgme-config --libs) \
gpgme-sign.c
Dans certains cas, GPGME mélange des
Et si on veut chiffrer et pas juste signer ? On utilise
gpgme_data_t clear_text, encrypted_text;
gpgme_key_t recipients[2] = {NULL, NULL};
/* The array must be NULL-terminated */
...
error = gpgme_op_keylist_start(context, "John Smith", 1);
error = gpgme_op_keylist_next(context, &recipients[0]);
...
error = gpgme_op_encrypt(context, recipients,
GPGME_ENCRYPT_ALWAYS_TRUST,
clear_text, encrypted_text);
Un exemple complet est disponible en
GPGME utilise le trousseau de clés habituel de GPG. Si on veut
générer ses propres clés, on peut mais GPGME ne sait pas encore les générer
en mémoire ou dans un fichier, il faut passer par un
trousseau.
\n \
Key-Type: DSA\n \
Key-Length: 512\n \
Subkey-Type: ELG-E\n \
Subkey-Length: 512\n \
Name-Real: John Smith\n \
Name-Comment: with stupid passphrase\n \
Name-Email: joe@foobar.example\n \
Expire-Date: 0\n \
Passphrase: abc\n \
\n"
...
error = gpgme_op_genkey(context, KEYPARAMS, NULL, NULL);
]]>
Pour
Voici un exemple de code pyme pour signer un message :
#!/usr/bin/python
# Modules necessaires
from pyme import core, callbacks
from pyme.constants.sig import mode
sentence = "I swear it is true"
passphrase = "abc"
# Renvoie la phrase de passe, qui est dans le code source. Pour des
# demos uniquement !
def mypassphrase(hint, desc, prev_bad):
print "Passphrase for %s" % hint
return passphrase
# Texte en clair
plain = core.Data(sentence)
# Le futur texte signe
signed = core.Data()
context = core.Context()
context.set_passphrase_cb(mypassphrase)
# Signer en clair (lisible même si on n'a pas gpg)
context.op_sign(plain, signed, mode.CLEAR)
# Rembobiner (revenir au debut)
sig.seek(0,0)
# Afficher le resultat
print sig.read()
Pour vérifier une signature, ce code peut convenir :
# Le texte signé est sur l'entrée standard
signed = core.Data(sys.stdin.read())
plain = core.Data()
context = core.Context()
context.op_verify(signed, None, plain)
result = context.op_verify_result()
sign = result.signatures
while sign:
if sign.status != 0:
print "BAD signature from:"
else:
print "Good signature from:"
print " uid: ", context.get_key(sign.fpr, 0).uids.uid
print " timestamp: ", sign.timestamp
print " fingerprint:", sign.fpr
sign = sign.next
On voit qu'il est très proche du code C, jusqu'aux parcours des
signatures qui se fait avec
Dernier exemple Python, un code de signature, encore, mais qui
permet de sélectionner une des clés disponibles dans le trousseau, en
indiquant son identificateur, ici
sentence = "I swear it is true"
passphrase = "abc"
keyID = "7F99B55C"
plain = core.Data(sentence)
sig = core.Data()
context = core.Context()
# The parameter is named "fpr" but can be used for key IDs as well
mykey = context.get_key(fpr=keyID, secret=True)
context.signers_add(mykey)
# passphrase_stdin is predefined in pyme and interactively queries the
# user.
context.set_passphrase_cb(callbacks.passphrase_stdin)
context.op_sign(plain, sig, mode.CLEAR)
sig.seek(0,0)
print sig.read()