openssl实现hmac(c++)

来源:互联网 发布:外服盒子mac 编辑:程序博客网 时间:2024/05/17 07:45

参考:http://www.askyb.com/cpp/openssl-hmac-hasing-example-in-cpp/


名词解释:

HMAC: Hash-based Message Authentication Code,即基于Hash的消息鉴别码


(下面的algo_hmac.h, algo_hmac.cpp 可以直接拿来放到自己的工程中)

本文工程在这里下载

algo_hmac.h

[cpp] view plaincopy
  1. #ifndef _ALGO_HMAC_H_  
  2. #define _ALGO_HMAC_H_  
  3.   
  4. int HmacEncode(const char * algo,  
  5.         const char * key, unsigned int key_length,  
  6.         const char * input, unsigned int input_length,  
  7.         unsigned char * &output, unsigned int &output_length);  
  8.   
  9. #endif  

algo_hmac.cpp

[cpp] view plaincopy
  1. #include "algo_hmac.h"  
  2. #include <openssl/hmac.h>  
  3. #include <string.h>  
  4. #include <iostream>  
  5. using namespace std;  
  6.   
  7. int HmacEncode(const char * algo,  
  8.                 const char * key, unsigned int key_length,  
  9.                 const char * input, unsigned int input_length,  
  10.                 unsigned char * &output, unsigned int &output_length) {  
  11.         const EVP_MD * engine = NULL;  
  12.         if(strcasecmp("sha512", algo) == 0) {  
  13.                 engine = EVP_sha512();  
  14.         }  
  15.         else if(strcasecmp("sha256", algo) == 0) {  
  16.                 engine = EVP_sha256();  
  17.         }  
  18.         else if(strcasecmp("sha1", algo) == 0) {  
  19.                 engine = EVP_sha1();  
  20.         }  
  21.         else if(strcasecmp("md5", algo) == 0) {  
  22.                 engine = EVP_md5();  
  23.         }  
  24.         else if(strcasecmp("sha224", algo) == 0) {  
  25.                 engine = EVP_sha224();  
  26.         }  
  27.         else if(strcasecmp("sha384", algo) == 0) {  
  28.                 engine = EVP_sha384();  
  29.         }  
  30.         else if(strcasecmp("sha", algo) == 0) {  
  31.                 engine = EVP_sha();  
  32.         }  
  33.         else if(strcasecmp("md2", algo) == 0) {  
  34.                 engine = EVP_md2();  
  35.         }  
  36.         else {  
  37.                 cout << "Algorithm " << algo << " is not supported by this program!" << endl;  
  38.                 return -1;  
  39.         }  
  40.   
  41.         output = (unsigned char*)malloc(EVP_MAX_MD_SIZE);  
  42.   
  43.         HMAC_CTX ctx;  
  44.         HMAC_CTX_init(&ctx);  
  45.         HMAC_Init_ex(&ctx, key, strlen(key), engine, NULL);  
  46.         HMAC_Update(&ctx, (unsigned char*)input, strlen(input));        // input is OK; &input is WRONG !!!  
  47.   
  48.         HMAC_Final(&ctx, output, &output_length);  
  49.         HMAC_CTX_cleanup(&ctx);  
  50.   
  51.         return 0;  
  52. }  
main.cpp

[cpp] view plaincopy
  1. #include "algo_hmac.h"  
  2. #include <stdio.h>  
  3. #include <string.h>  
  4. #include <iostream>  
  5. #include <algorithm>  
  6. #include <string>  
  7. using namespace std;  
  8.   
  9. int main(int argc, char * argv[])  
  10. {  
  11.         if(argc < 2) {  
  12.                 cout << "Please specify a hash algorithm!" << endl;  
  13.                 return -1;  
  14.         }  
  15.   
  16.         char key[] = "012345678";  
  17.         string data = "hello world";  
  18.   
  19.         unsigned char * mac = NULL;  
  20.         unsigned int mac_length = 0;  
  21.   
  22.         int ret = HmacEncode(argv[1], key, strlen(key), data.c_str(), data.length(), mac, mac_length);  
  23.   
  24.         if(0 == ret) {  
  25.                 cout << "Algorithm HMAC encode succeeded!" << endl;  
  26.         }  
  27.         else {  
  28.                 cout << "Algorithm HMAC encode failed!" << endl;  
  29.                 return -1;  
  30.         }  
  31.   
  32.         cout << "mac length: " << mac_length << endl;  
  33.         cout << "mac:";  
  34.         for(int i = 0; i < mac_length; i++) {  
  35.                 printf("%-03x", (unsigned int)mac[i]);  
  36.         }  
  37.         cout << endl;  
  38.   
  39.         if(mac) {  
  40.                 free(mac);  
  41.                 cout << "mac is freed!" << endl;  
  42.         }  
  43.   
  44.         return 0;  
  45. }  
Makefile

