这篇文章详细讲述一下RSA与证书的相关内容。内容有点多,但都是干货。
一、RSA算法
1.1简介
RSA算法是由美国三位科学家Rivest、Shamir和Adleman于1976年提出并在1978年正式发表的公开密码算法,其命名取自三位创始人名字的首字母缩写。该算法基于数论中的大数分解难题,即:根据数论,寻求两个大素数比较简单,而将它们的乘积分解开则极其困难。
1.2算法说明
该算法中,用户有两个密钥:公钥 PK={e,n}和私钥 SK={d,n},n 为两个大素数 p和q的乘积(素数p和q一般为1 0 0位以上的十进制数),e和d满足一定的关系,如只知道e和n并不能求出d。
(1)加密/解密过程
若用整数X表示明文,用整数Y表示密文(X和Y均小于n),则加密和解密运算为:
(2)密钥的产生
① 计算n。用户秘密选择两个大素数p和q,计算出n=pq。n称为RSA算法的模数。明文必须能够用小于n的数来表示。
RSA密钥长度指的是模数的位数,如2048位RSA密钥指的是模数为2048比特的RSA密钥对,常规选值为:1024、2048、4096等。
密文长度就是对给定符合条件的明文加密出来的结果位长,这个是可以确定的,加密后的密文长度跟密钥长度是相同的,也就是模数的长度。
② 计算φ(n)。用户计算出n的欧拉函数φ(n)=(p-1)(q-1)。φ(n)定义为不超过n并与n互素的数的个数。
③ 选择e。用户从[0,φ(n)-1]中选择一个与φ(n)互素的数e作为公开的加密指数。
④ 计算d。用户计算出满足公式ed=1 modφ(n)的d作为解密指数。
⑤ 得出所需要的公钥和私钥:
1.3实际运算
1.3.1公私钥值
实际走一下计算过程
1.随机选择两个不相等的质数p和q,p = 61,q = 53
2.计算p和q的乘积n,n = 61×53 = 3233
3.计算n的欧拉函数φ(n)(这儿用到了欧拉定理)
φ(n) = (p1-1)(p2-1)
φ(3233) = 60x52 = 3120
4.随机选择一个整数e,条件是1< e < φ(n),且e与φ(n) 互质 ,假设选择了17
5.计算出d,使得ed % φ(n)=(17*2753)%3120 = 1(如何解出d需要用到辗转相除法),得到d为2753
6.形成公钥(e, n) =(17, 3223) ,私钥 (d, n) = (2753, 3223)
1 | //计算欧拉函数值 |
1.3.2加解密
用公钥加密,假设选择A,ASCII为65,即m=65
加密过程
m^e=c(modn)
即 65 ^ 17=c(mod 3233)
得到c = 2790
解密过程
c^d=m(modn)
即2790 ^ 2753 = m(mod 3233)
得到m = 65
二、OpenSSL
openssl是一个安全套接字层密码库,囊括主要的密码算法、常用密钥、证书封装管理功能及实现ssl协议。openssl有三类命令:标准命令,消息摘要(dgst子命令),加密命令(enc子命令)
1 | $openssl |
2.1创建私钥
1 | openssl genrsa -out rsa_private.key 2048 |
通过该命令创建了pem编码的私钥
1 | -----BEGIN RSA PRIVATE KEY----- |
2.2获取公钥
Rsa命令专门管理RSA密钥,从私钥中提取公钥等。
1 | openssl rsa -in rsa_private.key -pubout -out rsa_public.pem |
该命令从私钥文件中提取出公钥,默认也是pem编码,可以通过 -outform 参数,指定想导出的编码格式,如 DER、PEM、PVK
1 | -----BEGIN PUBLIC KEY----- |
2.3生成证书
根据私钥,生成对应的X509证书
1 | openssl req -sha512 -new -x509 -key rsa_private.key -out rsatest.crt -days 3650 -subj /CN=jasonpang.com |
2.4证书格式转换
证书从pem转换为der
1 | openssl x509 -inform PEM -outform DER -text -in rsatest.crt -out rsatest.der |
证书从pem转换为p7b
1 | openssl crl2pkcs7 -nocrl -certfile rsatest.crt -out rsatest.p7b |
2.5查看证书
1 | 查看pem证书 |
三、公私钥格式说明
根据RSA算法描述,我们知道公私钥需要有哪些核心数据。
那公私钥在文件中的格式是怎样的呢?
3.1公钥格式
公钥的ASN.1格式
命令:openssl rsa -pubin -in rsa_public.key -noout -text
1 | RSA Public-Key: (2048 bit) |
3.2私钥格式
私钥的ASN.1格式
命令:openssl rsa -in rsa_private.key -noout -text
1 | RSA Private-Key: (2048 bit, 2 primes) |
3.3说明
1.e的值为65537原因
- NIST SP800-78 Rev 1 (2007) 曾强调“不允许使用比65537更低的公钥指数e”,但PKCS#1却从未有过类似的建议。e=65537=(2^16+1),其签名/加密运算只需要17次模乘,性能也算不错。但我认为选这个值的目的只是一个介于小指数攻击和运算效率之间的一个折中考虑,即以防万一将来有一天”e=3”被攻破而侥幸”e=65537”可能还是安全的。
2.公钥需要包含e、n,私钥需要d、n
- 私钥文件里面包含n、e、d、p、q等,所以可以从私钥文件获取到公钥
- 如果有n、e、d、p、q,也可自行制作出公私钥文件
- 公钥、私钥不是只有一个数值,而是由多个数值的组合而成
3.https://www.gmssl.cn/gmssl/index.jsp 可查看公私钥文件里的数值
四、数字证书
4.1证书格式
X.509数字证书用ASN.1描述如下:
1 | Certificate ::= SEQUENCE { |
4.2查看证书
4.2.1细节
我们可以使用mac的钥匙串查看openssl生成的证书信息
4.2.2说明
1.公共秘钥与指数:大家可以看到,这两个信息,正是公钥文件里的n和e
2.指纹:
纹指纹不是证书的签名值, 和证书本身生成的字段无关, 并不包含在证书中. 因为你的任何改动都会影响最终输出的DER或PEM文件, 所以只能在证书生成之后进行指纹生成.
计算:要计算指纹,首先需要将其从PEM表示解码为二进制。为此,需要删除页眉和页脚(从
-----
开始),其余部分需要解码为base64。从得到的二进制文件中,可以计算SHA1或SHA-256散列。
1 | grep -v ^- rsatest.crt | base64 -d | sha256sum |
结果为c455ce81fd0ce98eb70bc59f10f099f4ca9cd04ccd0eda4cce29ead2fcb72323,和图片信息一致
3.签名
签名过程:CA机构拥有非对称加密的私钥和公钥。CA机构对证书明文数据T进行hash。对hash后的值用私钥加密,得到数字签名S。明文和数字签名共同组成了数字证书。
验证过程:拿到证书,得到明文T,签名S。用CA机构的公钥对S解密(由于是浏览器信任的机构,所以浏览器保有它的公钥。详情见下文),得到S’。用证书里指明的hash算法对明文T进行hash得到T’。显然通过以上步骤,T’应当等于S‘,除非明文或签名被篡改。所以此时比较S’是否等于T’,等于则表明证书可信。
明文数据T:TBSCertificate结构的der编码是计算签名的数据
这个证书是根证书,所以自己给自己进行签名
五、证书编码
大家是否被众多的证书格式搞懵了?让我们看一下证书和私钥都以哪些形式进行保存!图片来自 https://www.processon.com/view/link/63c8f6ffc12afe0cada9b596
我们找几个常用的编码格式,看看它们的真实样子。
5.1pem编码
PEM是把二进制数据通过Base64进行编码,然后在头部添加header —–BEGIN XXX—– 尾部添加footer —–END XXX—–。 header 和footer之间的数据就是被Base64编码的二进制数据。XXX 是要编码的数据内容类型,可以是 CERTIFICATE 、CERTIFICATE REQUEST、 PRIVATE KEY 、X509 CRL 。
上述的公钥、私钥、证书都是pem编码,可以看到都带有—–BEGIN XXX—–。
Base64是一种很常用的编码方式,可以将任意二进制字节编码成64个可打印的ASCII码字符。Base64编码的基本原理如下。
1.字符索引
64个字符包括大小写英文字母各26个、10个数字、加号和左斜杠。等号用于末尾填充。64个字符所对应的索引如表所示。
2.编码过程
将二进制字节流按3个字节一组进行分组,然后分别对每个3字节组进行编码。当二进制字节流长度不是3的倍数时,最后一个3字节组可能不足3个字节,编码时需要进行填充处理。每个3字节组可编码成4个字符。
将3字节组中的每个字节按最高位到最低位顺序排列后组成24位,然后按6位一组分成4组,每组取值范围为0~63,将每组取值作为索引按照表5-7获得对应的字符后,最终形成4个字符。
如果最后的3字节组只有1个字节,则将形成8位,右边补足4位0比特后,可形成2个6位组,转换成2个字符,然后填充2个等号,凑成4个字符。如果最后的3字节组只有2个字节,将形成16位,右边补足2位0比特后,可形成3个6位组,转换成3个字符,然后填充1个等号,凑成4个字符。
Base64编码后的字符之间允许插入回车换行符,但每行不允许超过76个字个字符。
3.解码过程
将字符流中回车换行符删除后,按 4 个字符一组进行分组,然后分别对每个 4 字符组进行解码。若删除回车换行符后的字符流长度不是4的倍数,则该字符流有误。每个4字符组可解码成 3个字节。若最后一个 4字符组末尾是 1或 2个等号,将解码成 2或 1个字节。
将 4 字符组中每个字符按表 5-8 获得对应的索引,该索引取值范围为 0~63,将索引转换成6位比特后,按最高位到最低位顺序排列后组成24位比特;然后按8位一组分成3组,每组8位转换成1个字节,最终形成3个字节。
如果最后的4字符组末尾是1个等号,则前3个字符的索引将组合成18位,取前16位转换成2个字节。如果最后的4字符组末尾是2个等号,则前2个字符的索引将组合成12位,取前8位比特转换成1个字符。
5.2der编码
将X509证书从PEM格式转换为DER格式
1 | openssl x509 -inform PEM -outform DER -text -in rsatest.crt -out rsatest.der |
能够看到证书的信息,同时还有部分乱码
5.3p7b编码
1 | openssl crl2pkcs7 -nocrl -certfile rsatest.crt -out rsatest.p7b |
结果为:
1 | -----BEGIN PKCS7----- |
通过如下命令可查看p7b格式
1 | openssl pkcs7 -print -in rsatest.p7b |
六、总结
终于梳理完RSA和证书之间的关系。
RSA生成公私钥,其中公钥制成证书。公私钥、证书的格式、细节也都做了说明。希望对大家有所帮助。
这部分内容可以和这两篇文章一起看:
七、资料
- X509证书及其格式扩展名
- https://www.icode9.com/content-4-613610.html
- https://jamielinux.com/docs/openssl-certificate-authority/create-the-root-pair.html
- https://zh.m.wikipedia.org/zh-hans/X.509
- https://tech.bytedance.net/articles/7094078786773188639#doxcnAC0w2kWkwS8ui6Bajy5GUf
- 搞懂 PEM、ANS、PFX、P12、p8、CER、X509 等证书相关文件格式 后缀
- 国密实验室
- PHP实现DER密钥转PEM密钥
- pem 文件详解
- OpenSSL简介
- RSA 从私钥中获取公钥
- 素数/质数
- 欧拉函数
- 互质/互素
- RSA算法计算过程
- 欧拉函数及其计算
- C语言实现欧拉函数的计算
- 进制转换
- Java从RSA私钥中提取出 E N P Q D DP DQ QP
- RSA——长度问题
- RSA2048基础知识
- 使用OpenSSL从bash中的n,e,d,p,q值生成RSA私钥
- 为什么RSA 公钥指数(e=65537)
- 利用openssl生成X509证书
- RSA.SignHash SHA512 和 SHA256 给出相同的签名长度
- 如何正确计算证书的指纹
- 关于证书指纹
- 解释X509数字证书的证书签名值字段
- 数字签名、数字证书与CA
- 数字证书和数字签名
- 证书链的终极版
- 使用openssl进行证书格式转换
- openssl ans.1编码规则分析及证书密钥编码方式