RSA密钥格式解析 DER-PEM 转换代码

环境: 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字节]

解析完毕。如果觉得不清晰,请联系我,我再完善

3DER私钥解析

 

私钥的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/


版权声明:本文为luopandeng原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。