openssl签名报错

在调用RSA_private_encrypt函数时遇到如下报错。

复制代码
0:error:0D07803A:asn1 encoding routines:asn1_item_embed_d2i:nested asn1 error:crypto/asn1/tasn_dec.c:309:Type=X509
0:error:2406C06E:random number generator:RAND_DRBG_instantiate:error retrieving entropy:crypto/rand/drbg_lib.c:335:
0:error:2406B072:random number generator:RAND_DRBG_generate:in error state:crypto/rand/drbg_lib.c:588:
0:error:2406C06E:random number generator:RAND_DRBG_instantiate:error retrieving entropy:crypto/rand/drbg_lib.c:335:
0:error:2406B072:random number generator:RAND_DRBG_generate:in error state:crypto/rand/drbg_lib.c:588:
0:error:04088003:rsa routines:RSA_setup_blinding:BN lib:crypto/rsa/rsa_crpt.c:155:
0:error:04066044:rsa routines:rsa_ossl_private_encrypt:internal error:crypto/rsa/rsa_ossl.c:297:

openssl库版本为1.1.1Q。

复制代码
$ openssl version
OpenSSL 1.1.1q 

查看openssl源码,随机数生成器(DRBG)在获取熵时遇到了错误,错误码RAND_R_ERROR_RETRIEVING_ENTROPY。

