OpenSSL Crypto Library
Table of Contents
1. OpenSSL 简介
OpenSSL 是开源的安全相关工具套件,包含了密码学中常用算法的实现。
OpenSSL 的版本历史如表 1 所示。
Version | Original release date | Last minor version |
---|---|---|
0.9.1 | December 23, 1998 | 0.9.1c (December 23, 1998) |
0.9.2 | March 22, 1999 | 0.9.2b (April 6, 1999) |
0.9.3 | May 25, 1999 | 0.9.3a (May 27, 1999) |
0.9.4 | August 9, 1999 | 0.9.4 (August 9, 1999) |
0.9.5 | February 28, 2000 | 0.9.5a (April 1, 2000) |
0.9.6 | September 24, 2000 | 0.9.6m (March 17, 2004) |
0.9.7 | December 31, 2002 | 0.9.7m (February 23, 2007) |
0.9.8 | July 5, 2005 | 0.9.8zh (December 3, 2015) |
1.0.0 | March 29, 2010 | 1.0.0t (December 3, 2015) |
1.0.1 | March 14, 2012 | 1.0.1u (September 22, 2016) |
1.0.2 | January 22, 2015 | 1.0.2u (December 20, 2019) |
1.1.0 | August 25, 2016 | 1.1.0l (September 10, 2019) |
1.1.1 | September 11, 2018 | 1.1.1w (11 September 2023) |
3.0.0 | 7 September 2021 | N/A |
3.1.0 | 14 March 2023 | N/A |
执行命令 openssl version
可检查系统中 OpenSSL 的版本:
$ openssl version OpenSSL 1.1.1 11 Sep 2018 $ openssl version -a # 指定 -a,可得到更详细信息 OpenSSL 1.1.1 11 Sep 2018 built on: Wed May 27 19:15:54 2020 UTC platform: debian-amd64 options: bn(64,64) rc4(16x,int) des(int) blowfish(ptr) compiler: gcc -fPIC -pthread -m64 -Wa,--noexecstack -Wall -Wa,--noexecstack -g -O2 -fdebug-prefix-map=/build/openssl-dyPhHZ/openssl-1.1.1=. -fstack-protector-strong -Wformat -Werror=format-security -DOPENSSL_USE_NODELETE -DL_ENDIAN -DOPENSSL_PIC -DOPENSSL_CPUID_OBJ -DOPENSSL_IA32_SSE2 -DOPENSSL_BN_ASM_MONT -DOPENSSL_BN_ASM_MONT5 -DOPENSSL_BN_ASM_GF2m -DSHA1_ASM -DSHA256_ASM -DSHA512_ASM -DKECCAK1600_ASM -DRC4_ASM -DMD5_ASM -DAES_ASM -DVPAES_ASM -DBSAES_ASM -DGHASH_ASM -DECP_NISTZ256_ASM -DX25519_ASM -DPADLOCK_ASM -DPOLY1305_ASM -DNDEBUG -Wdate-time -D_FORTIFY_SOURCE=2 OPENSSLDIR: "/usr/lib/ssl" ENGINESDIR: "/usr/lib/x86_64-linux-gnu/engines-1.1" Seeding source: os-specific
1.1. OpenSSL 开发
如果要进行 OpenSSL 相关开发,则需要安装对应的软件包:
$ sudo yum install openssl-devel # redhat $ sudo apt install libssl-dev # debian
OpenSSL 提供了两个库:libssl 和 libcrypto。其中,libssl 实现了 TLS 协议,libcrypto 实现了常用的密码学算法。
2. EVP(高层接口)
OpenSSL EVP(high-level interface to cryptographic functions)提供了丰富的密码学中的各种函数。可以实现了各种对称算法加解密、摘要算法以及非对称加解密、签名与验证等等。
EVP 是单词 Envelope 的缩写,参见:https://stackoverflow.com/questions/3055454/what-does-openssls-evp-mean
2.1. 哈希
下面是使用 EVP 进行哈希(MD5 和 SHA256)计算的例子:
#include <stdio.h> #include <openssl/evp.h> int digest_message(const unsigned char *message, size_t message_len, const char *digest_name, unsigned char *digest, unsigned int *digest_len) { EVP_MD_CTX *mdctx; const EVP_MD *md; if ((mdctx = EVP_MD_CTX_new()) == NULL) { return -1; } if ((md = EVP_get_digestbyname(digest_name)) == NULL) { return -1; } EVP_DigestInit_ex(mdctx, md, NULL); EVP_DigestUpdate(mdctx, message, message_len); EVP_DigestFinal_ex(mdctx, digest, digest_len); EVP_MD_CTX_free(mdctx); return 0; } void dumphex(const unsigned char *src, size_t len) { size_t i; for (i = 0; i < len; i++) { fprintf(stderr, "%02x", src[i]); } printf("\n"); } int main() { unsigned char input[3] = {'a', 'b', 'c'}; unsigned char hash_outbuf[EVP_MAX_MD_SIZE]; unsigned int hash_len; digest_message(input, 3, "md5", hash_outbuf, &hash_len); fprintf(stderr, "md5(abc):\t"); dumphex(hash_outbuf, hash_len); // 900150983cd24fb0d6963f7d28e17f72 fprintf(stderr, "sha256(abc):\t"); digest_message(input, 3, "sha256", hash_outbuf, &hash_len); dumphex(hash_outbuf, hash_len); // ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad return 0; }
编译并运行上面程序:
$ cc -I/usr/local/opt/openssl/include -L/usr/local/opt/openssl/lib -lcrypto hash_example.c -o hash_example $ ./hash_example md5(abc): 900150983cd24fb0d6963f7d28e17f72 sha256(abc): ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad
3. HMAC
OpenSSL 中支持 HMAC,下面是 HMAC SHA256 的一个实例:
#include <stdio.h> #include <openssl/hmac.h> void dumphex(const unsigned char *src, size_t len) { size_t i; for (i = 0; i < len; i++) { fprintf(stderr, "%02x", src[i]); } printf("\n"); } int main() { unsigned char input[3] = {'a', 'b', 'c'}; unsigned char key[4] = {'k', 'e', 'y', '1'}; unsigned char hash_outbuf[EVP_MAX_MD_SIZE]; unsigned int hash_len; const EVP_MD *evp_md = EVP_sha256(); HMAC(evp_md, key, 4, input, 3, hash_outbuf, &hash_len); fprintf(stderr, "msg=abc, key=key1, sha256: "); dumphex(hash_outbuf, hash_len); // 8ca63d43eee01f745de3fc6c2b2cdf1824b6ef81b1179bc3ca650d4288d3f445 return 0; }
编译并运行上面程序:
$ cc -I/usr/local/opt/openssl/include -L/usr/local/opt/openssl/lib -lcrypto hmac_example.c -o hmac_example $ ./hmac_example msg=abc, key=key1, sha256: 8ca63d43eee01f745de3fc6c2b2cdf1824b6ef81b1179bc3ca650d4288d3f445
库中的函数 HMAC 是对 HMAC_Init_ex/HMAC_Update/HMAC_Final 等函数的封装,HMAC 的源码如下:
// From https://github.com/openssl/openssl/blob/master/crypto/hmac/hmac.c unsigned char *HMAC(const EVP_MD *evp_md, const void *key, int key_len, const unsigned char *d, size_t n, unsigned char *md, unsigned int *md_len) { HMAC_CTX *c = NULL; static unsigned char m[EVP_MAX_MD_SIZE]; static const unsigned char dummy_key[1] = {'\0'}; if (md == NULL) md = m; if ((c = HMAC_CTX_new()) == NULL) goto err; /* For HMAC_Init_ex, NULL key signals reuse. */ if (key == NULL && key_len == 0) { key = dummy_key; } if (!HMAC_Init_ex(c, key, key_len, evp_md, NULL)) goto err; if (!HMAC_Update(c, d, n)) goto err; if (!HMAC_Final(c, md, md_len)) goto err; HMAC_CTX_free(c); return md; err: HMAC_CTX_free(c); return NULL; }
4. EC(椭圆曲线)
OpenSSL 支持椭圆曲线,下面介绍相关的数据结构:
- EC_GROUP 用于表达某一种椭圆曲线的定义;
- EC_POINT 用于表达椭圆曲线上的一个点;
- EC_KEY 用于表达私钥/公钥对(私钥是一个大整数,即 BIGNUM;而公钥是椭圆曲线上的点,即 EC_POINT)。
下面例子演示了随机生成一个密钥对,并输出其私钥/公钥:
#include <stdio.h> #include <openssl/ec.h> #include <openssl/objects.h> int CURVE_NAME = NID_secp256k1; void dumpBN(const BIGNUM *bn) { char *buf; if ((buf = BN_bn2hex(bn)) == NULL) { return; } fprintf(stderr, "%s\n", buf); OPENSSL_free(buf); // must free memory allocated by BN_bn2hex } void dumpPoint(const EC_POINT *point) { EC_GROUP *group; char *hex; if ((group=EC_GROUP_new_by_curve_name(CURVE_NAME)) == NULL) { return; } hex = EC_POINT_point2hex(group, point, POINT_CONVERSION_UNCOMPRESSED, NULL); fprintf(stderr, "%s\n", hex); OPENSSL_free(hex); // must free memory allocated by EC_POINT_point2hex } int main() { EC_KEY *key; const BIGNUM *privateKey; // 私钥是一个大整数 const EC_POINT *publicKey; // 公钥是椭圆曲线上的一个点 // 创建 key 对象 if (( key = EC_KEY_new_by_curve_name(CURVE_NAME)) == NULL) { return 1; } // 随机生成一个 key EC_KEY_generate_key(key); // 从 key 中得到私钥 privateKey = EC_KEY_get0_private_key(key); dumpBN(privateKey); // 从 key 中得到公钥 publicKey = EC_KEY_get0_public_key(key); dumpPoint(publicKey); return 0; }
编译并运行上面程序:
$ cc -I/usr/local/opt/openssl/include -L/usr/local/opt/openssl/lib -lcrypto ec_example.c -o ec_example $ ./ec_example # 由于 key 是随机生成,所以每次运行输出都不一样 92E4E014D80DD548161AA01B5C1956DD5B4E2B32DC5566DA79E631AB4A78D741 04F12B5B62EDFA5D2E5376B8B085F58684DE524096A1530EF5419B6DC61962399D93C5B4B7BD959016C01DDEF97C019E0C8E92A05D24E391811AD8B199EDCA5EF6