Видео смотреть бесплатно

Смотреть измена видео

Официальный сайт synclub 24/7/365

Смотреть видео бесплатно

h0rr0rr_drag0n 12.08.2009 18:24

Я рекомендуюИспользование библиотеки OpenSSL для (де)шифрования массива данных по алгоритму RSA.

Как-то, в целях выполнения универской практики, мне понадобилось разобраться в шифровании, а именно при помощи метода шифрования с открытым ключом зашифровывать и расшифровывать некий массив данных.

В процессе поиска примеров шифрования блока данных, при помощи RSA и OpenSSL я столкнулся с тем, что таких примеров нет (ну может быть я плохо искал :-)). Поэтому я решил написать и выложить свой пример.

В качестве метода шифрования я выбрал RSA, используемой библиотекой стала OpenSSL (API в man 3 crypto - исчерпывающая информация по используемым в посте функциям). Язык программирования естественно C.



Для начала надо сгенерировать открытый и закрытый ключи, которыми мы будем шифровать\дешифровать наш массив данных. Для создания закрытого ключа дадим команду:



openssl genrsa -out privkey.pem 2048



где 2048 – это размер ключа в битах, privkey.pem – имя файла, в который будет помещен закрытый ключ. Наш ключ не защищен паролем, но это поведение можно исправить, передав утилите опцию -des3.



В создании открытого ключа есть один нюанс – в официальной документации сказано, что закрытые ключи также включают в себя и информацию об открытых ключах, но ИМХО ненормально раздавать файл с закрытым ключом только ради использования открытого. Создадим отдельный файл с открытым ключом, используя наш закрытый ключ privkey.pem:



openssl rsa -in privkey.pem -out pubkey.pem -pubout



Чтение файлов ключей в структуру RSA, используемую библиотекой, производится при помощи функций



для публичного ключа:



1
PEM_read_RSA_PUBKEY();





для закрытого ключа:



1
PEM_read_RSAPrivateKey();





Первым параметром эти функции принимают указатель типа FILE * на открытый файл ключа. Остальные параметры отвечают за чтение ключа защищенного паролем и поэтому я их не использую.



Функция PEM_read_RSAPublicKey() не используется, потому что она по так и не установленной причине не читает ключ из файла, а возвращает NULL.



Шифрование данных будет производится функцией RSA_public_encrypt(), которая объявлена так:



1
2
3
4
int RSA_public_encrypt(int flen, unsigned char *from,

unsigned char *to, RSA *rsa, int padding);






где flen – размер шифруемых данных, from – указатель на шифруемые данные, to – указатель на зашифрованные данные, rsa – указатель на наш публичный RSA ключ. Параметр padding позволяет нам выбрать режим выравнивания данных; у нас используется значение RSA_PKCS1_PADDING. Размер буфера from должен быть не больше чем RSA_size(rsa) – 11, размер буфера to всегда будет равен RSA_size(rsa).



Поскольку размер зашифровываемых данных может быть больше чем RSA_size(rsa) – 11, используем функцию-обертку, которая разделяет данные на блоки соответствующего размера, шифрует их и склеивает обратно. Привожу ее код:

 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
 
/* Функция шифрует блок данных data, размером size, открытым ключом serv_pubkey.

* Возвращает указатель на зашифрованный блок данных и его размер в encsize.

* Возвращает NULL при ошибке.

*/

