RSA算法理论学习解惑――复制粘贴RSA私钥导致tengine出错深入解析

本文涉及的产品
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: tengine的代码中使用了RSA_check_key函数进行RSA私钥格式正确性检查,有一次加载私钥测试时tengine reload失败。案例的看点是RSA格式私钥文件中的私钥指数d在tengine实际的加解密计算过程中并没有用到,至于为什么请细看下文。

原创文章:来自RSA算法理论学习解惑――复制粘贴RSA私钥导致tengine出错深入解析

tengine的代码中使用了RSA_check_key函数进行RSA私钥格式正确性检查,有一次加载私钥测试时tengine reload失败。案例的看点是RSA格式私钥文件中的私钥指数d在tengine实际的加解密计算过程中并没有用到,至于为什么请细看下文。

问题背景

在一次配置tengine https服务使用的证书和私钥操作时采用了从原文件复制粘贴的方式,当使用tengine启动服务时提示出错:

$tengine -c tengine.conf –t

nginx: [emerg] RSA private key broken: /xxxx/4ed20dc594d0d75926f517d2b29879e2
140319033337512:error:0407B07B:rsa routines:RSA_check_key:d e not congruent to 1:rsa_chk.c:175:
140319033337512:error:0407B07C:rsa routines:RSA_check_key:dmp1 not congruent to d:rsa_chk.c:194:
140319033337512:error:0407B07D:rsa routines:RSA_check_key:dmq1 not congruent to d:rsa_chk.c:212:

关键性提示背后的含义未知。
d e not congruent to 1
dmp1 not congruent to d
dmq1 not congruent to d

原因分析

排查过程中疑问如下:

  1. 从上述提示可以得知rsa_chk.c的行数,从而找到调用来源是tengine中RSA_check_key函数,
    if (RSA_check_key(pkey) != 1) {
        ngx_log_error(NGX_LOG_EMERG, ctx->log, 0,
                      "RSA private key broken: %V", name);
        ERR_print_errors_fp(stderr);
        RSA_free(pkey);
        ret = NGX_ABORT;
        goto out;
    }
  1. 排查此用户的证书与私钥的正确性,首先查看私钥的格式与输出:
  2. rsa -in 111.pem –text 测试正常没有错误
$openssl s_server -accept 9999 -cert cert.crt -key 111.pem 
Using default temp DH parameters
Using default temp ECDH parameters

然后用curl访问https://127.0.0.1:9999端口后, 上述openssl s_server服务打印输出如下:

ACCEPT

GET / HTTP/1.1
User-Agent: curl/7.29.0
Host: localhost:9999
Accept: */*

即ssl握手过程中用到证书与私钥能验证通过!应该能说明证书与私钥确实是配对的。这不可能太奇怪了?!
最后用openssl rsa -in 111.pem –check 才发现有问题。到底是什么原因tengine 判断私钥有问题?本着刨根问底的精神,联系了做openssl的几个同事,暂时也没有人对这块有深入的研究。只能自己动手分析了。

首先查openssl中出错时的代码块

/* d*e = 1  mod lcm(p-1,q-1)? */
173     if (!BN_is_one(i)) {
174         ret = 0;
175         RSAerr(RSA_F_RSA_CHECK_KEY, RSA_R_D_E_NOT_CONGRUENT_TO_1);
176     }

使用d和q参数计算 dmq1,然后与私钥文件中解出的dmq1比对查看是否正确.

197         /* dmq1 = d mod (q-1)? */
198         r = BN_sub(i, key->q, BN_value_one());
199         if (!r) {
200             ret = -1;
201             goto err;
202         }
203 
204         r = BN_mod(j, key->d, i, ctx);
205         if (!r) {
206             ret = -1;
207             goto err;
208         }
209 
210         if (BN_cmp(j, key->dmq1) != 0) {
211             ret = 0;
212             RSAerr(RSA_F_RSA_CHECK_KEY, RSA_R_DMQ1_NOT_CONGRUENT_TO_D);
213         }

