转自:http://www.thinkemb.com/wordpress/?p=18
参考:http://blog.csdn.net/shuanyancao/article/details/8985963
Openssl是很常见的C接口的库,个人觉得易用。以下是AES加密的使用备忘。如果你有一定的密码学基础,那么就很好理解。代码是从网上弄下来的(原始地址已经忘记了),然后在尝试的过程中改了一点东西。其它的cbc、cfb、ecb加密方式的用法都是类似的,只是函数名有点区别,就不一一列举了。
【yasi】IV: Initialization Vector,即初始化向量
一、接口简介
- //设置加密密钥,使用字符缓冲区
- 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);
- //加解密的接口,通过最后的enc来区分是加密还是解密操作
- //每次执行AES_cbc_encrypt后,iv(向量)会被更新,
- //所以需要自己保存它。
- 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);
二、一个简单的Makefile
【yasi】针对CentOS环境,做了修改
- LNK_OPT = -g -L/usr/lib64/ -lssl
- all:
- rm -f codec
- g++ -g aes_codec.cpp -o codec $(LNK_OPT)
- clean:
- rm -f codec
三、示例代码
- #include <stdio.h>
- #include <string.h>
- #include <openssl/aes.h>
- #include <openssl/rand.h>
- /* file testaes.cpp */
- 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)
- {
- //128bits key.
- unsigned char rkey[16];
- //Internal key.
- AES_KEY key;
- //Testdata.
- // [yasi] Make static content instead of random text
- 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];
- //Init vector.
- unsigned char iv[AES_BLOCK_SIZE * 4];
- //Save vector.
- unsigned char saved_iv[AES_BLOCK_SIZE * 4];
- int nr_of_bits = 0;
- int nr_of_bytes = 0;
- //Zeror buffer.
- memset(ciphertext, 0, sizeof ciphertext);
- memset(checktext, 0, sizeof checktext);
- //Generate random
- 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");
- //Entrypt
- 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");
- // [yasi] iv is changed in encryption
- hexdump(stdout, "== iv changed ==",
- iv,
- sizeof(iv));
- printf("\n");
- //Decrypt
- memcpy(iv, saved_iv, sizeof(iv)); // [yasi] without this line, decrypt will fail because iv is changed in encryption
- 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,结果是不同的。
- [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出先前的明码。
- #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;
- /* Create and initialise the context */
- if(!(ctx = EVP_CIPHER_CTX_new())) handleErrors();
- /* Initialise the encryption operation. IMPORTANT - ensure you use a key
- * and IV size appropriate for your cipher
- * In this example we are using 256 bit AES (i.e. a 256 bit key). The
- * IV size for *most* modes is the same as the block size. For AES this
- * is 128 bits */
- if(1 != EVP_EncryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, key, iv))
- handleErrors();
- /* Provide the message to be encrypted, and obtain the encrypted output.
- * EVP_EncryptUpdate can be called multiple times if necessary
- */
- if(1 != EVP_EncryptUpdate(ctx, ciphertext, &len, plaintext, plaintext_len))
- handleErrors();
- ciphertext_len = len;
- /* Finalise the encryption. Further ciphertext bytes may be written at
- * this stage.
- */
- if(1 != EVP_EncryptFinal_ex(ctx, ciphertext + len, &len)) handleErrors();
- ciphertext_len += len;
- /* Clean up */
- 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;
- /* Create and initialise the context */
- if(!(ctx = EVP_CIPHER_CTX_new())) handleErrors();
- /* Initialise the decryption operation. IMPORTANT - ensure you use a key
- * and IV size appropriate for your cipher
- * In this example we are using 256 bit AES (i.e. a 256 bit key). The
- * IV size for *most* modes is the same as the block size. For AES this
- * is 128 bits */
- if(1 != EVP_DecryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, key, iv))
- handleErrors();
- /* Provide the message to be decrypted, and obtain the plaintext output.
- * EVP_DecryptUpdate can be called multiple times if necessary
- */
- if(1 != EVP_DecryptUpdate(ctx, plaintext, &len, ciphertext, ciphertext_len))
- handleErrors();
- plaintext_len = len;
- /* Finalise the decryption. Further plaintext bytes may be written at
- * this stage.
- */
- if(1 != EVP_DecryptFinal_ex(ctx, plaintext + len, &len)) handleErrors();
- plaintext_len += len;
- /* Clean up */
- EVP_CIPHER_CTX_free(ctx);
- return plaintext_len;
- }
main.c
- #include "algo_aes.h"
- #include <stdio.h>
- #include <string.h>
- //#include <openssl/evp.h>
- int main(int arc, char *argv[])
- {
- /* Set up the key and iv. Do I need to say to not hard code these in a
- * real application? :-)
- */
- /* A 256 bit key */
- unsigned char *key = "01234567890123456789012345678901";
- /* A 128 bit IV */
- unsigned char *iv = "01234567890123456";
- /* Message to be encrypted */
- unsigned char *plaintext =
- "The quick brown fox jumps over the lazy dog1234";
- /* Buffer for ciphertext. Ensure the buffer is long enough for the
- * ciphertext which may be longer than the plaintext, dependant on the
- * algorithm and mode
- */
- unsigned char ciphertext[64];
- /* Buffer for the decrypted text */
- unsigned char decryptedtext[64];
- int decryptedtext_len, ciphertext_len;
- /* Initialise the library */
- /* ERR_load_crypto_strings();
- OpenSSL_add_all_algorithms();
- OPENSSL_config(NULL);*/
- printf("Plaintext is:\n%s~\n", plaintext);
- /* Encrypt the plaintext */
- ciphertext_len = encrypt(plaintext, strlen(plaintext), key, iv,
- ciphertext);
- /* Do something useful with the ciphertext here */
- printf("Ciphertext is %d bytes long:\n", ciphertext_len);
- BIO_dump_fp(stdout, ciphertext, ciphertext_len);
- /* Decrypt the ciphertext */
- decryptedtext_len = decrypt(ciphertext, ciphertext_len, key, iv,
- decryptedtext);
- /* Add a NULL terminator. We are expecting printable text */
- decryptedtext[decryptedtext_len] = '\0';
- /* Show the decrypted text */
- printf("Decrypted text is:\n");
- printf("%s~\n", decryptedtext);
- /* Clean up */
- 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,则二者有下面的关系,其中的“/”是整除。
cipherLen = (clearLen/16 + 1) * 16
clearLen | cipherLen |
---|---|
47 | 48 |
48 | 64 |
49 | 64 |