原文連接: http://blog.csdn.net/yasi_xi/article/details/13997337php
Openssl是很常見的C接口的庫,我的以爲易用。如下是AES加密的使用備忘。若是你有必定的密碼學基礎,那麼就很好理解。代碼是從網上弄下來的(原始地址已經忘記了),而後在嘗試的過程當中改了一點東西。其它的cbc、cfb、ecb加密方式的用法都是相似的,只是函數名有點區別,就不一一列舉了。算法
【yasi】IV: Initialization Vector,即初始化向量數組
1、接口簡介app
- int AES_set_encrypt_key(
- const unsigned char *userKey,
- const int bits,
- AES_KEY *key);
-
- int AES_set_decrypt_key(
- const unsigned char *userKey,
- const int bits,
- AES_KEY *key);
-
- void AES_cbc_encrypt(
- const unsigned char *in,
- unsigned char *out,
- const unsigned long length,
- const AES_KEY *key,
- unsigned char *ivec,
- const int enc);
?
2、一個簡單的Makefiledom
【yasi】針對CentOS環境,作了修改ide
- LNK_OPT = -g -L/usr/lib64/ -lssl
-
- all:
- rm -f codec
- g++ -g aes_codec.cpp -o codec $(LNK_OPT)
-
- clean:
- rm -f codec
3、示例代碼
【yasi】對源連接的代碼作了點修改:隨機明碼 改 靜態明碼
- #include <stdio.h>
- #include <string.h>
- #include <openssl/aes.h>
- #include <openssl/rand.h>
-
-
- static void hexdump(
- FILE *f,
- const char *title,
- const unsigned char *s,
- int l)
- {
- int n = 0;
-
- fprintf(f, "%s", title);
- for (; n < l; ++n) {
- if ((n % 16) == 0) {
- fprintf(f, "\n%04x", n);
- }
- fprintf(f, " %02x", s[n]);
- }
-
- fprintf(f, "\n");
- }
-
- int main(int argc, char **argv)
- {
-
- unsigned char rkey[16];
-
- AES_KEY key;
-
-
-
- unsigned char plaintext[AES_BLOCK_SIZE * 4] =
- {
- 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'i', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'i',
- '0', '1', '2', '3', '4', '5', '6', '7', '0', '1', '2', '3', '4', '5', '6', '7',
- 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'i', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'i',
- '0', '1', '2', '3', '4', '5', '6', '7', '0', '1', '2', '3', '4', '5', '6', '7'
- };
- unsigned char ciphertext[AES_BLOCK_SIZE * 4];
- unsigned char checktext[AES_BLOCK_SIZE * 4];
-
-
- unsigned char iv[AES_BLOCK_SIZE * 4];
-
- unsigned char saved_iv[AES_BLOCK_SIZE * 4];
-
- int nr_of_bits = 0;
- int nr_of_bytes = 0;
-
-
- memset(ciphertext, 0, sizeof ciphertext);
- memset(checktext, 0, sizeof checktext);
-
-
- RAND_pseudo_bytes(rkey, sizeof rkey);
- RAND_pseudo_bytes(saved_iv, sizeof saved_iv);
-
- hexdump(stdout, "== rkey ==",
- rkey,
- sizeof(rkey));
- hexdump(stdout, "== iv ==",
- saved_iv,
- sizeof(saved_iv));
- printf("\n");
-
- hexdump(stdout, "== plaintext ==",
- plaintext,
- sizeof(plaintext));
- printf("\n");
-
-
- memcpy(iv, saved_iv, sizeof(iv));
- nr_of_bits = 8 * sizeof(rkey);
- AES_set_encrypt_key(rkey, nr_of_bits, &key);
- nr_of_bytes = sizeof(plaintext);
- AES_cbc_encrypt(plaintext,
- ciphertext,
- nr_of_bytes,
- &key,
- iv,
- AES_ENCRYPT);
-
- hexdump(stdout, "== ciphertext ==",
- ciphertext,
- sizeof(ciphertext));
- printf("\n");
-
- hexdump(stdout, "== iv changed ==",
- iv,
- sizeof(iv));
- printf("\n");
-
-
- memcpy(iv, saved_iv, sizeof(iv));
- nr_of_bits = 8 * sizeof(rkey);
- AES_set_decrypt_key(rkey, nr_of_bits, &key);
- nr_of_bytes = sizeof(ciphertext);
-
- AES_cbc_encrypt(ciphertext,
- checktext,
- nr_of_bytes,
- &key, iv,
- AES_DECRYPT);
- hexdump(stdout, "== checktext ==",
- checktext,
- sizeof(checktext));
- printf("\n");
-
- return 0;
- }
【yasi 運行結果】
- [root@ampcommons02 aes-codec]# ./codec
- == rkey ==
- 0000 81 ac 9b 38 1c 02 c5 c8 1d 7c a0 3f 87 be f2 c6
- == iv ==
- 0000 34 a2 f1 f5 f3 93 76 32 cd 77 ad fb c8 82 f2 1b
- 0010 f3 cc 51 f6 35 f3 49 8d 44 97 8c 2c 89 50 0d d7
- 0020 68 21 d7 2f 0a 90 29 c1 dd c9 39 bc 7c 4f 18 2f
- 0030 04 cc 42 5e 84 8e fe a9 c5 49 00 9f 30 55 94 c0
-
- == plaintext ==
- 0000 61 62 63 64 65 66 67 69 61 62 63 64 65 66 67 69
- 0010 30 31 32 33 34 35 36 37 30 31 32 33 34 35 36 37
- 0020 61 62 63 64 65 66 67 69 61 62 63 64 65 66 67 69
- 0030 30 31 32 33 34 35 36 37 30 31 32 33 34 35 36 37
-
- == ciphertext ==
- 0000 c4 63 72 29 21 28 7b a2 27 24 4a e4 bb 95 1a d1
- 0010 b8 13 0e 77 0c 8a a4 09 2f ca 85 43 41 b5 5b d5
- 0020 a3 60 92 58 5b dd 45 0c e2 62 af f9 43 81 d7 06
- 0030 41 8e 85 28 3e eb 72 b5 ee 84 8c 27 7e 67 20 f6
-
- == iv changed ==
- 0000 41 8e 85 28 3e eb 72 b5 ee 84 8c 27 7e 67 20 f6
- 0010 f3 cc 51 f6 35 f3 49 8d 44 97 8c 2c 89 50 0d d7
- 0020 68 21 d7 2f 0a 90 29 c1 dd c9 39 bc 7c 4f 18 2f
- 0030 04 cc 42 5e 84 8e fe a9 c5 49 00 9f 30 55 94 c0
-
- == checktext ==
- 0000 61 62 63 64 65 66 67 69 61 62 63 64 65 66 67 69
- 0010 30 31 32 33 34 35 36 37 30 31 32 33 34 35 36 37
- 0020 61 62 63 64 65 66 67 69 61 62 63 64 65 66 67 69
- 0030 30 31 32 33 34 35 36 37 30 31 32 33 34 35 36 37
可見,encryption以後,IV當即被修改了。因此,爲了能正確decrypt,在decrypt時,必須使用先前encrypt時的IV,即代碼第98行。
【第二次運行的結果】
- [root@ampcommons02 aes-codec]# ./codec
- == rkey ==
- 0000 29 68 75 4d a5 9e 83 9a ed f8 ec bc 2e b8 09 7e
- == iv ==
- 0000 b8 21 09 de 8f 58 6e be 73 be a7 10 fb 91 87 65
- 0010 65 9c d7 0e 4c 88 d2 65 ae de 0b 49 40 c7 75 df
- 0020 19 69 53 0b 11 5d ac e7 08 f6 ae df 16 66 e0 13
- 0030 75 41 f7 bb be 56 a1 dd a7 3e fb 4e 5d 9e e4 a2
-
- == plaintext ==
- 0000 61 62 63 64 65 66 67 69 61 62 63 64 65 66 67 69
- 0010 30 31 32 33 34 35 36 37 30 31 32 33 34 35 36 37
- 0020 61 62 63 64 65 66 67 69 61 62 63 64 65 66 67 69
- 0030 30 31 32 33 34 35 36 37 30 31 32 33 34 35 36 37
-
- == ciphertext ==
- 0000 5e 85 9c e8 65 d6 3b f9 03 9a a0 b5 78 bd f6 d4
- 0010 11 70 94 c1 c3 78 9a 1d 12 9a 84 48 3a 70 88 13
- 0020 d6 b5 bf c6 e8 e1 76 dc 3c b9 b0 4e b9 bb c4 74
- 0030 35 3d ac fc 29 e3 a0 64 d7 76 ab 76 c7 af dd 39
-
- == iv changed ==
- 0000 35 3d ac fc 29 e3 a0 64 d7 76 ab 76 c7 af dd 39
- 0010 65 9c d7 0e 4c 88 d2 65 ae de 0b 49 40 c7 75 df
- 0020 19 69 53 0b 11 5d ac e7 08 f6 ae df 16 66 e0 13
- 0030 75 41 f7 bb be 56 a1 dd a7 3e fb 4e 5d 9e e4 a2
-
- == checktext ==
- 0000 61 62 63 64 65 66 67 69 61 62 63 64 65 66 67 69
- 0010 30 31 32 33 34 35 36 37 30 31 32 33 34 35 36 37
- 0020 61 62 63 64 65 66 67 69 61 62 63 64 65 66 67 69
- 0030 30 31 32 33 34 35 36 37 30 31 32 33 34 35 36 37
可見,兩次運行,雖然encrypt的明碼是同樣的,但encrypt出來的結果是不一樣的。隨機明碼 改 靜態明碼 的目的就是爲了比較兩次encrypt的結果。即,相同的明碼用不一樣的IV作encrypt,結果是不一樣的。
【屏蔽第98行從新編譯後的執行結果】
- [root@ampcommons02 aes-codec]# ./codec
- == rkey ==
- 0000 69 ef eb 49 25 5a c2 5e 0d 77 8a cb e6 fe ad 1d
- == iv ==
- 0000 8e 05 8c 50 2f 69 9d fb 64 3e cd e6 2d 38 26 1c
- 0010 6e 21 00 e6 32 3f c6 ca 93 8b c1 e3 47 9a bd 81
- 0020 d7 e5 0b 63 dc f8 9d 1f 13 49 35 25 70 4e 64 c2
- 0030 ea 0c d0 78 e7 6c 65 64 41 4d db 2b 50 4d b4 06
-
- == plaintext ==
- 0000 61 62 63 64 65 66 67 69 61 62 63 64 65 66 67 69
- 0010 30 31 32 33 34 35 36 37 30 31 32 33 34 35 36 37
- 0020 61 62 63 64 65 66 67 69 61 62 63 64 65 66 67 69
- 0030 30 31 32 33 34 35 36 37 30 31 32 33 34 35 36 37
-
- == ciphertext ==
- 0000 a4 ed ba 4b 9f e9 74 bd 6d f6 03 76 79 9f 17 4f
- 0010 0c cd f3 b8 da 69 44 81 c9 f2 8b 03 83 0d 9d 77
- 0020 12 48 ea 46 3f eb 58 fd 48 c5 cc 2d 74 6c 99 4f
- 0030 93 bd 0d 06 f3 55 40 11 cb e7 d4 29 3b 8f 15 76
-
- == iv changed ==
- 0000 93 bd 0d 06 f3 55 40 11 cb e7 d4 29 3b 8f 15 76
- 0010 6e 21 00 e6 32 3f c6 ca 93 8b c1 e3 47 9a bd 81
- 0020 d7 e5 0b 63 dc f8 9d 1f 13 49 35 25 70 4e 64 c2
- 0030 ea 0c d0 78 e7 6c 65 64 41 4d db 2b 50 4d b4 06
-
- == checktext ==
- 0000 7c da e2 32 b9 5a ba 83 ce bb 7a ab 73 d1 54 03
- 0010 30 31 32 33 34 35 36 37 30 31 32 33 34 35 36 37
- 0020 61 62 63 64 65 66 67 69 61 62 63 64 65 66 67 69
- 0030 30 31 32 33 34 35 36 37 30 31 32 33 34 35 36 37
可見,decrypt時使用和encrypt時不一樣的IV,不能正確decrypt出先前的明碼。
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
【yasi】Openssl官方wiki,《EVP Symmetric Encryption and Decryption》中,有個的簡單例子,整理以下(Linux下運行):(代碼下載)
algo_aes.h
- #ifndef ALGO_AES_H
- #define ALGO_AES_H
-
- int encrypt(unsigned char *plaintext, int plaintext_len, unsigned char *key,
- unsigned char *iv, unsigned char *ciphertext);
-
- int decrypt(unsigned char *ciphertext, int ciphertext_len, unsigned char *key,
- unsigned char *iv, unsigned char *plaintext);
-
- #endif
algo_aes.c
- #include <stdlib.h>
- #include <stdio.h>
- #include "algo_aes.h"
- #include <openssl/evp.h>
-
- void handleErrors(void)
- {
- ERR_print_errors_fp(stderr);
- abort();
- }
-
- int encrypt(unsigned char *plaintext, int plaintext_len, unsigned char *key,
- unsigned char *iv, unsigned char *ciphertext)
- {
- EVP_CIPHER_CTX *ctx;
-
- int len;
-
- int ciphertext_len;
-
-
- if(!(ctx = EVP_CIPHER_CTX_new())) handleErrors();
-
-
- if(1 != EVP_EncryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, key, iv))
- handleErrors();
-
-
- if(1 != EVP_EncryptUpdate(ctx, ciphertext, &len, plaintext, plaintext_len))
- handleErrors();
- ciphertext_len = len;
-
-
- if(1 != EVP_EncryptFinal_ex(ctx, ciphertext + len, &len)) handleErrors();
- ciphertext_len += len;
-
-
- EVP_CIPHER_CTX_free(ctx);
-
- return ciphertext_len;
- }
-
- int decrypt(unsigned char *ciphertext, int ciphertext_len, unsigned char *key,
- unsigned char *iv, unsigned char *plaintext)
- {
- EVP_CIPHER_CTX *ctx;
-
- int len;
-
- int plaintext_len;
-
-
- if(!(ctx = EVP_CIPHER_CTX_new())) handleErrors();
-
-
- if(1 != EVP_DecryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, key, iv))
- handleErrors();
-
-
- if(1 != EVP_DecryptUpdate(ctx, plaintext, &len, ciphertext, ciphertext_len))
- handleErrors();
- plaintext_len = len;
-
-
- if(1 != EVP_DecryptFinal_ex(ctx, plaintext + len, &len)) handleErrors();
- plaintext_len += len;
-
-
- EVP_CIPHER_CTX_free(ctx);
-
- return plaintext_len;
- }
main.c
- #include "algo_aes.h"
- #include <stdio.h>
- #include <string.h>
-
- int main(int arc, char *argv[])
- {
-
-
-
- unsigned char *key = "01234567890123456789012345678901";
-
-
- unsigned char *iv = "01234567890123456";
-
-
- unsigned char *plaintext =
- "The quick brown fox jumps over the lazy dog1234";
-
-
- unsigned char ciphertext[64];
-
-
- unsigned char decryptedtext[64];
-
- int decryptedtext_len, ciphertext_len;
-
-
-
- printf("Plaintext is:\n%s~\n", plaintext);
-
-
- ciphertext_len = encrypt(plaintext, strlen(plaintext), key, iv,
- ciphertext);
-
-
- printf("Ciphertext is %d bytes long:\n", ciphertext_len);
- BIO_dump_fp(stdout, ciphertext, ciphertext_len);
-
-
- decryptedtext_len = decrypt(ciphertext, ciphertext_len, key, iv,
- decryptedtext);
-
-
- decryptedtext[decryptedtext_len] = '\0';
-
-
- printf("Decrypted text is:\n");
- printf("%s~\n", decryptedtext);
-
-
- EVP_cleanup();
- ERR_free_strings();
-
- return 0;
- }
Mekefile:
- OBJ_DIR = ./obj
- BIN_DIR = ./bin
- SRC_DIR = ./
- OBJS = \
- $(OBJ_DIR)/algo_aes.o \
- $(OBJ_DIR)/main.o
- TARGET = $(BIN_DIR)/main
- INC_OPT = -I./
- LNK_OPT = -lssl
-
- $(TARGET) : clean chkobjdir chkbindir $(OBJS)
- gcc -g -o $@ $(OBJS) $(LNK_OPT)
-
- $(OBJ_DIR)/%.o : $(SRC_DIR)/%.c
- gcc -g $(INC_OPT) -c -o $@ $<
-
- chkobjdir :
- @if test ! -d $(OBJ_DIR) ; \
- then \
- mkdir $(OBJ_DIR) ; \
- fi
-
- chkbindir :
- @if test ! -d $(BIN_DIR) ; \
- then \
- mkdir $(BIN_DIR) ; \
- fi
-
- clean :
- -rm -f $(TARGET)
- -rm -rf $(OBJ_DIR)
運行結果:
- Plaintext is:
- The quick brown fox jumps over the lazy dog1234~
- Ciphertext is 48 bytes long:
- 0000 - e0 6f 63 a7 11 e8 b7 aa-9f 94 40 10 7d 46 80 a1 .oc.......@.}F..
- 0010 - 17 99 43 80 ea 31 d2 a2-99 b9 53 02 d4 39 b9 70 ..C..1....S..9.p
- 0020 - e9 29 d5 a8 03 bd 71 31-b8 c3 6f 3d 39 3a 3d 3d .)....q1..o=9:==
- Decrypted text is:
- The quick brown fox jumps over the lazy dog1234~
注意:(參考)
AES算法的塊(block)的長度固定爲16字節。假設一個字符串在AES加密前的長度爲cleanLen,加密後的長度爲cipherLen,則兩者有下面的關係,其中的「/」是整除。wordpress
cipherLen = (clearLen/16 + 1) * 16函數
好比:(注意第二行,即便48恰好能被16整除,也要額外追加一個16字節的塊)
clearLen |
cipherLen |
47 |
48 |
48 |
64 |
49 |
64 |
可見,對於AES算法:
1)加密後的長度>=加密前的長度
2)解密後的長度<=解密前的長度
這對於寫代碼時的指導意義在於:
1)假如咱們要作AES加密的字符串爲「The quick brown fox jumps over the lazy dog」,它的長度(不帶/0)爲43字節,則咱們能夠計算出加密後的串的長度爲 (43 / 16 + 1) * 16 = 48 字節,因而就能夠申明加密後的數組以下
unsigned char ciphertextp[48];
2)假如咱們知道AES加密後的串的長度爲64字節,那麼就能夠申明解密後的數組以下
unsigned char decryptedtext[64];