首頁(yè) > 解決方案 > 技術(shù)點(diǎn)滴
參考:http://www.askyb.com/cpp/openssl-hmac-hasing-example-in-cpp/
名詞解釋?zhuān)?/b>
HMAC: Hash-based Message Authentication Code,即基于Hash的消息鑒別碼
(下面的algo_hmac.h, algo_hmac.cpp 可以直接拿來(lái)放到自己的工程中)
本文工程在這里下載
algo_hmac.h
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
1. #include "algo_hmac.h"
2. #include
3. #include
4. #include
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
1. #include "algo_hmac.h"
2. #include
3. #include
4. #include
5. #include
6. #include
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
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
運(yùn)行結(jié)果
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!
各種算法得到的摘要的長(zhǎng)度
|
算法 |
摘要長(zhǎng)度(字節(jié)) |
|
MD2 |
16 |
|
MD5 |
16 |
|
SHA |
20 |
|
SHA1 |
20 |
|
SHA224 |
28 |
|
SHA256 |
32 |
|
SHA384 |
48 |
|
SHA512 |
64 |
注意
1)參考的帖子中的hmac_sample1.cpp,第13行沒(méi)有為digest申請(qǐng)空間,結(jié)果在第25行之后,做free(digest)會(huì)crash!但是,即使在第13行申請(qǐng)了足夠的空間,比如1024字節(jié),在第25行之后free(digest)還是會(huì)crash,原因如下:
參考 hmac.c 實(shí)現(xiàn)代碼, 如果傳入的 md 和 md_len 是NULL,則 HMAC 返回的是該函數(shù)里面定義的 static 的buffer的地址 m。這樣的m,由于是靜態(tài)分配的,不能在外面 free。
1. unsigned char *HMAC(const EVP_MD *evp_md, const void *key, int key_len,
2. const unsigned char *d, int n, unsigned char *md,
3. unsigned int *md_len)
4. {
5. HMAC_CTX c;
6. static unsigned char m[EVP_MAX_MD_SIZE];
7.
8. if (md == NULL) md=m;
9. HMAC_CTX_init(&c);
10. HMAC_Init(&c,key,key_len,evp_md);
11. HMAC_Update(&c,d,n);
12. HMAC_Final(&c,md,md_len);
13. HMAC_CTX_cleanup(&c);
14. return(md);
15.}
因此,建議使用 md, md_len 參數(shù)作為傳出值!
2)參考的帖子中的hmac_sample1.cpp,第23行的 mdString[i*2] 越界了,因?yàn)榈?/b>22行只申請(qǐng)了20字節(jié),應(yīng)改成 mdString[i]。
3)參考的帖子中的hmac_sample2.cpp,第17行有為result申請(qǐng)空間,在第36行做free(result)操作不會(huì)crash。但是根據(jù)上面表格所列的各種算法的摘要長(zhǎng)度,最大的長(zhǎng)度為64,于是申請(qǐng)的空間的大小可以用Openssl中的常量EVP_MAX_MD_SIZE,它的值就是64
4)參考的帖子中的hmac_sample2.cpp,第17行為result申請(qǐng)空間的操作,可以封裝到HmacEncode() 函數(shù)里面,使用者不用關(guān)心究竟要分配多少空間,傳進(jìn)一個(gè)指針即可,用完后free 它就可以了
5)HmacEncode 函數(shù)的output_length 參數(shù)是傳出參數(shù),HmacEncode 執(zhí)行結(jié)束后,會(huì)傳出HMAC的長(zhǎng)度
6)發(fā)現(xiàn)一個(gè)問(wèn)題:對(duì)于main() 中傳給HmacEncode() 的參數(shù)input,如果它的長(zhǎng)度≤56,那么如果其他參數(shù)不變的話,每次計(jì)算得到的結(jié)果是一致的;在其他參數(shù)不變的情況下,如果input的長(zhǎng)度大于56,則每次計(jì)算的結(jié)果是不一致的!也就是說(shuō),這里給成的代碼,對(duì)于長(zhǎng)度≤56字節(jié)的待編碼字串,每次執(zhí)行的結(jié)果是一致的,但對(duì)于長(zhǎng)度大于56字節(jié)的待編碼字串,不能保證輸出結(jié)果的一致性!難道說(shuō),algo_hmac.cpp中第43行的HMAC_Update() 有可能要執(zhí)行多次多次,即,把input按56個(gè)字節(jié)為一段的方式分成多段,多所有的分段依次調(diào)用HMAC_Update()?事實(shí)證明,原因不在于此。
對(duì)于6)的問(wèn)題,后來(lái)發(fā)現(xiàn),如果把const char * input 輸入?yún)?shù)去掉,把char data[] = "hello world";寫(xiě)在HmacEncode 里面,就不存在5)的問(wèn)題。出錯(cuò)的根本原因在于:
HMAC_Update() 函數(shù)的第二個(gè)參數(shù)是const unsigned char *類(lèi)型的,而不是 const unsigned char ** data 類(lèi)型的:int HMAC_Update(HMAC_CTX *ctx, const unsigned char *data, int len); 之所以參考的帖子中,以及把char data[] = "hello world";寫(xiě)在HmacEncode 里面,沒(méi)有出錯(cuò),是因?yàn)椋?b>對(duì)于數(shù)組char data[],用指針形式輸出data 和 &data,值是一樣的;而對(duì)于char * p,用指針形式輸出p 和 &p,值是不一樣的!對(duì)于數(shù)組char data[],&data仍然可以作為const unsigned char *類(lèi)型變量傳給HMAC_Update() 函數(shù),傳入的值不變;而對(duì)于 char * p,&p 實(shí)際上已經(jīng)是 char ** 類(lèi)型的了,并且它值和p 不一樣,實(shí)際上,這個(gè)臨時(shí)變量p的地址在程序每次運(yùn)行時(shí)都是不一樣的,于是,每次實(shí)際上傳給HMAC_Update() 函數(shù)的東西都是變化的,怪不得每次執(zhí)行的最終結(jié)果都不一樣了?。?b>上面貼的algo_hmac.cpp中的代碼是沒(méi)有問(wèn)題的,但請(qǐng)注意第46行的注釋?zhuān)?/b>)
- 中巨偉業(yè) -
相關(guān)案例