天天看點

摘要算法:使用 openssl 實作 md5、sha256 等

摘要算法:使用 openssl 實作 md5、sha256 等

相關連結:

摘要算法:MD5 及 Java實作樣例

摘要算法:SHA 及 Java 實作樣例

本文主要介紹如何使用 openssl 庫實作 md5、sha256 摘要算法。

Case 1:md5

#include <stdio.h>
#include <string.h>
#include <openssl/evp.h>

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();
    }

    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 = ; i < md_len; i++) {
        fprintf(stdout, "%02x", md_value[i]);
    }
    fprintf(stdout, "\n");

    return ;
}
           

編譯 && 執行:

$ gcc -o main main.c -lssl
$ ./main
af8d61035123906e58e067140cc5
           

PS:摘要散列值以十六進制小寫字元串表示。

可以将 algorithm 改為sha256、sha224、sha512 等值以實作不同摘要算法。

Case 2:sha224

更新:

char *algorithm = "sha224";
           

編譯 && 執行:

$ gcc -o main main.c -lssl
$ ./main
f6741fd2bacbd3658681a70f65e2e90a02887989018974ce8
           

關于 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 <stdio.h>
#include <string.h>
#include <openssl/evp.h>

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();
    }

    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 = ; i < md_len; i++) {
        fprintf(stdout, "%02x", md_value[i]);
    }
    fprintf(stdout, "\n");

    return ;
}
           

等價于上面的一次性傳入完成的待摘要的資料。

編譯 && 執行:

$ gcc -o main main.c -lssl
$ ./main
af8d61035123906e58e067140cc5
           

注意:

上面的示例代碼其實并不嚴謹,使用了 strlen 去對待摘要資料計算長度,這裡限制了待摘要資料一定不能包含 0x00 位元組。

實際上,待摘要資料并無此限制,待摘要資料可以包含任意資料(包括 0x00),并由最後的長度參數指明資料長度。

待摘要資料是任意的二進制資料,可讀的字元串隻是其中一種,還有不可讀的圖檔、音頻、檔案等形式。使用 strlen 将有問題。

參考連結:https://linux.die.net/man/3/evp_get_digestbyname

繼續閱讀