1. 程式人生 > >C/C++使用openssl進行摘要和加密解密(md5, sha256, des, rsa)

C/C++使用openssl進行摘要和加密解密(md5, sha256, des, rsa)

fin hex pau 字節 system org key rate 釋放

openssl裏面有很多用於摘要哈希、加密解密的算法,方便集成於工程項目,被廣泛應用於網絡報文中的安全傳輸和認證。下面以md5,sha256,des,rsa幾個典型的api簡單使用作為例子。

算法介紹

md5:https://en.wikipedia.org/wiki/MD5

sha256:https://en.wikipedia.org/wiki/SHA-2

des: https://en.wikipedia.org/wiki/Data_Encryption_Standard

rsa: https://en.wikipedia.org/wiki/RSA_(cryptosystem)

工程配置

以windows下為例

  1. 編譯openssl庫,得到頭文件include和鏈接庫lib和dll
  2. 配置包含頭文件目錄和庫目錄
  3. 工程中設置鏈接指定的lib:fenbieshlibssl.lib,libcrypto.lib
  4. 將對應的dll拷貝到exe執行目錄:libcrypto-1_1.dll, libssl-1_1.dll
linux下同理

代碼

[cpp] view plain copy print?
  1. #include <iostream>
  2. #include <cassert>
  3. #include <string>
  4. #include <vector>
  5. #include "openssl/md5.h"
  6. #include "openssl/sha.h"
  7. #include "openssl/des.h"
  8. #include "openssl/rsa.h"
  9. #include "openssl/pem.h"
  10. // ---- md5摘要哈希 ---- //
  11. void md5(const std::string &srcStr, std::string &encodedStr, std::string &encodedHexStr)
  12. {
  13. // 調用md5哈希
  14. unsigned char mdStr[33] = {0};
  15. MD5((const unsigned char *)srcStr.c_str(), srcStr.length(), mdStr);
  16. // 哈希後的字符串
  17. encodedStr = std::string((const char *)mdStr);
  18. // 哈希後的十六進制串 32字節
  19. char buf[65] = {0};
  20. char tmp[3] = {0};
  21. for (int i = 0; i < 32; i++)
  22. {
  23. sprintf(tmp, "%02x", mdStr[i]);
  24. strcat(buf, tmp);
  25. }
  26. buf[32] = ‘\0‘; // 後面都是0,從32字節截斷
  27. encodedHexStr = std::string(buf);
  28. }
  29. // ---- sha256摘要哈希 ---- //
  30. void sha256(const std::string &srcStr, std::string &encodedStr, std::string &encodedHexStr)
  31. {
  32. // 調用sha256哈希
  33. unsigned char mdStr[33] = {0};
  34. SHA256((const unsigned char *)srcStr.c_str(), srcStr.length(), mdStr);
  35. // 哈希後的字符串
  36. encodedStr = std::string((const char *)mdStr);
  37. // 哈希後的十六進制串 32字節
  38. char buf[65] = {0};
  39. char tmp[3] = {0};
  40. for (int i = 0; i < 32; i++)
  41. {
  42. sprintf(tmp, "%02x", mdStr[i]);
  43. strcat(buf, tmp);
  44. }
  45. buf[32] = ‘\0‘; // 後面都是0,從32字節截斷
  46. encodedHexStr = std::string(buf);
  47. }
  48. // ---- des對稱加解密 ---- //
  49. // 加密 ecb模式
  50. std::string des_encrypt(const std::string &clearText, const std::string &key)
  51. {
  52. std::string cipherText; // 密文
  53. DES_cblock keyEncrypt;
  54. memset(keyEncrypt, 0, 8);
  55. // 構造補齊後的密鑰
  56. if (key.length() <= 8)
  57. memcpy(keyEncrypt, key.c_str(), key.length());
  58. else
  59. memcpy(keyEncrypt, key.c_str(), 8);
  60. // 密鑰置換
  61. DES_key_schedule keySchedule;
  62. DES_set_key_unchecked(&keyEncrypt, &keySchedule);
  63. // 循環加密,每8字節一次
  64. const_DES_cblock inputText;
  65. DES_cblock outputText;
  66. std::vector<unsigned char> vecCiphertext;
  67. unsigned char tmp[8];
  68. for (int i = 0; i < clearText.length() / 8; i++)
  69. {
  70. memcpy(inputText, clearText.c_str() + i * 8, 8);
  71. DES_ecb_encrypt(&inputText, &outputText, &keySchedule, DES_ENCRYPT);
  72. memcpy(tmp, outputText, 8);
  73. for (int j = 0; j < 8; j++)
  74. vecCiphertext.push_back(tmp[j]);
  75. }
  76. if (clearText.length() % 8 != 0)
  77. {
  78. int tmp1 = clearText.length() / 8 * 8;
  79. int tmp2 = clearText.length() - tmp1;
  80. memset(inputText, 0, 8);
  81. memcpy(inputText, clearText.c_str() + tmp1, tmp2);
  82. // 加密函數
  83. DES_ecb_encrypt(&inputText, &outputText, &keySchedule, DES_ENCRYPT);
  84. memcpy(tmp, outputText, 8);
  85. for (int j = 0; j < 8; j++)
  86. vecCiphertext.push_back(tmp[j]);
  87. }
  88. cipherText.clear();
  89. cipherText.assign(vecCiphertext.begin(), vecCiphertext.end());
  90. return cipherText;
  91. }
  92. // 解密 ecb模式
  93. std::string des_decrypt(const std::string &cipherText, const std::string &key)
  94. {
  95. std::string clearText; // 明文
  96. DES_cblock keyEncrypt;
  97. memset(keyEncrypt, 0, 8);
  98. if (key.length() <= 8)
  99. memcpy(keyEncrypt, key.c_str(), key.length());
  100. else
  101. memcpy(keyEncrypt, key.c_str(), 8);
  102. DES_key_schedule keySchedule;
  103. DES_set_key_unchecked(&keyEncrypt, &keySchedule);
  104. const_DES_cblock inputText;
  105. DES_cblock outputText;
  106. std::vector<unsigned char> vecCleartext;
  107. unsigned char tmp[8];
  108. for (int i = 0; i < cipherText.length() / 8; i++)
  109. {
  110. memcpy(inputText, cipherText.c_str() + i * 8, 8);
  111. DES_ecb_encrypt(&inputText, &outputText, &keySchedule, DES_DECRYPT);
  112. memcpy(tmp, outputText, 8);
  113. for (int j = 0; j < 8; j++)
  114. vecCleartext.push_back(tmp[j]);
  115. }
  116. if (cipherText.length() % 8 != 0)
  117. {
  118. int tmp1 = cipherText.length() / 8 * 8;
  119. int tmp2 = cipherText.length() - tmp1;
  120. memset(inputText, 0, 8);
  121. memcpy(inputText, cipherText.c_str() + tmp1, tmp2);
  122. // 解密函數
  123. DES_ecb_encrypt(&inputText, &outputText, &keySchedule, DES_DECRYPT);
  124. memcpy(tmp, outputText, 8);
  125. for (int j = 0; j < 8; j++)
  126. vecCleartext.push_back(tmp[j]);
  127. }
  128. clearText.clear();
  129. clearText.assign(vecCleartext.begin(), vecCleartext.end());
  130. return clearText;
  131. }
  132. // ---- rsa非對稱加解密 ---- //
  133. #define KEY_LENGTH 2048 // 密鑰長度
  134. #define PUB_KEY_FILE "pubkey.pem" // 公鑰路徑
  135. #define PRI_KEY_FILE "prikey.pem" // 私鑰路徑
  136. // 函數方法生成密鑰對
  137. void generateRSAKey(std::string strKey[2])
  138. {
  139. // 公私密鑰對
  140. size_t pri_len;
  141. size_t pub_len;
  142. char *pri_key = NULL;
  143. char *pub_key = NULL;
  144. // 生成密鑰對
  145. RSA *keypair = RSA_generate_key(KEY_LENGTH, RSA_3, NULL, NULL);
  146. BIO *pri = BIO_new(BIO_s_mem());
  147. BIO *pub = BIO_new(BIO_s_mem());
  148. PEM_write_bio_RSAPrivateKey(pri, keypair, NULL, NULL, 0, NULL, NULL);
  149. PEM_write_bio_RSAPublicKey(pub, keypair);
  150. // 獲取長度
  151. pri_len = BIO_pending(pri);
  152. pub_len = BIO_pending(pub);
  153. // 密鑰對讀取到字符串
  154. pri_key = (char *)malloc(pri_len + 1);
  155. pub_key = (char *)malloc(pub_len + 1);
  156. BIO_read(pri, pri_key, pri_len);
  157. BIO_read(pub, pub_key, pub_len);
  158. pri_key[pri_len] = ‘\0‘;
  159. pub_key[pub_len] = ‘\0‘;
  160. // 存儲密鑰對
  161. strKey[0] = pub_key;
  162. strKey[1] = pri_key;
  163. // 存儲到磁盤(這種方式存儲的是begin rsa public key/ begin rsa private key開頭的)
  164. FILE *pubFile = fopen(PUB_KEY_FILE, "w");
  165. if (pubFile == NULL)
  166. {
  167. assert(false);
  168. return;
  169. }
  170. fputs(pub_key, pubFile);
  171. fclose(pubFile);
  172. FILE *priFile = fopen(PRI_KEY_FILE, "w");
  173. if (priFile == NULL)
  174. {
  175. assert(false);
  176. return;
  177. }
  178. fputs(pri_key, priFile);
  179. fclose(priFile);
  180. // 內存釋放
  181. RSA_free(keypair);
  182. BIO_free_all(pub);
  183. BIO_free_all(pri);
  184. free(pri_key);
  185. free(pub_key);
  186. }
  187. // 命令行方法生成公私鑰對(begin public key/ begin private key)
  188. // 找到openssl命令行工具,運行以下
  189. // openssl genrsa -out prikey.pem 1024
  190. // openssl rsa - in privkey.pem - pubout - out pubkey.pem
  191. // 公鑰加密
  192. std::string rsa_pub_encrypt(const std::string &clearText, const std::string &pubKey)
  193. {
  194. std::string strRet;
  195. RSA *rsa = NULL;
  196. BIO *keybio = BIO_new_mem_buf((unsigned char *)pubKey.c_str(), -1);
  197. // 此處有三種方法
  198. // 1, 讀取內存裏生成的密鑰對,再從內存生成rsa
  199. // 2, 讀取磁盤裏生成的密鑰對文本文件,在從內存生成rsa
  200. // 3,直接從讀取文件指針生成rsa
  201. RSA* pRSAPublicKey = RSA_new();
  202. rsa = PEM_read_bio_RSAPublicKey(keybio, &rsa, NULL, NULL);
  203. int len = RSA_size(rsa);
  204. char *encryptedText = (char *)malloc(len + 1);
  205. memset(encryptedText, 0, len + 1);
  206. // 加密函數
  207. int ret = RSA_public_encrypt(clearText.length(), (const unsigned char*)clearText.c_str(), (unsigned char*)encryptedText, rsa, RSA_PKCS1_PADDING);
  208. if (ret >= 0)
  209. strRet = std::string(encryptedText, ret);
  210. // 釋放內存
  211. free(encryptedText);
  212. BIO_free_all(keybio);
  213. RSA_free(rsa);
  214. return strRet;
  215. }
  216. // 私鑰解密
  217. std::string rsa_pri_decrypt(const std::string &cipherText, const std::string &priKey)
  218. {
  219. std::string strRet;
  220. RSA *rsa = RSA_new();
  221. BIO *keybio;
  222. keybio = BIO_new_mem_buf((unsigned char *)priKey.c_str(), -1);
  223. // 此處有三種方法
  224. // 1, 讀取內存裏生成的密鑰對,再從內存生成rsa
  225. // 2, 讀取磁盤裏生成的密鑰對文本文件,在從內存生成rsa
  226. // 3,直接從讀取文件指針生成rsa
  227. rsa = PEM_read_bio_RSAPrivateKey(keybio, &rsa, NULL, NULL);
  228. int len = RSA_size(rsa);
  229. char *decryptedText = (char *)malloc(len + 1);
  230. memset(decryptedText, 0, len + 1);
  231. // 解密函數
  232. int ret = RSA_private_decrypt(cipherText.length(), (const unsigned char*)cipherText.c_str(), (unsigned char*)decryptedText, rsa, RSA_PKCS1_PADDING);
  233. if (ret >= 0)
  234. strRet = std::string(decryptedText, ret);
  235. // 釋放內存
  236. free(decryptedText);
  237. BIO_free_all(keybio);
  238. RSA_free(rsa);
  239. return strRet;
  240. }
  241. int main(int argc, char **argv)
  242. {
  243. // 原始明文
  244. std::string srcText = "this is an example";
  245. std::string encryptText;
  246. std::string encryptHexText;
  247. std::string decryptText;
  248. std::cout << "=== 原始明文 ===" << std::endl;
  249. std::cout << srcText << std::endl;
  250. // md5
  251. std::cout << "=== md5哈希 ===" << std::endl;
  252. md5(srcText, encryptText, encryptHexText);
  253. std::cout << "摘要字符: " << encryptText << std::endl;
  254. std::cout << "摘要串: " << encryptHexText << std::endl;
  255. // sha256
  256. std::cout << "=== sha256哈希 ===" << std::endl;
  257. sha256(srcText, encryptText, encryptHexText);
  258. std::cout << "摘要字符: " << encryptText << std::endl;
  259. std::cout << "摘要串: " << encryptHexText << std::endl;
  260. // des
  261. std::cout << "=== des加解密 ===" << std::endl;
  262. std::string desKey = "12345";
  263. encryptText = des_encrypt(srcText, desKey);
  264. std::cout << "加密字符: " << std::endl;
  265. std::cout << encryptText << std::endl;
  266. decryptText = des_decrypt(encryptText, desKey);
  267. std::cout << "解密字符: " << std::endl;
  268. std::cout << decryptText << std::endl;
  269. // rsa
  270. std::cout << "=== rsa加解密 ===" << std::endl;
  271. std::string key[2];
  272. generateRSAKey(key);
  273. std::cout << "公鑰: " << std::endl;
  274. std::cout << key[0] << std::endl;
  275. std::cout << "私鑰: " << std::endl;
  276. std::cout << key[1] << std::endl;
  277. encryptText = rsa_pub_encrypt(srcText, key[0]);
  278. std::cout << "加密字符: " << std::endl;
  279. std::cout << encryptText << std::endl;
  280. decryptText = rsa_pri_decrypt(encryptText, key[1]);
  281. std::cout << "解密字符: " << std::endl;
  282. std::cout << decryptText << std::endl;
  283. system("pause");
  284. return 0;
  285. }


