相关链接:
摘要算法:MD5 及 Java实现样例
摘要算法:SHA 及 Java 实现样例
本文主要介绍如何使用 openssl 库实现 md5、sha256 摘要算法。
Case 1:md5
#include
#include
#include
int main(int argc, char *argv[])
{
EVP_MD_CTX mdctx;
const EVP_MD *md;
unsigned char md_value[EVP_MAX_MD_SIZE];
int md_len, i;
OpenSSL_add_all_digests();
/*********************************************************************/
char *algorithm = "md5";
char *msg = "0123456789abcdef";
md = EVP_get_digestbyname(algorithm);
if(!md) {
fprintf(stderr, "Unknown message digest %s\n", algorithm);
exit(1);
}
EVP_MD_CTX_init(&mdctx);
EVP_DigestInit_ex(&mdctx, md, NULL);
EVP_DigestUpdate(&mdctx, msg, strlen(msg));
EVP_DigestFinal_ex(&mdctx, md_value, &md_len);
EVP_MD_CTX_cleanup(&mdctx);
for(i = 0; i < md_len; i++) {
fprintf(stdout, "%02x", md_value[i]);
}
fprintf(stdout, "\n");
return 0;
}
编译 && 执行:
$ gcc -o main main.c -lssl
$ ./main
4032af8d61035123906e58e067140cc5
PS:摘要散列值以十六进制小写字符串表示。
可以将 algorithm 改为sha256、sha224、sha512 等值以实现不同摘要算法。
Case 2:sha224
更新:
char *algorithm = "sha224";
编译 && 执行:
$ gcc -o main main.c -lssl
$ ./main
7330215f6741fd2bacbd3658681a70f65e2e90a02887989018974ce8
关于 openssl 的两个函数:
int EVP_DigestUpdate(EVP_MD_CTX *ctx, const void *d, size_t cnt);
EVP_DigestUpdate() hashes cnt bytes of data at d into the digest context ctx.
This function can be called several times on the same ctx to hash additional data.
int EVP_DigestFinal_ex(EVP_MD_CTX *ctx, unsigned char *md, unsigned int *s);
EVP_DigestFinal_ex() retrieves the digest value from ctx and places it in md.
If the s parameter is not NULL then the number of bytes of data written (i.e. the length of the digest) will be written to the integer at s, at most EVP_MAX_MD_SIZE bytes will be written.
After calling EVP_DigestFinal_ex() no additional calls to EVP_DigestUpdate() can be made, but EVP_DigestInit_ex() can be called to initialize a new digest operation.
我们可以多次调用 EVP_DigestUpdate() 来 append 待摘要的数据到末尾,并通过调用 EVP_DigestFinal_ex 获取最后结果。
例如:
#include
#include
#include
int main(int argc, char *argv[])
{
EVP_MD_CTX mdctx;
const EVP_MD *md;
unsigned char md_value[EVP_MAX_MD_SIZE];
int md_len, i;
OpenSSL_add_all_digests();
/*********************************************************************/
char *algorithm = "md5";
char *msg1 = "0123";
char *msg2 = "4567";
char *msg3 = "89ab";
char *msg4 = "cdef";
md = EVP_get_digestbyname(algorithm);
if(!md) {
fprintf(stderr, "Unknown message digest %s\n", algorithm);
exit(1);
}
EVP_MD_CTX_init(&mdctx);
EVP_DigestInit_ex(&mdctx, md, NULL);
EVP_DigestUpdate(&mdctx, msg1, strlen(msg1));
EVP_DigestUpdate(&mdctx, msg2, strlen(msg2));
EVP_DigestUpdate(&mdctx, msg3, strlen(msg3));
EVP_DigestUpdate(&mdctx, msg4, strlen(msg4));
EVP_DigestFinal_ex(&mdctx, md_value, &md_len);
EVP_MD_CTX_cleanup(&mdctx);
for(i = 0; i < md_len; i++) {
fprintf(stdout, "%02x", md_value[i]);
}
fprintf(stdout, "\n");
return 0;
}
等价于上面的一次性传入完成的待摘要的数据。
编译 && 执行:
$ gcc -o main main.c -lssl
$ ./main
4032af8d61035123906e58e067140cc5
上面的示例代码其实并不严谨,使用了 strlen 去对待摘要数据计算长度,这里限制了待摘要数据一定不能包含 0x00 字节。
实际上,待摘要数据并无此限制,待摘要数据可以包含任意数据(包括 0x00),并由最后的长度参数指明数据长度。
待摘要数据是任意的二进制数据,可读的字符串只是其中一种,还有不可读的图片、音频、文件等形式。使用 strlen 将有问题。