细节分析

看来必须搞清楚这几个参数的含义,但要搞清楚这几个参数的作用需要了解rsa加解密的原理,建议先读“RSA算法原理(二)”
http://www.ruanyifeng.com/blog/2013/07/rsa_algorithm_part_two.html

这几个参数在openssl中具体定义是

struct crypto_rsa_key {
    int private_key; /* whether private key is set */
    struct bignum *n; /* modulus (p * q) */
    struct bignum *e; /* public exponent */
    /* The following parameters are available only if private_key is set */
    struct bignum *d; /* private exponent */
    struct bignum *p; /* prime p (factor of n) */
    struct bignum *q; /* prime q (factor of n) */
    struct bignum *dmp1; /* d mod (p - 1); CRT exponent */
    struct bignum *dmq1; /* d mod (q - 1); CRT exponent */
    struct bignum *iqmp; /* 1 / q mod p; CRT coefficient */
};

我以个人理解简单点来解释:公钥可以用n和e代表,私钥可以用n和d代表;且n=p*q算出,e和d需要满足 ed ≡ 1 (mod φ(n)),其中φ(n)代表n的欧拉函数;私钥文件中有了这几个参数完全可以实现用来私钥解密和签名等功能了。以m代表明文,c代表密文,所谓"加密"过程,就是算出下式的c:
me ≡ c (mod n)
所谓解密就是c的d次方除以n的余数为m:
cd ≡ m (mod n)

但是直接用n d大数来直接使用存在效率不高的问题,然后就有数学大牛们引入了新的算法-中国余数定理,用于解决效率的问题(本文简称为RSA-CRT算法)。解密和签名的过程就改为了 

https://w1.fi/cgit/hostap/plain/src/tls/rsa.c
/*
* Decrypt (or sign) using Chinese remainer theorem to speed
* up calculation. This is equivalent to tmp = tmp^d mod n
* (which would require more CPU to calculate directly).
*
* dmp1 = (1/e) mod (p-1)
* dmq1 = (1/e) mod (q-1)
* iqmp = (1/q) mod p, where p > q
* m1 = c^dmp1 mod p
* m2 = c^dmq1 mod q
* h = q^-1 (m1 - m2) mod p
* m = m2 + hq
*/

即RSA-CRT算法只需要5个元素就可以完成模幂运算,不需要用到d.现在也可以清楚了上述crypto_rsa_key结构中最后参数的含义了,即用于RSA-CRT计算用的,且dmp1 dmq1也可以通过d计算得到。

一个RSA私钥文件中的内容解析如下:

$openssl rsa -in serverkey.pem -text
Private-Key: (2048 bit)
modulus:
    00:e3:b7:cb:15:a0:92:a2:0f:10:25:a4:cd:a8:2f:
    24:95:d2:65:e1:3f:cf:4d:87:64:52:f8:d9:f9:dc:
    …………

publicExponent: 65537 (0x10001)
privateExponent:
    6b:39:60:c4:07:3e:e4:56:29:69:40:47:a2:38:c8:
    86:4f:72:af:74:87:5d:5f:32:2b:2b:88:1f:f2:17:
    ……。

prime1:
    00:ff:6a:2f:e3:fb:6c:3c:65:e9:03:0e:0e:8f:4b:
    65:9b:26:8d:22:39:07:26:e5:ca:cc:b2:79:05:4e:
   …………
prime2:
    00:e4:3d:5c:57:35:26:39:18:ab:ba:c4:91:45:cd:
    77:9a:f9:93:75:12:3b:50:d7:53:0b:ee:17:57:70:
    …………
exponent1:
    00:c9:f5:c0:0a:88:6a:ec:53:34:ed:6a:77:0e:cd:
    72:79:3d:01:8a:17:07:d5:b5:0c:27:d1:d3:a9:e3:
    …………