void * crypt_data(void * data, unsigned long int size, unsigned long int * encsize, RSA * serv_pubkey) {

void * cryptdata = NULL;

void * pcryptdata = NULL; /* указатель на начало зашифрованных

данных. Возвращается функцией

*/

unsigned long int cryptdatasize = 0; /* максимально необходимый размер области

памяти для зашифрованных данных

*/



/* Вычисляем размер максимально необходимой области памяти для зашифрованных данных

* как количество блоков, на которые делятся нешифрованные данные, умноженное на

* размер зашифрованного блока.

*/

cryptdatasize = ceil(((double)size / (double)(RSA_size(serv_pubkey) - 11))) * (double)RSA_size(serv_pubkey);

if ((cryptdata = malloc(cryptdatasize)) == NULL) {

return NULL;

}

pcryptdata = cryptdata;

*encsize = 0;



while (size > (RSA_size(serv_pubkey) - 11)) {

/* размер зашифрованного блока данных всегда гарантированно

* равен RSA_size(serv_pubkey)

*/

if (RSA_public_encrypt(RSA_size(serv_pubkey) - 11, data, cryptdata,

serv_pubkey, RSA_PKCS1_PADDING) == -1) {

return NULL;

}

data += RSA_size(serv_pubkey) - 11;

size -= RSA_size(serv_pubkey) - 11;

cryptdata += RSA_size(serv_pubkey);

*encsize += RSA_size(serv_pubkey);

} /* while (size > (RSA_size(serv_pubkey) - 11)) */

if (size != 0) { /* осталось еще немного данных */

if (RSA_public_encrypt(size, data, cryptdata,

serv_pubkey, RSA_PKCS1_PADDING) == -1) {

return NULL;

}

*encsize += RSA_size(serv_pubkey);

}



return pcryptdata;

}




Дешифрование данных производится функцией:

1
2
3
4
5
 
int RSA_private_decrypt(int flen, unsigned char *from,

unsigned char *to, RSA *rsa, int padding);




Ее параметры аналогичны параметрам функции для шифрования данных.



Размер блока для расшифровки всегда кратен RSA_size(our_key), а размер дешифрованного блока данных возвращается функцией RSA_private_decrypt(), поэтому код функции-обертки для расшифровки произвольного по размеру массива данных довольно прост:

 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
 
/* Функция расшифровывает блок данных data, размером size, ключом serv_privkey.

* Возвращает указатель на расшифрованный блок данных и его размер в decrypt_size.

* Возвращает NULL при ошибке.

*/

void * decrypt_data(void * data, unsigned long int size, unsigned long int * decrypt_size, RSA * serv_privkey) {

int decdatasize = 0;

/* размер расшифрованной области памяти гарантированно не

* может быть больше размера зашифрованной

*/

void * decdata = malloc(size);

void * pdecdata = decdata;



if (decdata == NULL) {

return NULL;

}

*decrypt_size = 0;



while (size > 0) {

if ((decdatasize = RSA_private_decrypt(RSA_size(serv_privkey), data, decdata, serv_privkey,

RSA_PKCS1_PADDING)) == -1) {

return NULL;

}

data += RSA_size(serv_privkey);

size -= RSA_size(serv_privkey);

*decrypt_size += decdatasize;

decdata += decdatasize;

}



return pdecdata;

}






В процессе написания и отлаживания кода можно столкнуться с тем, что функции библиотеки OpenSSL возвращают некоторые коды ошибок. Для их обработки у библиотеки OpenSSL есть свои методы:



1
2
fprintf(stderr,"Error: %s\n", ERR_error_string(ERR_get_error(), NULL));




Если произойдет ошибка, то этот код в обработчике ошибок выведет на STDOUT строку вида:



Error: error:0407006A:lib(4):func(112):reason(106)



Чтобы получить детальное описание ошибки, передадим полученный error code утилите openssl:



$ openssl errstr 0407006A

error:0407006A:rsa routines:RSA_padding_check_PKCS1_type_1:block type is not 01




Ну а далее в гугл или в ман-страницы разбираться в причинах возникновения ошибки.



Привожу полный исходный код программы, шифрующей при помощи открытого и дешифрующей при помощи закрытого ключей строку символов по алгоритму RSA.

rsafuncstest.c:

  1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
 
#include <err.h>

#include <errno.h>

#include <math.h>

#include <openssl/pem.h>

#include <openssl/rsa.h>

#include <stdio.h>

#include <stdlib.h>

#include <string.h>



#define PUBKEY "pubkey.pem"

#define PRIVKEY "privkey.pem"



/* Функция шифрует блок данных data, размером size, открытым ключом serv_pubkey.

* Возвращает указатель на зашифрованный блок данных и его размер в encsize.

* Возвращает NULL при ошибке.

*/

