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

4 篇文章 0 订阅
1 篇文章 0 订阅

环境: 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-----"

代码如下:

#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/

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

luopandeng

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值