Publié le 12/11/2008 Dans Programmation
Le Programme 'licence'
DescriptionLe programme que nous allons écrire se base donc sur les principes théoriques énoncés plus haut.
Dans sa forme, il permet de générer un fichier de licence signé à partir d'un fichier de licence non signé et de vérifier une licence (signée bien évidement ...).
Pour le compiler sous linux, il suffira de lancer la commande suivante:
gcc -o licence licence.c /path/to/libcrypto.a -ldl
ou:
/path/to/libcrypto.a est le chemin vers la libraire statique libcrypto.a
Utilisation
Pour signer un fichier de licence:
# ./licence -s licence.txt licence-signed.txt
Le fichier licence-signed.txt contiendra votre fichier de licence signé.
Pour vérifier qu'une licence est bien valide:
# ./licence -v licence-signed.txt
Note pour les développeurs
Les variable privkey_data et pubkey_data du fichier licence.c contiennent respectivement les clés privée et publique RSA générée au format PEM à l'aide d'openssl (voir la documentation d'OpenSSL).
Note: Il est évident que si vous vous basez sur ce code, votre application ne devra JAMAIS contenir la clé privée ... et aussi que vous ne devrez pas réutiliser les clés contenues dans ce programme mais bien générer votre propre paire de clé à l'aide du programme openssl:
openssl genrsa -out privkey.pem 1024
openssl rsa -in privkey.pem -out pubkey.pem -pubout
openssl rsa -in privkey.pem -out pubkey.pem -pubout
Code
licence.c:
- #include <stdio.h>
- #include <string.h>
- #include <openssl/pem.h>
- #include <openssl/err.h>
- #include <openssl/evp.h>
- char *privkey_data =
- "-----BEGIN RSA PRIVATE KEY-----\n"
- "MIICXgIBAAKBgQD3kEfHz/jRBjXqyZNt6TaAIDDhvQzQntG1TgLXV7wRhv8Vfjew\n"
- "e8df0d84jFNCP2Pw4GF1jdGolXUTrlGTPcj62wabiVt5r0waw/BHRrepkx1I84UM\n"
- "j5gCO13zEU1EwuY3IdharIBQO58sbd4yjwLz5Ngw1bSp1VLhSjKVlXIjsQIDAQAB\n"
- "AoGAHwoXBeV8g/CsZ/C4LGq2K6BdoKAMcEfJcVsDni5g5S2w4+f8YYNTfx8YRsXJ\n"
- "VqAODtCDR7LtW57JnBsaHUT84r/amlpWmaEmYzb/efFMOsGB9n1STBYDyJjQmhCB\n"
- "5V9gam/UQ8iyw5QR2PyL8FpSfvDLwNcw8K4Ph40zqp5CggECQQD/qDwr3Bej16mr\n"
- "1z+ovN77Q8w44/V3q53e8QgPvKJxoe0NsXgSOdCVjadrOdrAknRRaVwCxJOdoMvf\n"
- "Hvd4m1uRAkEA9+VEUxfjKJWMT8AFoVbKSjBo1+PvnEYJbq0R2leX10VKyaN8WnP5\n"
- "5+KxJQIXXrEDEl4cyyfroPYrVBvRJY72IQJBAKD7G2B2xz/5BbLZ6BDlVPccutS7\n"
- "3g5Lty3x0iSuoA8zaiRsMnIvi4MQXnJrTK5jyfCVikWH6HeiD53gTu3XzbECQQDF\n"
- "H2jPcoVONm1W1WkkvLErgYc9daGJ8R97BODXcSPrKMypvvkZrHOi817OAPW4dKXu\n"
- "qyvWWK5EVrxpq50Kcr5BAkEA8ZQhEGscDcLRsfiamg7fDiRugrWzLTrGJOf3CVr3\n"
- "0uyWAvOpjF7frSH7r3NpaaJDUSk6w3Kf1tXic7UFjuN4+A==\n"
- "-----END RSA PRIVATE KEY-----\n";
- char *pubkey_data =
- "-----BEGIN PUBLIC KEY-----\n"
- "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQD3kEfHz/jRBjXqyZNt6TaAIDDh\n"
- "vQzQntG1TgLXV7wRhv8Vfjewe8df0d84jFNCP2Pw4GF1jdGolXUTrlGTPcj62wab\n"
- "iVt5r0waw/BHRrepkx1I84UMj5gCO13zEU1EwuY3IdharIBQO58sbd4yjwLz5Ngw\n"
- "1bSp1VLhSjKVlXIjsQIDAQAB\n"
- "-----END PUBLIC KEY-----\n";
- unsigned char* sign_buffer(const void *buffer, unsigned int data_len, unsigned int *sig_len) {
- EVP_PKEY *pubkey = NULL;
- EVP_PKEY *privkey = NULL;
- unsigned int sign_buf_size = 0;
- unsigned char *sign;
- ERR_load_crypto_strings();
- BIO *bio_pubkey = BIO_new_mem_buf((void *) pubkey_data, strlen(pubkey_data));
- BIO *bio_privkey = BIO_new_mem_buf((void *) privkey_data, strlen(privkey_data));
- if (!PEM_read_bio_PrivateKey(bio_privkey, &privkey, NULL, NULL)) {
- printf("load error\n");
- ERR_print_errors_fp(stdout);
- exit(1);
- }
- if (!PEM_read_bio_PUBKEY(bio_pubkey, &pubkey, NULL, NULL)) {
- printf("load pubkey error\n");
- ERR_print_errors_fp(stdout);
- exit(1);
- }
- BIO_free(bio_pubkey);
- BIO_free(bio_privkey);
- OpenSSL_add_all_algorithms();
- EVP_MD_CTX ctx;
- EVP_MD_CTX_init(&ctx);
- sign_buf_size = EVP_PKEY_size(privkey);
- *sig_len = sign_buf_size;
- sign = (unsigned char *) malloc(sign_buf_size);
- EVP_SignInit(&ctx, EVP_sha512());
- EVP_SignUpdate(&ctx, (const void *) buffer, data_len);
- if (!EVP_SignFinal(&ctx, sign, sig_len, privkey)) {
- printf("sign error\n");
- ERR_print_errors_fp(stdout);
- exit(1);
- }
- EVP_VerifyInit(&ctx, EVP_sha512());
- EVP_VerifyUpdate(&ctx, (const void *) buffer, data_len);
- if (!EVP_VerifyFinal(&ctx, sign, *sig_len, pubkey)) {
- printf("verification error\n");
- ERR_print_errors_fp(stdout);
- exit(1);
- }
- return sign;
- }
- void make_license(char *in_file, char *out_file) {
- BIO *in = BIO_new_file(in_file, "r");
- if (!in) {
- fprintf(stderr, "cannot read file %s\n", in_file);
- exit(1);
- }
- BIO *out = BIO_new_file(out_file, "w");
- if (!out) {
- fprintf(stderr, "cannot create file %s\n", out_file);
- exit(1);
- }
- unsigned char in_buffer[4096];
- unsigned int in_buffer_len = 0;
- in_buffer_len = BIO_read(in, (void *) in_buffer, 4096);
- BIO *b64 = BIO_new(BIO_f_base64());
- BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
- b64 = BIO_push(b64, out);
- unsigned int sig_len = 0;
- unsigned char *signature = sign_buffer((void *) in_buffer, in_buffer_len, &sig_len);
- BIO_write(out, (const void *) in_buffer, in_buffer_len);
- BIO_write(out, "Sig: ", 5);
- BIO_flush(out);
- BIO_write(b64, signature, sig_len);
- BIO_flush(b64);
- BIO_write(out, "\n", 1);
- BIO_flush(out);
- BIO_flush(b64);
- BIO_free(b64);
- BIO_free(out);
- BIO_free(in);
- free(signature);
- }
- void verify_license(char *in_file) {
- unsigned char in_buffer[4096];
- unsigned int in_buffer_len;
- unsigned char *signature[256]; // Suitable for a 2048bits RSA Key
- BIO *in = BIO_new_file(in_file, "r");
- if (!in) {
- fprintf(stderr, "cannot read file %s\n", in_file);
- }
- in_buffer_len = BIO_read(in, (void *) in_buffer, 4096);
- BIO_free(in);
- int sig_start = -1;
- unsigned int i=0;
- for (; i < in_buffer_len - 5; i++) {
- if (!strncmp("Sig: ",(char *) ((unsigned int) in_buffer)+i, 5)) {
- sig_start = i+5;
- }
- }
- if (sig_start == -1) {
- printf("Signature not found\n");
- exit(1);
- }
- BIO *sig_in = BIO_new_mem_buf((void *) in_buffer+sig_start, in_buffer_len - sig_start);
- BIO *b64 = BIO_new(BIO_f_base64());
- BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
- b64 = BIO_push(b64, sig_in);
- unsigned int sig_len = BIO_read(b64, (void *) signature, 256);
- BIO_free(b64);
- BIO_free(sig_in);
- in = BIO_new_mem_buf(pubkey_data, strlen(pubkey_data));
- EVP_PKEY *pubkey;
- PEM_read_bio_PUBKEY(in, &pubkey, NULL, NULL);
- BIO_free(in);
- EVP_MD_CTX ctx;
- EVP_MD_CTX_init(&ctx);
- EVP_VerifyInit(&ctx, EVP_sha512());
- EVP_VerifyUpdate(&ctx, (const void *) in_buffer, sig_start-5);
- if (!EVP_VerifyFinal(&ctx, (void *) signature, sig_len, pubkey)) {
- printf("Invalid License File\n");
- exit(1);
- } else {
- printf("License File OK\n");
- }
- }
- void usage(char *progname) {
- printf("Usage:\n\t%s -s <infile> <outfile>\n\t%s -v <infile>\n", progname, progname);
- }
- int main(int argc, char **argv) {
- if (argc < 3) {
- usage(argv[0]);
- exit(1);
- }
- if (!strcmp(argv[1], "-s")) {
- if (argc < 4) {
- usage(argv[0]);
- exit(1);
- }
- make_license(argv[2], argv[3]);
- } else if (!strcmp(argv[1], "-v")) {
- verify_license(argv[2]);
- }
- return 0;
- }
La clé privée ne doit se trouver que dans le programme qui crée la license. Le programme qui la vérifie ne doit contenir que la clé publique (sinon il n'y a aucun intérêt à utiliser la crypto à clé publique).
Le link statique avec openssl est très lourd: le programme license que tu donnes fait chez moi 854kB à lui tout seul (strippé), et une version qui ne fait que la vérification ajoute 660kB au programme que tu veux protéger. Il existe des librairies crypto beaucoup plus légères qui seraient plus adaptées au link statique (matrixssl, libtomcrypt, ...)
Les exe protect dont tu parles, tu en as sous linux ? Parce que les clés en format ASCII armor comme tu les fais sont excessivement faciles à trouver et à remplacer dans un exécutable ELF... Il faut donc effectivement protéger l'exécutable contre toute modification.
Sinon, joli tuto J'embrayerais bien sur une version light...
La clé privée ne doit se trouver que dans le programme qui crée la license. Le programme qui la vérifie ne doit contenir que la clé publique (sinon il n'y a aucun intérêt à utiliser la crypto à clé publique).
Le link statique avec openssl est très lourd: le programme license que tu donnes fait chez moi 854kB à lui tout seul (strippé), et une version qui ne fait que la vérification ajoute 660kB au programme que tu veux protéger. Il existe des librairies crypto beaucoup plus légères qui seraient plus adaptées au link statique (matrixssl, libtomcrypt, ...)
Les exe protect dont tu parles, tu en as sous linux ? Parce que les clés en format ASCII armor comme tu les fais sont excessivement faciles à trouver et à remplacer dans un exécutable ELF... Il faut donc effectivement protéger l'exécutable contre toute modification.
Sinon, joli tuto J'embrayerais bien sur une version light...
Pour la clé privée, je l'ai noté dans le tuto
C'est vrai que le link static est très lourd, mais je ne connaissais pas vraiment d'autres lib. Je vais regarder du coté des libs que tu as renseigné tiens.
Pour ce qui est de la protection des exécutables ELF, ça existe, j'en suis sûr. Je n'ai pas de nom en tête la mais un bête UPX (un peu bidouillé) pourrait faire l'affaire.
Mais si je te dis que je lis pour le moment les specs ELF ... tu comprendras quel pourrait être le contenu du prochain article