exponent2:
    69:42:23:23:d4:cf:1b:e5:d4:cc:fd:7a:41:c6:d0:
    32:18:87:78:a6:3f:d4:b8:79:04:37:79:6c:49:d0:
    …………
coefficient:
    00:c0:7a:72:d3:fe:81:de:de:3d:21:21:cc:c2:20:
    a0:0e:2e:d2:91:1f:af:b3:89:a2:12:50:88:2c:c6:
    …………
writing RSA key

从上可以看出所有的参数都包含在私钥文件中。

现在可以理解RSA_check_key(pkey) 函数为什么出错了:即拿到私钥文件中dmp1,dmq1,d用公式计算他们的关系发现结果不一致所以报错了。
与原文件正确的私钥经过对比发现有一个字符出错,下图中101行代表原始的pem格式私钥数据C13改为了C12,第32行是经过转换后的数据,
0610_1

查32行得知属于privateExponent部分,即属于私钥元素d

0610_2

到此本该结束了,但还有一个疑问为什么nginx与 openssl s_server都没有出错,追了一下openssl代码
0610_3

rsa->meth->rsa_mod_exp()最后调用 RSA_eay_mod_exp()此函数实现解密没有用到d参数。
nginx与 openssl s_server都没有调用RSA_check_key函数,而tengine做加载私钥用到了RSA_check_key函数。
推断openssl rsa -in 111.pem –check应该也用到了此RSA_check_key函数

小结

主要是需要对RSA私钥文件中各参数的作用需要详细了解,由于需要数论原理很多,理解openssl代码难度还是很高的,搞了几个小时终于搞明白了原理。

