OpenSSL之隨機(jī)數(shù)用法

隨機(jī)數(shù)是一種無規(guī)律的數(shù),但是真正做到完全無規(guī)律也較困難,所以一般將它稱之為偽隨機(jī)數(shù)。隨機(jī)數(shù)在密碼學(xué)用的很多,比如SSL握手中的客戶端hello和服務(wù)端hello消息中都有隨機(jī)數(shù);SSL握手中的預(yù)主密鑰是隨機(jī)數(shù);RSA密鑰生成也用到隨機(jī)數(shù)。如果隨機(jī)數(shù)有問題,會帶來很大的安全隱患。軟件生成隨機(jī)數(shù)一般預(yù)先設(shè)置隨機(jī)數(shù)種子,再生成隨機(jī)數(shù)。設(shè)置隨機(jī)數(shù)種子可以說是對生成隨機(jī)數(shù)過程的一種擾亂,讓產(chǎn)生的隨機(jī)數(shù)更加無規(guī)律可循。生成隨機(jī)數(shù)有多種方法,可以是某種算法也可以根據(jù)某種或多種隨機(jī)事件來生成。比如,鼠標(biāo)的位置、系統(tǒng)的當(dāng)前時(shí)間、本進(jìn)程/線程相關(guān)信息以及機(jī)器噪聲等。安全性高的應(yīng)用一般都采用硬件方式(隨機(jī)數(shù)發(fā)生器)來生成隨機(jī)數(shù)。

本文假設(shè)你已經(jīng)安裝好了OpenSSL,并且持有一份1.1.1的源碼。
隨機(jī)數(shù)相關(guān)的頭文件為rand.h、源文件在crypto/rand目錄中。

主要結(jié)構(gòu):

struct rand_meth_st {
    int (*seed) (const void *buf, int num);
    int (*bytes) (unsigned char *buf, int num);
    void (*cleanup) (void);
    int (*add) (const void *buf, int num, double randomness);
    int (*pseudorand) (unsigned char *buf, int num);
    int (*status) (void);
};
typedef struct rand_meth_st RAND_METHOD;

這個(gè)結(jié)構(gòu)定義了涉及隨機(jī)數(shù)生成的抽象方法集合。主要字段含義:
seed —— 隨機(jī)數(shù)種子函數(shù)。
bytes —— 隨機(jī)數(shù)生成函數(shù)。
cleanup —— 狀態(tài)清除函數(shù)。
add —— 隨機(jī)數(shù)種子添加函數(shù)。
pseudorand —— 可重現(xiàn)的隨機(jī)數(shù)函數(shù)。
status —— 狀態(tài)查詢函數(shù)。

在1.1.1中,大多數(shù)的數(shù)據(jù)結(jié)構(gòu)已經(jīng)不再向使用者開放,從封裝的角度來看,這是更合理的。如果你在頭文件中找不到結(jié)構(gòu)定義,不妨去源碼中搜一搜。

主要函數(shù):

int RAND_set_rand_method(const RAND_METHOD *meth);
設(shè)置自定義的隨機(jī)數(shù)抽象方法。
成功返回1,失敗返回0。

const RAND_METHOD *RAND_get_rand_method(void);
獲取當(dāng)前的隨機(jī)數(shù)抽象方法集合。
成功返回有效指針,失敗返回NULL。
在我們未調(diào)用RAND_set_rand_method()的情況下,該函數(shù)返回默認(rèn)的抽象方法集合。

RAND_METHOD *RAND_OpenSSL(void);
這個(gè)函數(shù)返回OpenSSL內(nèi)置的隨機(jī)數(shù),通常為RAND_DRBG隨機(jī)數(shù)。

void RAND_seed(const void *buf, int num);
種子函數(shù),為了讓openssl內(nèi)部維護(hù)的隨機(jī)數(shù)據(jù)更加無序,可使用本函數(shù)。buf為用戶輸入的隨機(jī)數(shù)地址,num為其字節(jié)數(shù)。Openssl將用戶提供的buf中的隨機(jī)內(nèi)容與其內(nèi)部隨機(jī)數(shù)據(jù)進(jìn)行摘要計(jì)算,更新其內(nèi)部隨機(jī)數(shù)據(jù)。

