Protection d'application, un système de licence simple
Publié le 12/11/2008 Dans Programmation
1. Introduction
2. Théorie
3. » Le Programme 'licence'
4. Conclusion
Le Programme 'licence'
Description

Le 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


Code

licence.c:
  1. #include <stdio.h> 
  2. #include <string.h> 
  3. #include <openssl/pem.h> 
  4. #include <openssl/err.h> 
  5. #include <openssl/evp.h> 
  6. char *privkey_data = 
  7. "-----BEGIN RSA PRIVATE KEY-----\n" 
  8. "MIICXgIBAAKBgQD3kEfHz/jRBjXqyZNt6TaAIDDhvQzQntG1TgLXV7wRhv8Vfjew\n" 
  9. "e8df0d84jFNCP2Pw4GF1jdGolXUTrlGTPcj62wabiVt5r0waw/BHRrepkx1I84UM\n" 
  10. "j5gCO13zEU1EwuY3IdharIBQO58sbd4yjwLz5Ngw1bSp1VLhSjKVlXIjsQIDAQAB\n" 
  11. "AoGAHwoXBeV8g/CsZ/C4LGq2K6BdoKAMcEfJcVsDni5g5S2w4+f8YYNTfx8YRsXJ\n" 
  12. "VqAODtCDR7LtW57JnBsaHUT84r/amlpWmaEmYzb/efFMOsGB9n1STBYDyJjQmhCB\n" 
  13. "5V9gam/UQ8iyw5QR2PyL8FpSfvDLwNcw8K4Ph40zqp5CggECQQD/qDwr3Bej16mr\n" 
  14. "1z+ovN77Q8w44/V3q53e8QgPvKJxoe0NsXgSOdCVjadrOdrAknRRaVwCxJOdoMvf\n" 
  15. "Hvd4m1uRAkEA9+VEUxfjKJWMT8AFoVbKSjBo1+PvnEYJbq0R2leX10VKyaN8WnP5\n" 
  16. "5+KxJQIXXrEDEl4cyyfroPYrVBvRJY72IQJBAKD7G2B2xz/5BbLZ6BDlVPccutS7\n" 
  17. "3g5Lty3x0iSuoA8zaiRsMnIvi4MQXnJrTK5jyfCVikWH6HeiD53gTu3XzbECQQDF\n" 
  18. "H2jPcoVONm1W1WkkvLErgYc9daGJ8R97BODXcSPrKMypvvkZrHOi817OAPW4dKXu\n" 
  19. "qyvWWK5EVrxpq50Kcr5BAkEA8ZQhEGscDcLRsfiamg7fDiRugrWzLTrGJOf3CVr3\n" 
  20. "0uyWAvOpjF7frSH7r3NpaaJDUSk6w3Kf1tXic7UFjuN4+A==\n" 
  21. "-----END RSA PRIVATE KEY-----\n"
  22. char *pubkey_data = 
  23. "-----BEGIN PUBLIC KEY-----\n" 
  24. "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQD3kEfHz/jRBjXqyZNt6TaAIDDh\n" 
  25. "vQzQntG1TgLXV7wRhv8Vfjewe8df0d84jFNCP2Pw4GF1jdGolXUTrlGTPcj62wab\n" 
  26. "iVt5r0waw/BHRrepkx1I84UMj5gCO13zEU1EwuY3IdharIBQO58sbd4yjwLz5Ngw\n" 
  27. "1bSp1VLhSjKVlXIjsQIDAQAB\n" 
  28. "-----END PUBLIC KEY-----\n"
  29. unsigned char* sign_buffer(const void *buffer, unsigned int data_len, unsigned int *sig_len) { 
  30. EVP_PKEY *pubkey = NULL; 
  31. EVP_PKEY *privkey = NULL; 
  32. unsigned int sign_buf_size = 0
  33. unsigned char *sign; 
  34. ERR_load_crypto_strings(); 
  35. BIO *bio_pubkey = BIO_new_mem_buf((void *) pubkey_data, strlen(pubkey_data)); 
  36. BIO *bio_privkey = BIO_new_mem_buf((void *) privkey_data, strlen(privkey_data)); 
  37. if (!PEM_read_bio_PrivateKey(bio_privkey, &privkey, NULL, NULL)) { 
  38. printf("load error\n"); 
  39. ERR_print_errors_fp(stdout); 
  40. exit(1); 
  41. if (!PEM_read_bio_PUBKEY(bio_pubkey, &pubkey, NULL, NULL)) { 
  42. printf("load pubkey error\n"); 
  43. ERR_print_errors_fp(stdout); 
  44. exit(1); 
  45. BIO_free(bio_pubkey); 
  46. BIO_free(bio_privkey); 
  47. OpenSSL_add_all_algorithms(); 
  48. EVP_MD_CTX ctx; 
  49. EVP_MD_CTX_init(&ctx); 
  50.     sign_buf_size = EVP_PKEY_size(privkey); 
  51.     *sig_len = sign_buf_size; 
  52.     sign = (unsigned char *) malloc(sign_buf_size); 
  53. EVP_SignInit(&ctx, EVP_sha512()); 
  54. EVP_SignUpdate(&ctx, (const void *) buffer, data_len); 
  55. if (!EVP_SignFinal(&ctx, sign, sig_len, privkey)) { 
  56. printf("sign error\n"); 
  57. ERR_print_errors_fp(stdout); 
  58. exit(1); 
  59. EVP_VerifyInit(&ctx, EVP_sha512()); 
  60. EVP_VerifyUpdate(&ctx, (const void *) buffer, data_len); 
  61. if (!EVP_VerifyFinal(&ctx, sign, *sig_len, pubkey)) { 
  62.                 printf("verification error\n"); 
  63.                 ERR_print_errors_fp(stdout); 
  64.                 exit(1); 
  65. return sign; 
  66. void make_license(char *in_file, char *out_file) { 
  67. BIO *in = BIO_new_file(in_file, "r"); 
  68. if (!in) { 
  69. fprintf(stderr, "cannot read file %s\n", in_file); 
  70. exit(1); 
  71. BIO *out = BIO_new_file(out_file, "w"); 
  72. if (!out) { 
  73. fprintf(stderr, "cannot create file %s\n", out_file); 
  74. exit(1); 
  75. unsigned char in_buffer[4096]; 
  76. unsigned int in_buffer_len = 0
  77. in_buffer_len = BIO_read(in, (void *) in_buffer, 4096); 
  78. BIO *b64 = BIO_new(BIO_f_base64()); 
  79. BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL); 
  80. b64 = BIO_push(b64, out); 
  81. unsigned int sig_len = 0
  82. unsigned char *signature = sign_buffer((void *) in_buffer, in_buffer_len, &sig_len); 
  83. BIO_write(out, (const void *) in_buffer, in_buffer_len); 
  84. BIO_write(out, "Sig: "5); 
  85. BIO_flush(out); 
  86. BIO_write(b64, signature, sig_len); 
  87. BIO_flush(b64); 
  88. BIO_write(out, "\n"1); 
  89. BIO_flush(out); 
  90. BIO_flush(b64); 
  91. BIO_free(b64); 
  92. BIO_free(out); 
  93. BIO_free(in); 
  94. free(signature); 
  95. void verify_license(char *in_file) { 
  96. unsigned char in_buffer[4096]; 
  97. unsigned int in_buffer_len; 
  98. unsigned char *signature[256]; // Suitable for a 2048bits RSA Key 
  99. BIO *in = BIO_new_file(in_file, "r"); 
  100. if (!in) { 
  101. fprintf(stderr, "cannot read file %s\n", in_file); 
  102. in_buffer_len = BIO_read(in, (void *) in_buffer, 4096); 
  103. BIO_free(in); 
  104. int sig_start = -1
  105. unsigned int i=0
  106. for (; i < in_buffer_len - 5; i++) { 
  107. if (!strncmp("Sig: ",(char *) ((unsigned int) in_buffer)+i, 5)) { 
  108. sig_start = i+5
  109. if (sig_start == -1) { 
  110. printf("Signature not found\n"); 
  111. exit(1); 
  112. BIO *sig_in = BIO_new_mem_buf((void *) in_buffer+sig_start, in_buffer_len - sig_start); 
  113. BIO *b64 = BIO_new(BIO_f_base64()); 
  114. BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL); 
  115. b64 = BIO_push(b64, sig_in); 
  116. unsigned int sig_len = BIO_read(b64, (void *) signature, 256); 
  117. BIO_free(b64); 
  118. BIO_free(sig_in); 
  119. in = BIO_new_mem_buf(pubkey_data, strlen(pubkey_data)); 
  120. EVP_PKEY *pubkey; 
  121. PEM_read_bio_PUBKEY(in, &pubkey, NULL, NULL); 
  122. BIO_free(in); 
  123. EVP_MD_CTX ctx; 
  124. EVP_MD_CTX_init(&ctx); 
  125. EVP_VerifyInit(&ctx, EVP_sha512()); 
  126. EVP_VerifyUpdate(&ctx, (const void *) in_buffer, sig_start-5); 
  127. if (!EVP_VerifyFinal(&ctx, (void *) signature, sig_len, pubkey)) { 
  128.                 printf("Invalid License File\n"); 
  129.                 exit(1); 
  130. else { 
  131. printf("License File OK\n"); 
  132. void usage(char *progname) { 
  133. printf("Usage:\n\t%s -s <infile> <outfile>\n\t%s -v <infile>\n", progname, progname); 
  134. int main(int argc, char **argv) { 
  135. if (argc < 3) { 
  136. usage(argv[0]); 
  137. exit(1); 
  138. if (!strcmp(argv[1], "-s")) { 
  139. if (argc < 4) { 
  140. usage(argv[0]); 
  141. exit(1); 
  142. make_license(argv[2], argv[3]); 
  143. else if (!strcmp(argv[1], "-v")) { 
  144. verify_license(argv[2]); 
  145. return 0
  146. }
Commentaires
Editer  Citer
Protection d'application, un système de licence simple
Si je peux me permettre quelques remarques...

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 :wink: J'embrayerais bien sur une version light...
Par philfr, Publié le 13/11/2008 @ 14:30:08
Editer  Citer
Protection d'application, un système de licence simple
Si je peux me permettre quelques remarques...

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 :wink: J'embrayerais bien sur une version light...


Pour la clé privée, je l'ai noté dans le tuto :wink:

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 :wink:
Par rfr, Publié le 13/11/2008 @ 17:46:42

Poster un commentaire
Vous devez être identifié pour accéder à cette fonctionnalité

Utilisateur
Mot de passe
 
Informaticien.be - © 2002-2024 AkretioSPRL  - Generated via Kelare
The Akretio Network: Akretio - Freedelity - KelCommerce - Votre publicité sur informaticien.be ?