[plain] view plaincopy
  1. LNK_OPT = -g -L/usr/lib64/ -lssl  
  2.   
  3. all:  
  4.     g++ -g -c algo_hmac.cpp  
  5.     g++ -g main.cpp -o test algo_hmac.o $(LNK_OPT)  
  6.   
  7. clean:  
  8.     rm -f *.o  
  9.     rm -f test  

运行结果

[plain] view plaincopy
  1. [root@ampcommons02 hmac]# ./test sha1  
  2. Algorithm HMAC encode succeeded!  
  3. mac length: 20  
  4. mac:e1 9e 22 1  22 b3 7b 70 8b fb 95 ac a2 57 79 5  ac ab f0 c0  
  5. mac is freed!  
  6.   
  7. [root@ampcommons02 hmac]# ./test sha256  
  8. Algorithm HMAC encode succeeded!  
  9. mac length: 32  
  10. mac:8b 4c 3e 7  ad 88 7a 8a 81 d0 80 ce d7 a3 bb 27 db a6 53 5f 28 fd 5e f1 45 2a 40 a6 e9 66 80 42  
  11. mac is freed!  
  12.   
  13. [root@ampcommons02 hmac]# ./test sha512  
  14. Algorithm HMAC encode succeeded!  
  15. mac length: 64  
  16. mac:40 81 e3 29 1e ec 15 4  91 10 e3 b8 d3 af 74 78 20 d1 c5 b5 39 f3 7b d7 72 49 a6 3c aa a6 e5 82 83 1  ae 2b 34 46 b1 ee 1c 45 39 d4 18 a6 4b 44 12 22 9f 9d 7  8e dc c7 8  8b 3b 41 b9 3c e6 e3  
  17. mac is freed!  
  18.   
  19. [root@ampcommons02 hmac]# ./test md5  
  20. Algorithm HMAC encode succeeded!  
  21. mac length: 16  
  22. mac:49 d5 2c b  92 54 b8 65 2b ea af fc 8d 7e 4c 21  
  23. mac is freed!  
各种算法得到的摘要的长度

算法摘要长度(字节)MD216MD516SHA20SHA120SHA22428SHA25632SHA38448SHA51264









注意

1)参考的帖子中的hmac_sample1.cpp,第13行没有为digest申请空间,结果在第25行之后,做free(digest)会crash!但是,即使在第13行申请了足够的空间,比如1024字节,在第25行之后free(digest)还是会crash,原因不清楚

2)参考的帖子中的hmac_sample2.cpp,第17行有为result申请空间,在第36行做free(result)操作不会crash。但是根据上面表格所列的各种算法的摘要长度,最大的长度为64,于是申请的空间的大小可以用Openssl中的常量EVP_MAX_MD_SIZE,它的值就是64

3)参考的帖子中的hmac_sample2.cpp,第17行为result申请空间的操作,可以封装到HmacEncode() 函数里面,使用者不用关心究竟要分配多少空间,传进一个指针即可,用完后free 它就可以了

4)HmacEncode 函数的output_length 参数是传出参数,HmacEncode 执行结束后,会传出HMAC的长度

5)发现一个问题:对于main() 中传给HmacEncode() 的参数input,如果它的长度≤56,那么如果其他参数不变的话,每次计算得到的结果是一致的;在其他参数不变的情况下,如果input的长度大于56,则每次计算的结果是不一致的!也就是说,这里给成的代码,对于长度≤56字节的待编码字串,每次执行的结果是一致的,但对于长度大于56字节的待编码字串,不能保证输出结果的一致性!难道说,algo_hmac.cpp中第43行的HMAC_Update() 有可能要执行多次多次,即,把input按56个字节为一段的方式分成多段,多所有的分段依次调用HMAC_Update()?事实证明,原因不在于此。

对于5)的问题,后来发现,如果把const char * input 输入参数去掉,把char data[] = "hello world";写在HmacEncode 里面,就不存在5)的问题。出错的根本原因在于:

HMAC_Update() 函数的第二个参数是const unsigned char *类型的,而不是 const unsigned char ** data 类型的:int HMAC_Update(HMAC_CTX *ctx, const unsigned char *data, int len); 之所以参考的帖子中,以及把char data[] = "hello world";写在HmacEncode 里面,没有出错,是因为,对于数组char data[],用指针形式输出data 和 &data,值是一样的;而对于char * p,用指针形式输出p 和 &p,值是不一样的对于数组char data[],&data仍然可以作为const unsigned char *类型变量传给HMAC_Update() 函数,传入的值不变;而对于 char * p,&p 实际上已经是 char ** 类型的了,并且它值和p 不一样,实际上,这个临时变量p的地址在程序每次运行时都是不一样的,于是,每次实际上传给HMAC_Update() 函数的东西都是变化的,怪不得每次执行的最终结果都不一样了!(上面贴的algo_hmac.cpp中的代码是没有问题的,但请注意第46行的注释!

详情见《C 字符串数组和char*指针在做&操作时的区别》。

原创粉丝点击