环境: ubuntu20.4 内核 5.10.10
RSA是最常见的非对称加密算法,RSA的原理就不做介绍了,主要介绍一下密钥格式,linux 进行密钥转换编程。
要学习RSA编码,首先需要理解RSA密钥的格式。
RSA有公钥,私钥之分,公钥私钥都使用相同的语法格式。
RSA的密钥有两种格式,PEM格式,DER格式
PEM格式
PEM格式包含几种报文头
"-----BEGIN PUBLIC KEY-----": PKCS#8格式公钥
"-----BEGIN PRIVATE KEY-----": PKCS#8格式私钥
"-----BEGIN RSA PUBLIC KEY-----": PKCS#1格式公钥
"-----BEGIN RSA PRIVATE KEY-----": PKCS#1格式私钥
常见的PEM格式如下
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDX2k9fV+TXr0sIw/oGI9v2g+Nm
Drf16cdfK45oPEyEGH0sqcYvvTTjD2iovHltOMCidgV2TH+S0bIb6JEoPaW7/+ya
tX3+MvHpCnOylaDH5aKNKoy/JyRn/cy9TXAk0QLAlKTCnfP1A3s5WqRDO2f6B70r
imffp9gfs/SVVhjy0wIDAQAB
-----END PUBLIC KEY-----
这种格式是linux下面最常见的文本存储格式,这种格式实际上是二进制的密钥经过base64编码之后的产物,无法直观的解析密钥的内容。
DER格式
如下是一个公钥的DER格式:
30819f300d06092a864886f70d010101050003818d0030818902818100cc9a1f4ccfe7bf8bbf4ff41e6f0d373d3bdd36c6fb96e88158a19d3b8eb145fc0cd0f02b57fa54b8df14f3bbbf44a218ba04e9b2d501ac400d9eb986e6eb2c3c388b8edd233bd959ad0661ecf468a0ed57b850ba8aca7d2150b78448b74386c16df6df3adc56e7f25bdd4cd22859df54fef495d938ac3391af323b6f1e270c190203010001
DER格式可以认为是二进制的密钥格式,咱们可以根据一定的规则进行解析(准确的说是ASN1语法,ASN1语法比较复杂,如果无法彻底理解,可以大概理解为tlv格式)
DER公钥解析
公钥语法为:
RSAPublicKey ::= SEQUENCE {
modulus INTEGER, -- n
publicExponent INTEGER -- e
}
类型RSAPublicKey的域具有以下意义:
• modulus 是RSA的合数模n。
• publicExponent 是RSA公开幂e。
按照ASN1语法解析上述公钥:
30819f//容器[30 ASN1语法中的SEQUENCE] [81长度数据占1位] [9F容器内有9F字节数据] 类似TLV格式,对吧?
300d06092a864886f70d0101010500//容器[30 SEQUENCE] [0D容器内有0D字节数据] 固定内容,长度为0x0D个字节,为RSA OID
03818d00//03 ASN1语法中的BIT STRING, BIT STRING内容前面需要加00
308189//容器[30 SEQUENCE] [81长度数据占1位] [89容器内有0x89字节数据]
028181//02 ASN1语法中的INTEGER整数[02 INTEGER] [81长度数据占1位] [81 Modulus内容长x81字节]
00cc9a1f4ccfe7bf8bbf4ff41e6f0d373d3bdd36c6fb96e88158a19d3b8eb145fc0cd0f02b57fa54b8df14f3bbbf44a218ba04e9b2d501ac400d9eb986e6eb2c3c388b8edd233bd959ad0661ecf468a0ed57b850ba8aca7d2150b78448b74386c16df6df3adc56e7f25bdd4cd22859df54fef495d938ac3391af323b6f1e270c19//Modulus
0203010001//02 INTEGER整数[02 INTEGER] [03 Exponent内容长x03字节]
解析完毕。如果觉得不清晰,请联系我,我再完善
3)DER私钥解析
私钥的asn1语法:
RSAPrivateKey ::= SEQUENCE {
version Version,
modulus INTEGER, -- n
publicExponent INTEGER, -- e
privateExponent INTEGER, -- d
prime1 INTEGER, -- p
prime2 INTEGER, -- q
exponent1 INTEGER, -- d mod (p-1)
exponent2 INTEGER, -- d mod (q-1)
coefficient INTEGER, -- (inverse of q) mod p
otherPrimeInfos OtherPrimeInfos OPTIONAL
}
• version 是版本号,为了与本文档的今后版本兼容。本篇文档的这个版本号应该是0,如果使用了多素数,则版本号应该是1。
Version ::= INTEGER { two-prime(0), multi(1) }
(CONSTRAINED BY {-- version must be multi if otherPrimeInfos present --})
modulus 是RSA合数模n。
publicExponent 是RSA的公开幂e。
privateExponent 是RSA的私有幂d。
prime1 是n的素数因子p。
prime2 i是n的素数因子q。
exponent1 等于d mod (p − 1)。
exponent2 等于d mod (q − 1)。
coefficient 是CRT系数 q–1 mod p。
otherPrimeInfos 按顺序包含了其它素数r3, …, ru的信息。如果version是0 ,它应该被忽略;而如果version是1,它应该至少包含OtherPrimeInfo的一个实例。
OtherPrimeInfos ::= SEQUENCE SIZE(1..MAX) OF OtherPrimeInfo
OtherPrimeInfo ::= SEQUENCE {
prime INTEGER, -- ri
exponent INTEGER, -- di
coefficient INTEGER -- ti
}
OtherPrimeInfo的各域具有以下意义:
• prime 是n的一个素数因子ri ,其中i ≥ 3。
• exponent 是di = d mod (ri − 1)。
• coefficient 是CRT系数 ti = (r1 · r2 · … · ri–1)–1 mod ri。
私钥的内容太多,解析就不举例说明了,如果有不明白的,欢迎留言。
密钥格式转换代码
PEM = "-----BEGIN PUBLIC KEY-----" + encode_base64(decode_hex(DER)) + "-----END PUBLIC KEY-----"
代码如下:
编译:
g++ main_dertoper.cpp -g -O0 -o main -lcrypto
下面是输出结果:
完整代码请关注公众号
参考文章:
https://www.linuxidc.com/linux/2015-01/112074.htm
https://www.cnblogs.com/yiyongling/articles/11365380.html
https://bbear.com.cn/index.php/archives/157/