复制代码
int RAND_DRBG_instantiate(RAND_DRBG *drbg,
                          const unsigned char *pers, size_t perslen)
{
    if (drbg->get_entropy != NULL)
        entropylen = drbg->get_entropy(drbg, &entropy, min_entropy,   min_entropylen, max_entropylen, 0);
    if (entropylen < min_entropylen || entropylen > max_entropylen) {
        RANDerr(RAND_F_RAND_DRBG_INSTANTIATE, RAND_R_ERROR_RETRIEVING_ENTROPY);
        goto end;
    }

将程序与openssl库做静态链接,放到其它机器上运行一切正常,排除了openssl库的问题,应当是系统问题。

复制代码
# gcc rsasign.c -L/lib/x86_64-linux-gnu/ libssl.a  libcrypto.a -ldl -lpthread 

使用strace调试,发现问题出在getrandom函数,出错的机器上提示函数未实现。

复制代码
$ strace ./rsasign
...
prlimit64(0, RLIMIT_STACK, NULL, {rlim_cur=8192*1024, rlim_max=RLIM64_INFINITY}) = 0
getrandom(0x7f0366004398, 8, GRND_NONBLOCK) = -1 ENOSYS (Function not implemented)
brk(NULL)                               = 0x7f036633b000
brk(0x7f036635c000)                     = 0x7f036635c000

单独写一个getrandom.c测试程序:

复制代码
#include<stdio.h>
#include <errno.h>
#include <string.h>
#define _GNU_SOURCE        /* See feature_test_macros(7) */
#include <unistd.h>
#include <sys/syscall.h>   /* For SYS_xxx definitions */
#define __NR_getrandom 318

int main(int argc, void *argv[])
{
    unsigned char buf[32];
    ssize_t result = syscall(__NR_getrandom, buf, sizeof(buf), 0);

    if (result == -1) {
        printf("getrandom failed:%s\n", strerror(errno));
        return 1;
    }
    for (int i = 0; i < sizeof(buf); i++)
        printf("%02x ", buf[i]);
    printf("\n");
    return 0;
}

运行测试程序,同样有如下报错:

复制代码
$ gcc getrandom.c -o rand
$ ./rand
getrandom failed:Function not implemented

其它正常运行的设备上,如下信息,可以正常获取到随机数:

复制代码
$ ./rand
32 69 78 58 4b c8 8b a4 3d c5 11 95 b6 de 00 29 4f 6f 9e 02 0b 66 28 78 f9 70 e4 53 62 1b 64 e6 

openssl文件crypto/rand/rand_unix.c中函数syscall_random代码如下,linux内核在3.17版本之后开始支持getrandom系统调用。出问题的系统内核为3.10,还不能支持getrandom。

复制代码
/* syscall_random(): Try to get random data using a system call
 * returns the number of bytes returned in buf, or < 0 on error.
 */
static ssize_t syscall_random(void *buf, size_t buflen)
{
    /* Linux supports this since version 3.17 */
#  if defined(__linux) && defined(__NR_getrandom)
    return syscall(__NR_getrandom, buf, buflen, 0);

另外,对于linux系统,随机数还可以从以下设备文件中获取。

复制代码
#  define DEVRANDOM "/dev/urandom", "/dev/random", "/dev/hwrng", "/dev/srandom"

在由设备文件读取随机数之前,函数wait_random_seeded等待设备有足够的种子(seed)。

复制代码
size_t rand_pool_acquire_entropy(RAND_POOL *pool)
{
#   if defined(OPENSSL_RAND_SEED_DEVRANDOM)
    if (wait_random_seeded()) {
        size_t bytes_needed;
        unsigned char *buffer;
        
        bytes_needed = rand_pool_bytes_needed(pool, 1 /*entropy_factor*/);
        for (i = 0; bytes_needed > 0 && i < OSSL_NELEM(random_device_paths); i++) {
            ssize_t bytes = 0; 
            int attempts = 3;    /* Maximum number of consecutive unsuccessful attempts */
            const int fd = get_random_device(i);
            if (fd == -1) continue;
            
            while (bytes_needed != 0 && attempts-- > 0) {
                buffer = rand_pool_add_begin(pool, bytes_needed);
                bytes = read(fd, buffer, bytes_needed);

从内核4.8(DEVRANDOM_SAFE_KERNEL)开始,当/dev/random(DEVRANDOM_WAIT)可读时,不确保设备/dev/urandom被正确的Seed。这里使用uname获取系统的内核版本,如果版本号大于4.8,返回0。

否则,检查/dev/random是否可读,来判断/dev/urandom是否正确的Seed。

复制代码
static int wait_random_seeded(void)
{
    static int seeded = OPENSSL_RAND_SEED_DEVRANDOM_SHM_ID < 0;
    static const int kernel_version[] = { DEVRANDOM_SAFE_KERNEL };

    if (!seeded) {     /* See if anything has created the global seeded indication */
        if ((shm_id = shmget(OPENSSL_RAND_SEED_DEVRANDOM_SHM_ID, 1, 0)) == -1) {
            /*
             * Check the kernel's version and fail if it is too recent.
             * Linux kernels from 4.8 onwards do not guarantee that
             * /dev/urandom is properly seeded when /dev/random becomes
             * readable.  However, such kernels support the getentropy(2)
             * system call and this should always succeed which renders
             * this alternative but essentially identical source moot.
             */
            if (uname(&un) == 0) {
                kernel[0] = atoi(un.release);
                p = strchr(un.release, '.');
                kernel[1] = p == NULL ? 0 : atoi(p + 1);
                if (kernel[0] > kernel_version[0] || (kernel[0] == kernel_version[0] && kernel[1] >= kernel_version[1]))
                    return 0;
            }
            /* Open /dev/random and wait for it to be readable */
            if ((fd = open(DEVRANDOM_WAIT, O_RDONLY)) != -1) {
                if (DEVRANDM_WAIT_USE_SELECT && fd < FD_SETSIZE) {
                    FD_ZERO(&fds);
                    FD_SET(fd, &fds);
                    while ((r = select(fd + 1, &fds, NULL, NULL, NULL)) < 0 && errno == EINTR);
                } else {
                    while ((r = read(fd, &c, 1)) < 0 && errno == EINTR);

出现问题的系统使用的内核为3.10,但是uname系统调用获取到的内核版本为5.16,导致了问题的产生。修复uname的问题,获取内核版本号为实际的版本号3.10之后,问题得到解决。

命令strace跟踪,不再调用getrandom,而是使用了urandom来获取随机数。

复制代码
$ strace ./rsasign
open("/dev/urandom", O_RDONLY|O_NOCTTY|O_NONBLOCK) = 3
相关推荐
封奚泽优6 天前
使用Python制造扫雷游戏
开发语言·python·游戏·pygame·扫雷·random
封奚泽优11 天前
使用Python实现单词记忆软件
开发语言·python·random·qpushbutton·qtwidgets·qtcore·qtgui
赴33511 天前
Numpy 库 矩阵数学运算,点积,文件读取和保存等
人工智能·算法·numpy·random·dot
沉在嵌入式的鱼16 天前
RK3588移植Openssl库
linux·rk3588·openssl
黑屋里的马19 天前
ssl相关命令生成证书
服务器·网络·ssl·openssl·gmssl
山烛24 天前
小白学Python,标准库篇——随机库、正则表达式库
开发语言·python·正则表达式·random·re·随机库·正则表达式库
fangeqin1 个月前
ubuntu源码安装python3.13遇到Could not build the ssl module!解决方法
linux·python·ubuntu·openssl
API开发1 个月前
苹果芯片macOS安装版Homebrew(亲测) ,一键安装node、python、vscode等,比绿色软件还干净、无污染
vscode·python·docker·nodejs·openssl·brew·homebrew
码农不惑2 个月前
Rust使用tokio(二)HTTPS相关
https·rust·web·openssl
liulilittle2 个月前
通过高级处理器硬件指令集AES-NI实现AES-256-CFB算法并通过OPENSSL加密验证算法正确性。
linux·服务器·c++·算法·安全·加密·openssl