運行結果

[plain] view plain copy print?
  1. === 原始明文 ===
  2. this is an example
  3. === md5哈希 ===
  4. 摘要字符: 乵驥!範
  5. 摘要串: 9202816dabaaf34bb106a10421b9a0d0
  6. === sha256哈希 ===
  7. 摘要字符: 訪X5衽鄁媫j/醢?17?P?4膡zD
  8. 摘要串: d44c035835f1c5e0668b7d186a2ff5b0
  9. === des加解密 ===
  10. 加密字符:
  11. ?/灲取鮋t8:夽U錺?說
  12. 解密字符:
  13. this is an example
  14. === rsa加解密 ===
  15. 公鑰:
  16. -----BEGIN RSA PUBLIC KEY-----
  17. MIIBCAKCAQEA59WESdYbPsD6cYATooC4ebClTpvbTsu3X29Ha0g31kW3AmLR2zLj
  18. hMvdWjUhhVuM7xBoh3Ufoyj4jTGHVhunFfbzxNrt1Nb64N95bZH8e9u6LjJYqh4e
  19. sNoFknG+McjoSLNqGW9Yd8ejKH1Ju6C9SBUcC43XbB3XdC2matgV1zTsKhqjuywm
  20. gVN9DZdo2TlZkqsvOHC23rbQ+lP09rpQJ/RI4NQSnCUBqQxErCN85trcWRj1zyJA
  21. WaBZSvKh7J5RJcrC2ByMDmL7jrDDZl7YEolyW93SSc4xTE9Dr20OXznXNDsfQc9r
  22. RQHBri8Aqsu4WW3tHSBRmjW5kxFMxS4qxwIBAw==
  23. -----END RSA PUBLIC KEY-----
  24. 私鑰:
  25. -----BEGIN RSA PRIVATE KEY-----
  26. MIIEowIBAAKCAQEA59WESdYbPsD6cYATooC4ebClTpvbTsu3X29Ha0g31kW3AmLR
  27. 2zLjhMvdWjUhhVuM7xBoh3Ufoyj4jTGHVhunFfbzxNrt1Nb64N95bZH8e9u6LjJY
  28. qh4esNoFknG+McjoSLNqGW9Yd8ejKH1Ju6C9SBUcC43XbB3XdC2matgV1zTsKhqj
  29. uywmgVN9DZdo2TlZkqsvOHC23rbQ+lP09rpQJ/RI4NQSnCUBqQxErCN85trcWRj1
  30. zyJAWaBZSvKh7J5RJcrC2ByMDmL7jrDDZl7YEolyW93SSc4xTE9Dr20OXznXNDsf
  31. Qc9rRQHBri8Aqsu4WW3tHSBRmjW5kxFMxS4qxwIBAwKCAQEAmo5YMTlnfytRoQAN
  32. FwB6+8sY3xKSNIfPlPTaR4V6jtkkrEHhPMyXrd0+PCNrrj0In2BFr6NqbMX7CMuv
  33. jr0aDqSigzyejeSnQJT7nmFS/T0myXblxr6/IJFZDEvUITCa2yJGu5+QT9psxajb
  34. 0mso2ri9XQk6SBPk+B5u8eVj5Myt4tqpWL0DEEDzwfhihs+uEGM7g6bPvQBI4JXu
  35. 8uxfSRUkpyZ5s1koEhqj+RCguksPzSWO/Ut2Sd60iOUMRhya2aEbAyRTtfhsXja3
  36. 4NMWjXorJ0SRkryM1iLJvVWkhkcr2vShH9rm9qz16BkrkI9/9Yx++GNNr6VU/p/+
  37. Waa8CwKBgQD4m0ryXi6rCqazdCICGoZJGzaljApOZ1rWOiotM9TekaYE7tZ2NDAT
  38. eytiCzxvs4/+1Jt5XzdGJ035VJKSai/n2ZzAq1YYtVHy5CG2olmeFtwaIWU18m2s
  39. RjHQf/FiscVB4XdKrHjh3gLgSB8MWMDg/krisxT86HNyp1UE2jZv+QKBgQDuuoez
  40. V+H23ktb9oDS9HuLXt+wZuww29uNb0jhVoLiqK6M90Pl2u8yErjsq04cG9pF0MUl
  41. 8/nIw4RRKQh9GUOBBbxZqA/1yBxmHTz48siYJ3YXf5HB+0WxxOlEk3s05AnTilTi
  42. 5Y4u9Ptwieoy+TOXatBL9XZgKkpHbcxKZH2gvwKBgQClvNyhlB8cscR3osFWvFmG
  43. EiRuXVw0ROc5fBweIo3ptm6t9I75eCAM/MeWsihKd7VUjbz7lM+EGjP7jbcMRsqa
  44. kRMrHOQQeOFMmBZ5wZEUDz1mwO4j9vPILsvgVUuXIS4r66TccvtBPqyVhWoIOytA
  45. qYdBzLiomvehxONYkXmf+wKBgQCfJwUiOpaklDI9TwCMov0HlJUgRJ115+ezn4Xr
  46. jwHscHRd+i1D50ohYdCdx4loEpGD4INuoqaF162LcLBTZi0Arn2RGrVOhWhEE337
  47. TIW6xPlk/7aBUi52g0Ytt6d4mAaNBuNB7l7J+KegW/F3UM0PnIrdTk7qxtwvnogx
  48. mFPAfwKBgAEuRGqF2Q9bNu/r0OufeFxsYm0zFvWBIxbq3DxPYRtzfhiQMeTOzl1g
  49. 5rowAtb/w1SusGAZ4/lEUZoBgzV+8fr+rpx3eavVCmcXBVjDi9B5nNLIXWkcoEQG
  50. G/4ZwXUr5kyTBktL6mIBVNJ8dJUQo8xyxK0GjfWhlsk5t/Zu8tmK
  51. -----END RSA PRIVATE KEY-----
  52. 加密字符:
  53. 佷篒?z_&欗霐嗪K赸;J╄[i9?S絑?て晄p?[hD∞51鱠,k|1裡郿 犓鈪鑒?饞w2?`vlu
  54. L<萿囂?圖L潥?O0佲y▃ 颼E堿^桮??,e鉀煯ACsJ挈R聡-鯿帔!eQC乥+1\(齀
  55. я盈Xj飲[o6覾羂≯傁澓
  56. 解密字符:
  57. this is an example

註:

(1)在讀取公鑰文件時,PEM_read_RSA_PUBKEY()函數和PEM_read_RSAPublicKEY()的疑惑。有時候為什麽讀取私鑰文件用的PEM_read_RSAPrivateKey(),針對上述openssl命令生成的公鑰文件,在讀取其內容時用對稱的PEM_read_RSAPublicKEY()接口卻會報錯,必須要用PEM_read_RSA_PUBKEY()才可以。

RSA PUBLIC KEY和PUBLIC KEY的兩種公鑰文件其存儲方式是不一樣的,PEM_read_RSAPublicKEY()只能讀取RSA PUBLIC KEY開頭形式的公鑰文件(用函數生成的);而PEM_read_RSA_PUBKEY()只能讀取PUBLIC KEY開頭格式的公鑰文件(用命令行生成),所以公鑰私鑰讀取函數一定要跟生成的密鑰對的格式對應起來。

(2)公鑰加密和私鑰解密, 私鑰加密公鑰解密 這兩種都可以使用

(3)一般加密之後的字符串因為編碼跟中文對應不上所以是亂碼,在很多場合選擇用十六進制串輸出

(4)實際的工程應用中讀取密鑰對需要加安全驗證

(5)用純代碼不依賴openssl庫也是可以自己實現這些加解密算法的,搞清楚原理就行

http://blog.csdn.net/u012234115/article/details/72762045

C/C++使用openssl進行摘要和加密解密(md5, sha256, des, rsa)