RSA密钥格式解析 DER-PEM 转换代码
环境: ubuntu20.4 内核 5.10.10RSA是最常见的非对称加密算法,RSA的原理就不做介绍了,主要介绍一下密钥格式,linux 进行密钥转换编程。要学习RSA编码,首先需要理解RSA密钥的格式。RSA有公钥,私钥之分,公钥私钥都使用相同的语法格式。RSA的密钥有两种格式,PEM格式,DER格式PEM格式PEM格式包含几种报文头"-----BEGIN PUBLIC KEY-----":
环境: 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-----"
代码如下:
#include <stdio.h>
#include <iostream>
#include <string.h>
#include <stdlib.h>
#include <openssl/bn.h>
#include <openssl/rsa.h>
#include <openssl/pem.h>
#include <openssl/err.h>
#define debug
//gcc main.cpp -g -O0 -o main -lcrypto
#if 1
char pub_key[294] ={
'\x30','\x82','\x01','\x22',
'\x30','\x0d','\x06','\x09','\x2a','\x86','\x48','\x86','\xf7','\x0d','\x01','\x01','\x01','\x05','\x00',
'\x03','\x82','\x01','\x0f','\x00',
'\x30','\x82','\x01','\x0a',
'\x02','\x82','\x01','\x01','\x00',
'\xDB','\x10','\x1A','\xC2','\xA3','\xF1','\xDC','\xFF','\x13','\x6B','\xED','\x44','\xDF','\xF0','\x02','\x6D',
'\x13','\xC7','\x88','\xDA','\x70','\x6B','\x54','\xF1','\xE8','\x27','\xDC','\xC3','\x0F','\x99','\x6A','\xFA',
'\xC6','\x67','\xFF','\x1D','\x1E','\x3C','\x1D','\xC1','\xB5','\x5F','\x6C','\xC0','\xB2','\x07','\x3A','\x6D',
'\x41','\xE4','\x25','\x99','\xAC','\xFC','\xD2','\x0F','\x02','\xD3','\xD1','\x54','\x06','\x1A','\x51','\x77',
'\xBD','\xB6','\xBF','\xEA','\xA7','\x5C','\x06','\xA9','\x5D','\x69','\x84','\x45','\xD7','\xF5','\x05','\xBA',
'\x47','\xF0','\x1B','\xD7','\x2B','\x24','\xEC','\xCB','\x9B','\x1B','\x10','\x8D','\x81','\xA0','\xBE','\xB1',
'\x8C','\x33','\xE4','\x36','\xB8','\x43','\xEB','\x19','\x2A','\x81','\x8D','\xDE','\x81','\x0A','\x99','\x48',
'\xB6','\xF6','\xBC','\xCD','\x49','\x34','\x3A','\x8F','\x26','\x94','\xE3','\x28','\x82','\x1A','\x7C','\x8F',
'\x59','\x9F','\x45','\xE8','\x5D','\x1A','\x45','\x76','\x04','\x56','\x05','\xA1','\xD0','\x1B','\x8C','\x77',
'\x6D','\xAF','\x53','\xFA','\x71','\xE2','\x67','\xE0','\x9A','\xFE','\x03','\xA9','\x85','\xD2','\xC9','\xAA',
'\xBA','\x2A','\xBC','\xF4','\xA0','\x08','\xF5','\x13','\x98','\x13','\x5D','\xF0','\xD9','\x33','\x34','\x2A',
'\x61','\xC3','\x89','\x55','\xF0','\xAE','\x1A','\x9C','\x22','\xEE','\x19','\x05','\x8D','\x32','\xFE','\xEC',
'\x9C','\x84','\xBA','\xB7','\xF9','\x6C','\x3A','\x4F','\x07','\xFC','\x45','\xEB','\x12','\xE5','\x7B','\xFD',
'\x55','\xE6','\x29','\x69','\xD1','\xC2','\xE8','\xB9','\x78','\x59','\xF6','\x79','\x10','\xC6','\x4E','\xEB',
'\x6A','\x5E','\xB9','\x9A','\xC7','\xC4','\x5B','\x63','\xDA','\xA3','\x3F','\x5E','\x92','\x7A','\x81','\x5E',
'\xD6','\xB0','\xE2','\x62','\x8F','\x74','\x26','\xC2','\x0C','\xD3','\x9A','\x17','\x47','\xE6','\x8E','\xAB',
'\x02','\x03','\x01','\x00','\x01'
};
int pub_key_len = 294;
#if 0
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2xAawqPx3P8Ta+1E3/AC
bRPHiNpwa1Tx6Cfcww+ZavrGZ/8dHjwdwbVfbMCyBzptQeQlmaz80g8C09FUBhpR
d722v+qnXAapXWmERdf1BbpH8BvXKyTsy5sbEI2BoL6xjDPkNrhD6xkqgY3egQqZ
SLb2vM1JNDqPJpTjKIIafI9Zn0XoXRpFdgRWBaHQG4x3ba9T+nHiZ+Ca/gOphdLJ
qroqvPSgCPUTmBNd8NkzNCphw4lV8K4anCLuGQWNMv7snIS6t/lsOk8H/EXrEuV7
/VXmKWnRwui5eFn2eRDGTutqXrmax8RbY9qjP16SeoFe1rDiYo90JsIM05oXR+aO
qwIDAQAB
-----END PUBLIC KEY-----
#endif
#endif
static int base64_encode(char *str,int str_len,char *encode,int encode_len){
BIO *bmem,*b64;
BUF_MEM *bptr;
b64=BIO_new(BIO_f_base64());
bmem=BIO_new(BIO_s_mem());
b64=BIO_push(b64,bmem);
BIO_write(b64,str,str_len); //encode
BIO_flush(b64);
BIO_get_mem_ptr(b64,&bptr);
if(bptr->length>encode_len){
printf("encode_len too small\n");
return -1;
}
encode_len=bptr->length;
memcpy(encode,bptr->data,bptr->length);
BIO_free_all(b64);
return encode_len;
}
int key_der_pem(char *key)
{
int len = 0, beginlen=0, endlen=0;
char base64_key[512];
const char *begin="-----BEGIN PUBLIC KEY-----\n";
const char *end="-----END PUBLIC KEY-----\n";
#ifdef debug
printf("\n");
for(int i = 0 ; i < pub_key_len; i++)
{
printf("%02x",(unsigned char)pub_key[i]);
}
printf("\n");
#endif
len = base64_encode(pub_key,pub_key_len,base64_key,512);
beginlen = strlen(begin);
memset(key,0,512);
memcpy(key,begin,beginlen);
memcpy(key+beginlen,base64_key,len);
endlen = strlen(end);
memcpy(key+beginlen+len,end,endlen);
len = len + beginlen + endlen;
#ifdef debug
printf("key len %d\n",len);
for(int i = 0 ; i < len; i++)
{
printf("%c",(unsigned char)key[i]);
}
printf("\n\n");
#endif
return len;
}
#if 1
int main(int argc, char *argv[])
{
int i = 0,len = 0;
char pem_key[512];
memset(pem_key,0,512);
len = key_der_pem(pem_key);
return 0;
}
#endif
编译:
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/
更多推荐
所有评论(0)