void RAND_add(const void *buf, int num, double randomness);
與seed類似,也是為了讓openssl內(nèi)部隨機(jī)數(shù)據(jù)更加無序,其中entropy(信息熵)可以看作用戶本次加入的隨機(jī)數(shù)的個(gè)數(shù)。從內(nèi)部實(shí)現(xiàn)來看,RAND_seed()相當(dāng)于調(diào)用RAND_add(buf, num, num),此時(shí)傳遞的entropy(信息熵)和緩沖區(qū)長度是一樣的。至于num和randomness這兩個(gè)參數(shù)如何設(shè)置,建議randomness不要超過num的長度,最好是兩者相同,或者直接調(diào)用RAND_seed(),盡量避免RAND_add()的調(diào)用。

int RAND_bytes(unsigned char *buf, int num);
生成隨機(jī)數(shù),openssl根據(jù)內(nèi)部維護(hù)的隨機(jī)數(shù)狀態(tài)來生成結(jié)果。buf用于存放生成的隨機(jī)數(shù)。num為輸入?yún)?shù),用來指明生成隨機(jī)數(shù)的字節(jié)長度。
成功返回1,失敗返回0。

int RAND_status(void);
查看熵值是否達(dá)到預(yù)定值,如果達(dá)到則返回1,否則返回0。
在openssl實(shí)現(xiàn)的md_rand中該函數(shù)會調(diào)用RAND_poll函數(shù)來使熵值合格。如果本函數(shù)返回0,則說明此時(shí)用戶不應(yīng)生成隨機(jī)數(shù),需要調(diào)用seed和add函數(shù)來添加熵值。
從1.1.1版本的使用情況來看,不需要調(diào)用RAND_seed(),RAND_status()總是返回成功的,但是建議使用者從安全考慮,雖然不需要理會RAND_status(),請?jiān)谡{(diào)用RAND_bytes()之前,總是使用RAND_seed()先初使化隨機(jī)種子。

const char *RAND_file_name(char *file, size_t num);
指定file緩沖區(qū)和num長度,生成隨機(jī)的文件路徑,如果num設(shè)置太小不足以容納完整路徑,則返回NULL,建議file緩沖區(qū)通常指定256字節(jié)。

int RAND_load_file(const char *file, long max_bytes);
將file指定的隨機(jī)數(shù)文件中的數(shù)據(jù)讀取bytes字節(jié)(如果bytes大于1024,則讀取1024字節(jié)),內(nèi)部調(diào)用RAND_add進(jìn)行計(jì)算,生成內(nèi)部隨機(jī)數(shù)。
成功返回加載的字節(jié)數(shù)(0表示文件為空),失敗返回-1。

int RAND_write_file(const char *file);
生成一個(gè)隨機(jī)數(shù)文件,返回生成的內(nèi)容大小,通常為1024字節(jié)。

使用舉例:

這個(gè)例子演示了隨機(jī)數(shù)的相關(guān)API操作。

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>

#include <openssl/rand.h>

namespace dakuang {}

int main(int argc, char* argv[])
{
    int nRet = RAND_status();
    printf("before RAND_seed() RAND_status() ret:[%d] \n", nRet);

    char sBuf[20] = {0};
    strcpy(sBuf, "1234567890");
    RAND_seed(sBuf, 10);

    nRet = RAND_status();
    printf("RAND_status() ret:[%d] \n", nRet);

    unsigned char sBufOut[20] = {0};
    nRet = RAND_bytes(sBufOut, 20);
    printf("RAND_bytes() ret:[%d] \n", nRet);
    for (int i = 0; i < 20; ++i)
    {
        printf("%02x", sBufOut[i]);
    }
    printf("\n");

    char sFileName[256] = {0};
    const char* p = RAND_file_name(sFileName, 256);
    printf("p:[%p - %s] sFile:[%p - %s] \n", p, p, sFileName, sFileName);

    int nBytesWrite = RAND_write_file(p);
    printf("byteswrite:[%d] \n", nBytesWrite);

    int nBytesRead = RAND_load_file(p, 512);
    printf("bytesread:[%d] \n", nBytesRead);

    return 0;
}

輸出:
before RAND_seed() RAND_status() ret:[1]
RAND_status() ret:[1]
RAND_bytes() ret:[1]
04196aa58505fdeef8c5bf9ef9d22e07a3d6859c
p:[0x7ffd028a9070 - /home/test/.rnd] sFile:[0x7ffd028a9070 - /home/test/.rnd]
byteswrite:[1024]
bytesread:[512]

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容