void * crypt_data(void * data, unsigned long int size, unsigned long int * encsize, RSA * serv_pubkey) {

void * cryptdata = NULL;

void * pcryptdata = NULL; /* указатель на начало зашифрованных

данных. Возвращается функцией

*/

unsigned long int cryptdatasize = 0; /* максимально необходимый размер области

памяти для зашифрованных данных

*/



/* Вычисляем размер максимально необходимой области памяти для зашифрованных данных

* как количество блоков, на которые делятся нешифрованные данные, умноженное на

* размер зашифрованного блока.

*/

cryptdatasize = ceil(((double)size / (double)(RSA_size(serv_pubkey) - 11))) * (double)RSA_size(serv_pubkey);

if ((cryptdata = malloc(cryptdatasize)) == NULL) {

return NULL;

}

pcryptdata = cryptdata;

*encsize = 0;



while (size > (RSA_size(serv_pubkey) - 11)) {

/* размер зашифрованного блока данных всегда гарантированно

* равен RSA_size(serv_pubkey)

*/

if (RSA_public_encrypt(RSA_size(serv_pubkey) - 11, data, cryptdata,

serv_pubkey, RSA_PKCS1_PADDING) == -1) {

return NULL;

}

data += RSA_size(serv_pubkey) - 11;

size -= RSA_size(serv_pubkey) - 11;

cryptdata += RSA_size(serv_pubkey);

*encsize += RSA_size(serv_pubkey);

} /* while (size > (RSA_size(serv_pubkey) - 11)) */

if (size != 0) { /* осталось еще немного данных */

if (RSA_public_encrypt(size, data, cryptdata,

serv_pubkey, RSA_PKCS1_PADDING) == -1) {

return NULL;

}

*encsize += RSA_size(serv_pubkey);

}



return pcryptdata;

}



/* Функция расшифровывает блок данных data, размером size, ключом serv_privkey.

* Возвращает указатель на расшифрованный блок данных и его размер в decrypt_size.

* Возвращает NULL при ошибке.

*/

void * decrypt_data(void * data, unsigned long int size, unsigned long int * decrypt_size, RSA * serv_privkey) {

int decdatasize = 0;

/* размер расшифрованной области памяти гарантированно не

* может быть больше размера зашифрованной

*/

void * decdata = malloc(size);

void * pdecdata = decdata;



if (decdata == NULL) {

return NULL;

}

*decrypt_size = 0;



while (size > 0) {

if ((decdatasize = RSA_private_decrypt(RSA_size(serv_privkey), data, decdata, serv_privkey,

RSA_PKCS1_PADDING)) == -1) {

return NULL;

}

data += RSA_size(serv_privkey);

size -= RSA_size(serv_privkey);

*decrypt_size += decdatasize;

decdata += decdatasize;

}



return pdecdata;

}



int main(int argc, char * argv
) {

FILE * serv_pubkey_file = NULL;

FILE * serv_privkey_file = NULL;

RSA * serv_privkey = NULL;

RSA * serv_pubkey = NULL;

char * data = "Fuck^WHack the planet!";

void * cryptdata = NULL;

unsigned long int cryptdatasize = 0;

void * decryptdata = NULL;

unsigned long int decryptdatasize = 0;



/* Считываем открытый ключ сервера */

if ((serv_pubkey_file = fopen(PUBKEY, "r")) == NULL) {

fprintf(stderr, "Error opening pubkey file %s: %s\n", PUBKEY, strerror(errno));

return -1;

}

if ((serv_pubkey = PEM_read_RSA_PUBKEY(serv_pubkey_file, NULL, NULL, NULL)) == NULL) {

fprintf(stderr, "Error reading RSA public key: %s\n", PUBKEY);

err(1, NULL);

return -1;

}

if (fclose(serv_pubkey_file) == EOF) {

fprintf(stderr, "Error closing %s: %s\n", PUBKEY, strerror(errno));

return -1;

}

/* Считываем закрытый ключ сервера */

if ((serv_privkey_file = fopen(PRIVKEY, "r")) == NULL) {

fprintf(stderr, "Error opening private key file %s: %s\n", PRIVKEY, strerror(errno));

return -1;

}

if ((serv_privkey = PEM_read_RSAPrivateKey(serv_privkey_file, NULL, NULL, NULL)) == NULL) {

fprintf(stderr, "Error reading RSA public key: %s\n", PRIVKEY);

return -1;

}

if (fclose(serv_privkey_file) == EOF) {

fprintf(stderr, "Error closing %s: %s\n", PRIVKEY, strerror(errno));

return -1;

}



if ((cryptdata = crypt_data(data, strlen(data) + 1, &cryptdatasize;, serv_pubkey)) == NULL) {

fprintf(stderr, "Error crypt data\n");

return -1;

}

if ((decryptdata = decrypt_data(cryptdata, cryptdatasize, &decryptdatasize;, serv_privkey)) == NULL) {

fprintf(stderr, "Error decrypt data\n");

return -1;

}



printf("Original string: \"%s\" with length: %d\nDecrypted string: \"%s\" with length: %lu\n",

data, strlen(data) + 1,

(char *)decryptdata, decryptdatasize);



return 0;

}




