[This article appeared on OndaQuadra0A Elettronic Magazine - Nov 2003 ]
OpenSSL/RSA: toolkit & implementation
Article && sources by Paolo Ardoino AKA binduck
<paolo.ardoino@gmail.com>
0. Intro
1 RSA - Teoria
1.1 OpenSSL/RSA [genrsa,rsa,rsautl]
(generare chiave,cifrare/decifrare,firmare,...)
1.1.1 GPG
1.2 Impariamo ad implementare RSA nei nostri programmi
1.2.1 RSA - Headers
1.2.2 RSA - Le chiavi
1.2.3 RSA - Cifrare e decifrare
0. Intro
In questo articolo voglio illustrarvi le basi che vi permetteranno di
capire e implementare l' algoritmo RSA.
1]Vedremo, quindi, come utilizzare il toolkit openssl (ne vedremo le
principali funzioni, come creare le chiavi, come cifrare/decifrare
dati).
2]Vedremo insieme le basi delle librerie openssl, e in particolare
quelle che vi permetteranno di implementare l'algoritmo nei vostri
programmi.
Per affrontare la lettura di questo articolo avrete bisogno di
conoscere:
- il linguaggio C
- se volete comprendere la parte matematica dovrebbe bastare
l'infarinatura di matematica fornitavi nelle elementari e nelle medie.
- dovreste avere openssl e le librerie (/usr/include/openssl/) se vi
mancano....installatele.
1 RSA - Teoria
RSA fu sviluppato da tre matematici del MIT:Rivest, Shamir e Adleman:
- Cifrario a chiave pubblica (usato nel PGP e in molti altri programmi)
- Si basa sulla teoria matematica dei numeri; infatti le chiavi sono
costituite dal prodotto di due numeri primi molto grandi(piu' di
trecento cifre)
- Cifrazione/Decifrazione di dati
- Firma/Controllo firma
- Supporta chivi di diversa grandezza in modo da fornire vari livelli
di sicurezza (grandezza minima consigliata e' 1024).
Riprendiamo alcuni concetti basilari della crittografia che ci saranno
utili. Un cifrario a chiave pubblica (o cifrario asimmetrico) e'
basato sull'utilizzo di due chiavi,una per cifrare e una per decifrare.
La prima chiave puo' essere distribuita a chi vuole farci pervenire
messaggi cifrati in modo che solo noi possiamo decifrarli con la nostra
chiave segreta(che DEVE rimanere segreta).
Il fatto che la chiave pubblica possa essere tranquillamente
distribuita anche via email, in messaggi in chiaro, elimina la
necessita' di utilizzare canali altamente sicuri per lo scambio della
chiave utilizzata nei cifrari a chiave semplice.
Iniziamo a vedere l'algoritmo in generale ed a analizzarlo dal punto
di vista matematico.
1)Il cifrario RSA e' basato sul prodotto di due numeri primi di grandi
dimensioni, che possono superare le 300 cifre.
Ricordo inanzitutto che un numero si dice primo solo se e' divisibile
per 1 e se stesso.
Noi prenderemo due numeri primi piccoli per fare un esempio:
P = 7 e Q = 11
2)Ora chiamiamo N il prodotto dei due: N = P * Q = 77
3)Ora calcoliamo il valore della funzione di Eulero di N in questo
modo:
M = f(n) = (P-1)*(Q-1)
Nel nostro caso M = 60
Vi ricordo che il valore della funzione di Eulero dovra' rimanere
segreto.
4)Ora scegliamo un numero E tale che E e M siano primi tra loro.
Poiche' 1 < E < M allora iniziamo a dividere M per tutti i numeri
maggiori di 1 fino a che non troviamo un numero che abbia una parte
decimale.
60 / 2 = 30
60 / 3 = 20
60 / 4 = 15
60 / 5 = 12
60 / 6 = 10
60 / 7 = 8.57
Quindi E = 7
5)Ora calcoliamo D usando la formula D = ((H * M) + 1) / E
in modo che H sia il piu' piccolo valore per cui D sia intero.
Quindi iniziamo a sostituire H con i numeri 1,2,3,... fintanto che il
risultato trovato non sia intero.
D = ((1 * 60) + 1) / 7 -> NO
D = ((2 * 60) + 1) / 7 -> NO
....
....
D = ((5 * 60) + 1) / 7 = 301 / 7 = 43
D = 43
6)Bene ora abbiamo generato la nostra chiave che useremo per
crifrare/decifrare i dati. Per cifrare usiamo la formula:
C = (B^E) mod N
Dove C e' il testo cifrato, mentre B e' il valore decimale del
testo che vogliamo cifrare.
Prendiamo come esempio B = 3 ->
C = (3 ^ 7) mod 77 = 31
Per decifrare usiamo la formula:
L = (C^D) mod N
Prendiamo ora il numero precedentemente ottenuto e decifriamolo:
L = (31 ^ 43) mod 77 = 3
Nota: si potrebbe pensare che si possa semplicemente calcolare la
funzione di Eulero senza utilizzare i numeri primi P e Q. Questo non e'
impossibile ma richiede un ingente quantita' di tempo, avendo lo stesso
grado di complessita' della fattorizzazione di N.
1.1 OpenSSL/RSA
Vediamo ora come utilizzare openssl in combinazione con RSA.
Creiamo inanzitutto le chiavi RSA, e per fare questo si usa openssl
genrsa:
openssl genrsa [-out nome_file] [-passout arg] [-des] [-des3]
[-idea] [-f4] [-3] [-rand file(s)] [numbits]
binduck@fuzzy:~$ openssl genrsa -des3 1024 > key.pem
Con questo comando generiamo una chiave RSA di lunghezza 1024 bits
e la salviamo nel file key.pem nel formato PEM.
Verra' chiesto di inserire una password per cifrare la chiave
con 3des prima di salvarla(possiamo sostituire -des3 con -des o -idea).
Se invece non vogliamo cifrarla basta fare eliminarel'opzione -des3:
binduck@fuzzy:~$ openssl genrsa 1024 > key.pem
Vediamo le altre opzioni del comando openssl genrsa:
-out file_out :Il nome del file di output.
-passout arg :Il file sorgente delle password per l'output.
-F4|-3 :L'esponente pubblico da usare(65537 o 3(default 65537))
numbits :Numero di bits(minimo consigliato 1024).
PER MAGGIORI INFORMAZIONI SULLE OPZIONI: genrsa(1)
Ora ricaviamo la chiave pubblica da quella privata appena generata,
utilizzando openssl rsa:
openssl rsa[-inform] [-outform |PEM|NET|DER] [-in filename]
[-passin arg] [-out filename] [passout arg] [-sgckey] [-des] [-des3]
[-idea] [-text] [-noout] [-modulus] [-check] [-pubin] [-pubout]
-in file :Legge la chiave RSA contenuta nel file passato come
argomento.(Per default la chiave sara' intesa come privata).
-pubin :Dichiara che la chiave letta con -in file sara'
pubblica.
-pubout :Dichiara che l'output sara' una chiave pubblica.
binduck@fuzzy:~$ openssl rsa -in key.pem -pubout > pub.pem
o in maniera equivalente:
binduck@fuzzy:~$ openssl rsa -in key.pem -pubout -out pub.pem
Vediamo le altre opzioni interessanti:
-check :Controlla la struttura di una chiave privata RSA.
binduck@fuzzy:~$ openssl rsa -check -in key.pem
Altre opzioni utili sono quelle che permettono di cifrare una chiave,
con un algoritmo a scelta tra des, 3des, idea, in un secondo momento
dalla creazione della chiave.
binduck@fuzzy:~$ openssl rsa -in key.pem -des3 -out key.pem
A questo punto bastera' digitare la pass phrase, confermarla e la
vostra chiave sara' cifrata.
Ovviamente possiamo essere interessati a rimuovere la password
dalla chiave:
binduck@fuzzy:~$ openssl rsa -in key.pem -out keydec.pem
Dovrete inserire la pass phrase per eliminare la cifratura e il
gioco e' fatto.
-modulus :Stampa il modulo della chiave.
binduck@fuzzy:~$ openssl rsa -in key.pem -modulus
PER MAGGIORI INFORMAZIONI SULLE ALTRE OPZIONI: rsa(1)
Ora passiamo a vedere come cifrare/decifrare,firmare/verificare dati
usando l'algoritmo RSA.
openssl rsautl [-in file][-out file][-inkey file][pubin][-certin]
[-sign][-verify][-encrypt] [-decrypt][-pkcs][-ssl][-raw][-hexdump]
[-asn1parse]
NOTA: rsautl, poiche' usa l'algoritmo RSA direttamente puo' essere
usato solo per cifrare dati di piccole dimensioni.
Se tentiamo di firmare un file troppo grande verra' generato un errore
che ci dira' appunto che la dimensione del file era troppo elevata per
la chiave usata.
-inkey file :Usa la chiave contenuta nel file passato come argomento.
(Per default rsautl si aspettera' una chiave privata).
-pubin :Dichiara che la password contenuta nel file letto
con -inkey file e' una chiave pubblica.
-certin :Dichiare che l'input sara' un certificato contentente una
chiave RSA.
Prendiamo come esempio un file contenente una password.
echo "rsa_algorithm" > pass.txt
Ora per cifrare il file digitiamo:
binduck@fuzzy:~$ openssl rsautl -encrypt -in pass.txt -inkey pub.pem
-pubin> crypto
Ora avremo la nostra password cifrata nel file "crypto".
Ovviamente pub.pem e' la chiave pubblica precedentemente generata con
openssl genrsa.
Vediamo ora il processo inverso, decifriamo la password:
binduck@fuzzy:~$ openssl rsautl -decrypt -in crypto -inkey key.pem
Questo stampera' sullo stdout la password in chiaro.
key.pem e' la chiave privata, poiche' stiamo decifrando.
Mettiamo il caso che vogliamo semplicemente firmare un documento
usando la nostra chiave privata(anche nel caso delle firme possiamo
firmare solo dati di piccole dimensioni).
binduck@fuzzy:~$ openssl rsautl -sign -in small_file.txt -inkey key.pem
-out signed_file
Vediamo ora come verificare dei dati firmati:
binduck@fuzzy:~$ openssl rsautl -verify -in signed_file -inkey key.pem
PER MAGGIORI INFORMAZIONE SULLE ALTRE OPZIONI: rsautl(1)
1.1.1 GPG
Se vi siete rotti delle limitazioni insite nell'uso di rsautl a causa
della dimensione dei file vi consiglio di usare gpg.
Pero' per quanto riguarda l'utilizzo di RSA in gpg bisogna ricordare
che questo algoritmo e' usato solo per le firme.
Ci sono ottime guide dedicate a questo programma, prima fra tutte il
manuale che potrete trovare a questo indirizzo:
http://www.gnupg.org/(en)/documentation
1.2 Impariamo ad implementare RSA nei nostri programmi.
Ora passiamo alla parte piu' interessante:
Impariamo come implementare RSA nei nostri programmi.
1.2.1 RSA - Headers
Qui e' riportata la lista di headers che utilizzeremo per creare dei
programmini di base.
#include<openssl/rsa.h>
Funzioni per generare le chiavi, cifrare, decifrare, firmare,
verificare dati.
-----------------------------
#include<openssl/pem.h>
Funzioni per leggere e scrivere strutture in formato PEM.
Noi le utilizzeremo per salvae le chiavi RSA nei files.
Ricordate openssl genrsa che salva le chiavi generate in formato PEM.
-----------------------------
#include<openssl/err.h>
Funzioni per accedere ai codici di errore generati dalle funzioni della
libreria openssl.
-----------------------------
1.2.2 RSA - Le chiavi
Vediamo la struttura di una chiave RSA:
struct
{
BIGNUM *n; //public modulus
BIGNUM *e; //public exponent
BIGNUM *d; //private exponent
BIGNUM *p; //secret prime factor
BIGNUM *q; //secret prime factor
BIGNUM *dmp1; // d mod (p-1)
BIGNUM *dmq1; // d mod (q-1)
BIGNUM *iqmp; // q^-1 mod p
};
RSA
Ovviamente nelle chiavi pubbliche l'esponente privato(d) e i relativi
valori segreti (p e q) sono NULL (per chiarirvi le idee potete andare
a rivedervi la trattazione matematica al paragrafo 1).
Nelle chiavi private p, q, dmp1, dmq1 e iqmp potrebbero anche essere
NULL, ma le operazioni effettuate usando questa chiave risulterebbero
piu' lente.
Ricordiamo infatti che per decifrare si usa solo d (guardatevi sempre
il par.1).
Vediamo come creare le chiavi.
Prima presentero' le funzioni necessarie e poi vedremo un semplice
programma per creare le nostre chiavi.
Inanzitutto dobbiamo allocare e iniziare una struttura RSA.
Questo e' possibile con la funzione RSA_new():
---------------------------------------------------------------------
#include<openssl/rsa.h>
RSA * RSA_new(void);
Ritorna NULL se fallisce l'allocazione; l'errore puo' essere ottenuto
usando ERR_get_error() (spiegata piu' avanti).
Come potete vedere dal programma per crear le chiavi usando la funzione
RSA_generate_key() la struttura viene direttamente inizializzata.
---------------------------------------------------------------------
RSA_free() libera la memoria occupata dalla struttura RSA, ma prima di
fare cio' cancella la chiave.
#include <openssl/rsa.h>
RSA_free(RSA *rsa);
Ricordatevi di liberare sempre la memoria occupata dalle vostre chiavi.
---------------------------------------------------------------------
Per creare le chiavi usiamo la funzione RSA_generate_key():
#include <openssl/rsa.h>
RSA *RSA_generate_key(int num, unsigned long e, void(*callback)
(int,int,void *), void *cb_arg);
int num: numero di bit. Come dice la man page e' meglio scegliere una
chiave >= di 1024 bit.
unsigned long e: e' l'esponente. Deve essere un numero dispari,
tipicamente si sceglie 3, 17 o 65537.
Se la funzione fallisce ritorna NULL. Il numero dell'errore puo'
essere ottenuto sempre con ERR_get_error().
---------------------------------------------------------------------
#include <openssl/rsa.h>
int RSA_check_key(RSA *rsa);
Questa funzione controlla se la chiave e' una vera chiave RSA.
Se e' corretta allora la funzione ritorna 1, se non e' una chiave
ritorna 0, mentre se la funzione fallisce ritorna -1.
---------------------------------------------------------------------
Ora che sappiamo come creare la nostra chiave vediamo come scrivere
la chiave privata in un file.
Possiamo usare una funzione presente in pem.h:
PEM_write_RSAPrivateKey():
#include <openssl/pem.h>
int PEM_write_RSAPrivateKey(FILE *fp,RSA *x,const EVP_CIPHER *enc,
unsigned char *kstr,int klen,pem_password_cb *cb,void *u);
---------------------------------------------------------------------
Mentre per scrivere la chiave pubblica useremo
PEM_write_RSAPublicKey():
#include <openssl/pem.h>
int PEM_write_RSAPublicKey(FILE *fp,RSA *x);
Tutte e due queste funzioni di scrittura ritornano 1 se hanno successo
e 0 se falliscono.
---------------------------------------------------------------------
Per leggere la chiave privata scritta in formato pem usiamo
PEM_read_RSAPrivateKey():
#include <openssl/pem.h>
RSA *PEM_read_RSAPrivateKey(FILE *fp,RSA **x,pem_password_cb *cb,
void *u);
---------------------------------------------------------------------
Per leggere la chiave pubblica usiamo PEM_read_RSAPublicKey():
#include <openssl/pem.h>
RSA *PEM_read_RSAPublicKey(FILE *fp,RSA **x,pem_password_cb *cb,
void *u);
Le funzioni di lettura ritornano un puntatore alla struttura letta o
NULL se c'e' un errore.
Settiamo enc, cb, kstr e u a NULL e poniamo klen a 0 poiche' non
necessitiamo di crittare le strutture PEM.
---------------------------------------------------------------------
Ora sappiamo come creare e salvare le nostre chiavi.
Prima di vedere un esempio pratico introduciamo anche le funzioni
per il controllo degli errori.
#include <openssl/err.h>
unsigned long ERR_get_error(void);
Questa funzione ritorna il codice dell'ultimo errore.
---------------------------------------------------------------------
#include <openssl/err.h>
char *ERR_error_string(unsigned long e, char *buf);
Genera una stringa "human-readable".
Un utilizzo di queste due funzioni combinate e' questo:
printf("%s\n",ERR_error_string(ERR_get_error(),NULL));
---------------------------------------------------------------------
Bene ora vediamo un piccolo esempio su come creare una chiave e
salvarla su dei files.
-----------------------------keygen.c--------------------------------
/*
Simple RSA Key generator. Saves keys in PEM format.
Coded by Paolo Ardoino - <paolo.ardoino@gmail.com>
Compile: gcc -lssl keygen.c -o keygen
Usage: ./keygen <numbits>
Ex. ./keygen 1024
*/
#include <stdio.h>
#include <openssl/err.h>
#include <openssl/rsa.h>
#include <openssl/pem.h>
#define SECFILE "sec.pem"
#define PUBFILE "pub.pem"
int main(int argc, char *argv[])
{
RSA *key;
FILE *fp;
int keylen=0;
if(argc!=2)
{
fprintf(stderr,"Error: too many/few arguments.\n "
"Usage: %s <numbits>\n",argv[0]);
exit(0);
}
keylen = atoi(argv[1]);
if((key = RSA_generate_key(keylen,3,NULL,NULL)) == NULL)
{
fprintf(stderr,"%s\n",ERR_error_string(ERR_get_error(),NULL));
exit(-1);
}
if(RSA_check_key(key) < 1)
{
fprintf(stderr,"Error: Problems while generating RSA Key.\nRetry.\n");
exit(-1);
}
fp=fopen(SECFILE,"w");
if(PEM_write_RSAPrivateKey(fp,key,NULL,NULL,0,0,NULL) == 0)
{
fprintf(stderr,"Error: problems while writing RSA Private Key.\n");
exit(-1);
}
fclose(fp);
fp=fopen(PUBFILE,"w");
if(PEM_write_RSAPublicKey(fp,key) == 0)
{
fprintf(stderr,"Error: problems while writing RSA Public Key.\n");
exit(-1);
}
fclose(fp);
RSA_free(key);
printf("RSA key generated.\nLenght = %d bits.\n",keylen);
return 0;
}
---------------------------------------------------------------------
Per compilare digitate: gcc -lssl keygen.c -o keygen
Usage: ./keygen <numbits>
binduck@fuzzy:~$ ./keygen 1024
Eseguendo il programma, questo ci creera' due files 'pub.pem' e
'sec.pem' in cuiverranno salvate le nostre chiavi RSA.
Ovviamente potrete specificare la lunghezza da voi desiderata
(Es. 64 o 512 o 2048).
Ecco come risulterebbe una chiave privata a 1024 bit generata.
-----BEGIN RSA PRIVATE KEY-----
MIICXQIBAAKBgQDWxE5+qNqE20NQWqRi71vkqroI+52CgzGmKv42BsE6kk1QiVSS
9Qm75bKfEP7Jz83NO/YO5DPHp6ZVr38nY6HCY3Ta/fNU0O/xNE3UiOQSK8dn7lbw
7itshIwjPpK09cgL6wos4Sm+lcHjdRk5DyTKcRNadWyXSxNsdrR5FWtImwIBAwKB
gQCPLYmpxecDPNeK5xhB9Oftxyawp75XAiEZcf7Oryt8YYjgW423TgZ9Q8xqC1SG
iokzfU60mCKFGm7jylTE7RaAXuiq84NDz71wmFjOgqGzePHl4CH5WnPi0GTwfssI
cEBr5WjUu2DTNKAUdk+1jLJFXFY6UIws6tzZB1SPNNagewJBAPAvdSm765vsMcOL
zfIks0rMpx9c4QZrXAgm+IoAN3EQOvZ9KNAK1xkUYDPJEyJ93GY5ZjyPaFnM9t+x
n97l1r8CQQDk6GVm8oN9Z5aMPNDSzNGrj+f+xxngEjxDzcH2YfDcVy8cb8T4Daqt
vWSP2JWZm4YAWJzACuuQ+zAMBAJnQ4ElAkEAoB+jcSfyZ/LL17KJTBh3hzMaFOiW
BEeSsBn7BqrPoLV8pFNwirHku2LqzTC3bFPoRCZEKF+a5oiklSEVP0PkfwJBAJia
7kShrP5FDwgoizczNnJf7/8vZpVhfYKJK/mWoJLkyhL1Lfqzxx5+QwqQY7u9BAA7
Eyqx8mCndV1YAZotAMMCQQDYZg4eFgmTJuq/J/Ls7NusdBxuwh0fKRt2KPhCZw5r
5mEeCDflQ3ATBjeUt6Z77JG0aEmQUq5NOqd/ju1VtBst
-----END RSA PRIVATE KEY-----
Ed ecco la chiave pubblica relativa:
-----BEGIN RSA PUBLIC KEY-----
MIGHAoGBANbETn6o2oTbQ1BapGLvW+Squgj7nYKDMaYq/jYGwTqSTVCJVJL1Cbvl
sp8Q/snPzc079g7kM8enplWvfydjocJjdNr981TQ7/E0TdSI5BIrx2fuVvDuK2yE
jCM+krT1yAvrCizhKb6VweN1GTkPJMpxE1p1bJdLE2x2tHkVa0ibAgED
-----END RSA PUBLIC KEY-----
1.2.3 Cifrare/Decifrare
Nell'header openssl/rsa.h troviamo le funzioni di cui abbiamo
bisogno: RSA_private_decrypt() e RSA_public_encrypt().
---------------------------------------------------------------------
#include <openssl/rsa.h>
int RSA_public_encrypt(int flen, unsigned char *from,
unsigned char *to,RSA *rsa,int padding);
unsigned char *from: stringa da crittografare.
unsigned char *to: buffer dove la funzione mette l'output
crittografato.
RSA *rsa: chiave pubblica RSA.
Come padding impostiamo RSA_PKCS1_OAEP_PADDING
Ritorna la grandezza dei dati crittografati.
---------------------------------------------------------------------
int RSA_private_decrypt(int flen,unsigned char *from,unsigned char
*to,RSA *rsa,int padding);
unsigned char *from: stringa da decrittare.
unsigned char *to: buffer che conterra' l'output in chiaro.
RSA *rsa: chiave privata RSA.
Come padding impostiamo sempre RSA_PKCS1_OAEP_PADDING.
Ritorna la grandezza del testo in chiaro.
---------------------------------------------------------------------
Bene ora vediamo un programmino che cifra e decifra il contenuto di
un file.
RICORDO CHE STIAMO USANDO DIRETTAMENTE L'ALGORITMO RSA, QUINDI
POSSIAMO INTERVENIRE SU UNA PICCOLA QUANTITA' DI DATI(ricordate
rsautl nella prima parte dell'articolo?).
--------------------------file_crypt.c-------------------------------
/*
Simple RSA encrypting/decrypting tool.
Coded by binduck - <binduck@coder.hu>
Compile: gcc -lssl file_crypt.c -o file_crypt
Usage: ./file_crypt -h for help
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <openssl/pem.h>
#include <openssl/err.h>
#include <openssl/rsa.h>
#define SECFILE "sec.pem"
#define PUBFILE "pub.pem"
#define READPUB 0
#define READSEC 1
#define HELP 'h'
#define GENKEY 'g'
#define CIPHER 'c'
#define DECIPHER 'd'
RSA* readpemkeys(int type);
void genkey(int size);
int rsa_encrypt(RSA *key, unsigned char *plain, int len, unsigned char **cipher);
int rsa_decrypt(RSA *key, unsigned char *cipher, int len, unsigned char **plain);
int main(int argc,char *argv[])
{
int ch,size=0,len=0,ks=0;
RSA *key=NULL;
FILE *fpin=NULL, *fpout=NULL;
char char_opt[] = {HELP,GENKEY,CIPHER,DECIPHER};
unsigned char *cipher=NULL,*plain=NULL;
printf("RSA Cipher/Decipher\n");
printf("Coded by Paolo Ardoino - <ardoino.gnu@disi.unige.it>\n\n");
if (argc == 1)
printf("'%s -h' for help.\n", *argv);
while((ch=getopt(argc,argv,char_opt)) != -1) {
switch(ch) {
case HELP:
printf("%s -g <num_bits>\n", *argv);
printf("\tgenerates RSA keys and save them in PEM format.(1024 or 2048 are strongly suggested).\n");
printf("%s -c <file_in> <file_out>\n", *argv);
printf("\tcrypts datas in 'file_in' and stores output in 'file_out'.\n");
printf("%s -d <file_in> <file_out>\n", *argv);
printf("\tdecript datas in 'file_in' and stores output in 'file_out'.\n");
break;
case GENKEY:
if(argc != 3) {
fprintf(stderr,"Error: check arguments.\n'%s -h' for help.\n",*argv);
exit(EXIT_FAILURE);
}
ks = atoi(*(argv + 2));
printf("Generating RSA keys [size = %d bits]\n", ks);
genkey(ks);
printf("Private Key saved in %s file.\n", SECFILE);
printf("Public Key saved in %s file.\n", PUBFILE);
printf("Done.\n");
break;
case CIPHER:
if(argc != 4) {
fprintf(stderr,"Error: check arguments.\n'%s -h' for help.\n", *argv);
exit(EXIT_FAILURE);
}
key = readpemkeys(READPUB);
if(!(fpin = fopen(*(argv + 2), "r"))) {
fprintf(stderr, "Error: Cannot locate input file.\n");
exit(EXIT_FAILURE);
}
fpout = fopen(*(argv + 3), "w");
ks = RSA_size(key);
plain = (unsigned char *)malloc(ks * sizeof(unsigned char));
cipher = (unsigned char*)malloc(ks * sizeof(unsigned char));
printf("Encrypting '%s' file.\n",*(argv + 2));
while(!feof(fpin)) {
memset(plain,'\0',ks + 1);
memset(cipher, '\0', ks + 1);
len = fread(plain, 1, ks - 11, fpin);
size = rsa_encrypt(key, plain, len, &cipher);
fwrite(cipher, 1, size, fpout);
}
fclose(fpout);
fclose(fpin);
free(cipher);
free(plain);
RSA_free(key);
printf("Done.\n");
break;
case DECIPHER:
if(argc != 4) {
fprintf(stderr,"Error: check arguments.\n'%s -h' for help.\n", *argv);
exit(EXIT_FAILURE);
}
key = readpemkeys(READSEC);
if(!(fpin = fopen(*(argv + 2), "r"))) {
fprintf(stderr, "Error: Cannot locate input file.\n");
exit(EXIT_FAILURE);
}
fpout = fopen(*(argv + 3), "w");
ks = RSA_size(key);
cipher = (unsigned char*)malloc(ks * sizeof(unsigned char));
plain = (unsigned char*)malloc(ks * sizeof(unsigned char));
printf("Decrypting '%s' file.\n", *(argv + 2));
while(!feof(fpin)) {
memset(cipher, '\0', ks);
memset(plain, '\0', ks);
if ((len = fread(cipher, 1, ks, fpin)) == 0)
break;
size = rsa_decrypt(key, cipher, len, &plain);
fwrite(plain, 1, size, fpout);
}
fclose(fpout);
fclose(fpin);
free(plain);
free(cipher);
RSA_free(key);
printf("Done.\n");
break;
}
}
return 0;
}
void genkey(int size)
{
RSA *key=NULL;
FILE *fp;
if((key = RSA_generate_key(size,3,NULL,NULL)) == NULL) {
fprintf(stderr,"%s\n",ERR_error_string(ERR_get_error(),NULL));
exit(EXIT_FAILURE);
}
if(RSA_check_key(key) < 1) {
fprintf(stderr,"Error: Problems while generating RSA Key.\nRetry.\n");
exit(EXIT_FAILURE);
}
fp=fopen(SECFILE,"w");
if(PEM_write_RSAPrivateKey(fp,key,NULL,NULL,0,0,NULL) == 0) {
fprintf(stderr,"Error: problems while writing RSA Private Key.\n");
exit(EXIT_FAILURE);
}
fclose(fp);
fp=fopen(PUBFILE,"w");
if(PEM_write_RSAPublicKey(fp,key) == 0) {
fprintf(stderr,"Error: problems while writing RSA Public Key.\n");
exit(EXIT_FAILURE);
}
fclose(fp);
RSA_free(key);
return;
}
RSA* readpemkeys(int type)
{
FILE *fp;
RSA *key=NULL;
if(type == READPUB) {
if((fp = fopen(PUBFILE,"r")) == NULL) {
fprintf(stderr,"Error: Public Key file doesn't exists.\n");
exit(EXIT_FAILURE);
}
if((key = PEM_read_RSAPublicKey(fp,NULL,NULL,NULL)) == NULL) {
fprintf(stderr,"Error: problems while reading Public Key.\n");
exit(EXIT_FAILURE);
}
fclose(fp);
return key;
}
if(type == READSEC) {
if((fp = fopen(SECFILE,"r")) == NULL) {
fprintf(stderr,"Error: Private Key file doesn't exists.\n");
exit(EXIT_FAILURE);
}
if((key = PEM_read_RSAPrivateKey(fp,NULL,NULL,NULL)) == NULL) {
fprintf(stderr,"Error: problmes while reading Private Key.\n");
exit(EXIT_FAILURE);
}
fclose(fp);
if(RSA_check_key(key) == -1) {
fprintf(stderr,"Error: Problems while reading RSA Private Key in '%s' file.\n",SECFILE);
exit(EXIT_FAILURE);
} else if(RSA_check_key(key) == 0) {
fprintf(stderr,"Error: Bad RSA Private Key readed in '%s' file.\n",SECFILE);
exit(EXIT_FAILURE);
}
else
return key;
}
return key;
}
int rsa_encrypt(RSA *key, unsigned char *plain, int len, unsigned char **cipher)
{
int clen=0;
srand(time(NULL));
if((clen = RSA_public_encrypt(len, plain, *cipher, key, RSA_PKCS1_PADDING)) == -1) {
fprintf(stderr, "%s\n", ERR_error_string(ERR_get_error(), NULL));
exit(EXIT_FAILURE);
} else
return clen;
}
int rsa_decrypt(RSA *key, unsigned char *cipher, int len, unsigned char **plain)
{
int plen=0;
if((plen = RSA_private_decrypt(len, cipher, *plain, key, RSA_PKCS1_PADDING)) == -1) {
fprintf(stderr, "%s\n", ERR_error_string(ERR_get_error(), NULL));
exit(EXIT_FAILURE);
} else
return plen;
}
---------------------------------------------------------------------
readpemkeys() e' un esempio di come e' possibile leggere le chiavi
salvate in formato PEM.
Il codice e' molto semplice e quindi non ritengo necessario commenti.
E con questo programmino io avrei terminato questo articolo, che spero
vi abbia chiarito le idee su RSA e openssl.
Per eventuali chiarimenti potete scrivermi a paolo.ardoino@gmail.com
Grazie a tutti e alla prossima.
1 Comment so far
Manuel on
November 4th, 2008
Ciao,
sono un newbie e sto giocando con le librere openSSL.
ho trovato molto interessante il tuo articolo, ma ora sarei interessato in particolare alle funzioni RSA_sign e RSA_verify.
Ci sono degli esempi a riguardo? Perchè le API non sono chiarissime.
Ciao,
sono un newbie e sto giocando con le librere openSSL.
ho trovato molto interessante il tuo articolo, ma ora sarei interessato in particolare alle funzioni RSA_sign e RSA_verify.
Ci sono degli esempi a riguardo? Perchè le API non sono chiarissime.