目录
相关文章
|
24天前
|
机器学习/深度学习 人工智能 算法
深入解析图神经网络:Graph Transformer的算法基础与工程实践
Graph Transformer是一种结合了Transformer自注意力机制与图神经网络(GNNs)特点的神经网络模型,专为处理图结构数据而设计。它通过改进的数据表示方法、自注意力机制、拉普拉斯位置编码、消息传递与聚合机制等核心技术,实现了对图中节点间关系信息的高效处理及长程依赖关系的捕捉,显著提升了图相关任务的性能。本文详细解析了Graph Transformer的技术原理、实现细节及应用场景,并通过图书推荐系统的实例,展示了其在实际问题解决中的强大能力。
126 30
|
3天前
|
存储 监控 算法
企业内网监控系统中基于哈希表的 C# 算法解析
在企业内网监控系统中,哈希表作为一种高效的数据结构,能够快速处理大量网络连接和用户操作记录,确保网络安全与效率。通过C#代码示例展示了如何使用哈希表存储和管理用户的登录时间、访问IP及操作行为等信息,实现快速的查找、插入和删除操作。哈希表的应用显著提升了系统的实时性和准确性,尽管存在哈希冲突等问题,但通过合理设计哈希函数和冲突解决策略,可以确保系统稳定运行,为企业提供有力的安全保障。
|
28天前
|
存储 算法
深入解析PID控制算法:从理论到实践的完整指南
前言 大家好,今天我们介绍一下经典控制理论中的PID控制算法,并着重讲解该算法的编码实现,为实现后续的倒立摆样例内容做准备。 众所周知,掌握了 PID ,就相当于进入了控制工程的大门,也能为更高阶的控制理论学习打下基础。 在很多的自动化控制领域。都会遇到PID控制算法,这种算法具有很好的控制模式,可以让系统具有很好的鲁棒性。 基本介绍 PID 深入理解 (1)闭环控制系统:讲解 PID 之前,我们先解释什么是闭环控制系统。简单说就是一个有输入有输出的系统,输入能影响输出。一般情况下,人们也称输出为反馈,因此也叫闭环反馈控制系统。比如恒温水池,输入就是加热功率,输出就是水温度;比如冷库,
227 15
|
1月前
|
存储 算法 安全
2024重生之回溯数据结构与算法系列学习之串(12)【无论是王道考研人还是IKUN都能包会的;不然别给我家鸽鸽丟脸好嘛?】
数据结构与算法系列学习之串的定义和基本操作、串的储存结构、基本操作的实现、朴素模式匹配算法、KMP算法等代码举例及图解说明;【含常见的报错问题及其对应的解决方法】你个小黑子;这都学不会;能不能不要给我家鸽鸽丢脸啊~除了会黑我家鸽鸽还会干嘛?!!!
2024重生之回溯数据结构与算法系列学习之串(12)【无论是王道考研人还是IKUN都能包会的;不然别给我家鸽鸽丟脸好嘛?】
|
1月前
|
算法 Linux 定位技术
Linux内核中的进程调度算法解析####
【10月更文挑战第29天】 本文深入剖析了Linux操作系统的心脏——内核中至关重要的组成部分之一,即进程调度机制。不同于传统的摘要概述,我们将通过一段引人入胜的故事线来揭开进程调度算法的神秘面纱,展现其背后的精妙设计与复杂逻辑,让读者仿佛跟随一位虚拟的“进程侦探”,一步步探索Linux如何高效、公平地管理众多进程,确保系统资源的最优分配与利用。 ####
74 4
|
1月前
|
机器学习/深度学习 人工智能 自然语言处理
【EMNLP2024】基于多轮课程学习的大语言模型蒸馏算法 TAPIR
阿里云人工智能平台 PAI 与复旦大学王鹏教授团队合作,在自然语言处理顶级会议 EMNLP 2024 上发表论文《Distilling Instruction-following Abilities of Large Language Models with Task-aware Curriculum Planning》。
|
1月前
|
缓存 负载均衡 算法
Linux内核中的进程调度算法解析####
本文深入探讨了Linux操作系统核心组件之一——进程调度器,着重分析了其采用的CFS(完全公平调度器)算法。不同于传统摘要对研究背景、方法、结果和结论的概述,本文摘要将直接揭示CFS算法的核心优势及其在现代多核处理器环境下如何实现高效、公平的资源分配,同时简要提及该算法如何优化系统响应时间和吞吐量,为读者快速构建对Linux进程调度机制的认知框架。 ####
|
1月前
|
算法 安全 搜索推荐
2024重生之回溯数据结构与算法系列学习(8)【无论是王道考研人还是IKUN都能包会的;不然别给我家鸽鸽丢脸好嘛?】
数据结构王道第2.3章之IKUN和I原达人之数据结构与算法系列学习x单双链表精题详解、数据结构、C++、排序算法、java、动态规划你个小黑子;这都学不会;能不能不要给我家鸽鸽丢脸啊~除了会黑我家鸽鸽还会干嘛?!!!
|
1月前
|
算法 安全 搜索推荐
2024重生之回溯数据结构与算法系列学习之单双链表精题详解(9)【无论是王道考研人还是IKUN都能包会的;不然别给我家鸽鸽丢脸好嘛?】
数据结构王道第2.3章之IKUN和I原达人之数据结构与算法系列学习x单双链表精题详解、数据结构、C++、排序算法、java、动态规划你个小黑子;这都学不会;能不能不要给我家鸽鸽丢脸啊~除了会黑我家鸽鸽还会干嘛?!!!
|
1月前
|
算法 安全 NoSQL
2024重生之回溯数据结构与算法系列学习之栈和队列精题汇总(10)【无论是王道考研人还是IKUN都能包会的;不然别给我家鸽鸽丢脸好嘛?】
数据结构王道第3章之IKUN和I原达人之数据结构与算法系列学习栈与队列精题详解、数据结构、C++、排序算法、java、动态规划你个小黑子;这都学不会;能不能不要给我家鸽鸽丢脸啊~除了会黑我家鸽鸽还会干嘛?!!!

推荐镜像

更多