Makefile для сборки проекта:

 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
CC=gcc

SOURCES=rsafuncstest.c

OBJECTS=rsafuncstest.o

BIN=rft

CLFAGS=-c -Wall -ggdb

LDFLAGS=-o $(BIN)

LIBS=-lc -lssl -lm



all: make_objs

$(CC) $(LDFLAGS) $(OBJECTS) $(LIBS)



make_objs: $(SOURCES)

$(CC) $(CLFAGS) $(SOURCES)



.PHONY: clean

clean:

rm -f $(BIN) \

$(OBJECTS)





По материалам блока Дракон-линуксоид


Тэги: example openssl RSA
+ 6 -
Похожие Поделиться

n0p 13.08.2009 07:57 #
+ 0 -
Статья интересная, но что-то я сильно сомневаюсь в отсутствии примеров использования RSA :)
В молодости занимался вопросами безопасности и доводилось мне тогда писать несколько разных модулей шифрования/дешифрования данных на ассемблере. Помнится, не ощутил тогда проблем с информацией по алгоритмам и примерами на Цэ :)
А вообще, в мемориз, чтобы когда еще раз приспичит не искать в гугле :)
evgenyl 13.08.2009 09:55 #
+ 0 -
Небольшое дополнение к статье.

По правде говоря чтобы использовать RSA не нужна специализированная библиотека, этот повсеместно используемый алгоритм прост как 2 копейки.
Основная проблема это генерация устойчивых к взлому ключей, но если они есть, все очень просто.

Открытый ключ соостоит из 2х чисел e и n
а закрытый из d и n
e всегда равно 65537
Таким образом n открытый ключ, а d закрытый, все это обычные Числа и достаточно большие.
Теперь расмотрим процесс шифрования с помощью открытого ключа

У нас есть сообщение m его преобразуютв число но не большее чем наш ключ, иначе делаят на части и шифруют по отдельности.
Итак у нас есть m в числовом виде
Процес шифрования c=(m^e)%n вот такое вот сложное шифрование :)))
Расшифровываем приватным ключем m=(c^d)%n - тоже все предельно просто

Я редко пишу на си поэтому не очень хорошо знаю библиотеки, но например в питоне есть стандартная функция pow(x,y,n) которая возводит очень польшие числа x в степень y с остатком n, думаю чтото подобное есть и на C.
Питон поддерживает работу с числами любой длинны, в С прийдется искать опять же библиотеку.

Собственно вот и весь алгоритм.
Craftuser 13.08.2009 10:09 #
+ 0 -
После общения с челом, который работает в жутко секретной организации, понял что ломаются все сертифицированные и допущенные для использования алгоритмы шифрования:) Потому в шифровании вообще смысла не вижу:(
evgenyl 13.08.2009 10:11 #
+ 0 -
если я не ошибаюсь RSA сертифицирован до 1024 битов :)

Смотреть онлайн бесплатно

Онлайн видео бесплатно


Смотреть русское с разговорами видео

Online video HD

Видео скачать на телефон

Русские фильмы бесплатно

Full HD video online

Смотреть видео онлайн

Смотреть HD видео бесплатно

School смотреть онлайн