当前位置:   article > 正文

一文搞懂系列——PEM文件解析流程

pem文件

在这里插入图片描述

背景

前几周,协助同事解决了SM2软签名的需求,其流程可参考终于解决了!!! 基于GmSSL的SM2签名算法及思路分享

但是在解决这个问题的过程中,让我想起了一些不好的回忆:曾经在大众项目中,也接触过椭圆曲线算法签名。其中因为平台下发的公钥格式,由于双方理解不一致,导致最终调试很久,并且自己也处于懵懵懂懂的状态

在协助同事解决软签名的过程中,也遇到了类似问题:证书内容与公钥私钥的关系,如何从.pem格式文件中,提取我们需要的公私钥。

而本文的目的就是介绍.pem文件中的数据格式以及如何解析。作为上层应用开发者可能并不需要了解,知道如何调用接口即可。但是对于我而言,这就是我心中的痛,所以我一定要了解:如何从.pem格式的私钥证书中,获取公钥、私钥信息。

有兴趣的朋友不妨可以了解一下。本文将以下列私钥文件举例描述:

xieyihua@xieyihua:~/GmSSL-master$ cat prikey.pem
-----BEGIN PRIVATE KEY-----
MIGTAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBHkwdwIBAQQgtJNqd4cc2GtzhhR/
Up73/B5aPXainNVCT3m9Pz09m2ygCgYIKoEcz1UBgi2hRANCAATX3mX8KCvFzcPM
7vB3Ys62UoJFNxreM0RwDxTCBJ00UhnfLTN347ELOQpxeCuWG5spGyacvvlnehN3
nNwtem+V
-----END PRIVATE KEY-----
xieyihua@xieyihua:~/GmSSL-master$
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

基础内容学习

在分析上述文件前,我们需要了解以下内容。

pem 与 der

  • PEM:Privacy-Enhanced Mail,隐私增强邮件。PEM格式文件后缀通常为".pem"、“.cer”、“.crt”、“.key”,后缀名并不会影响 PEM 格式文件的解析。
  • DER:Distinguished Encoding Rules,可分辩编码规则。DER格式文件后缀通常为 “.der” 和 “.cer”,后缀名并不会影响 DER 格式文件的解析。

其中pemder的关系:

ASN.1 ----->(序列化)-----> DER -----(Base64编码)-----> PEM
  • 1

即:对ASN.1进行序列化后,得到一个二进制串,这就是DER格式。再将DER格式数据进行base64编码,加上PEM特有的头、尾(本文中-----BEGIN PRIVATE KEY----------END PRIVATE KEY-----),即可得到私钥文件内容。

由上可知,我们只需要逆向解析,得到DER格式数据,按照ASN.1的规则进行解析即可了解文件中的各字段内容。

初识 ANS.1

在现代通信和信息技术领域,数据的精确表示和高效传输是至关重要的。ASN.1(Abstract Syntax Notation One)作为一种国际标准化组织(ISO)和国际电信联盟(ITU)制定的国际标准,提供了一种抽象的方法来描述、编码、解码和传输数据。ASN.1的核心组成部分之一是BER(Basic Encoding Rules),它定义了一种将ASN.1数据结构转换为二进制形式的编码规则。

而DER是BER的特殊版本,主要应用于安全领域中,对一些场景具有唯一性,但是解析规则都是一样的。

BER编码基本格式

BER(Basic Encoding Rules)是一种用于描述ASN.1(Abstract Syntax Notation One)数据的编码规则。BER编码使用一种TLV(Type-Length-Value)的结构方法编码。

即,BER编码的基本结构由以下三个部分组成:

  • 类型域(Type)
  • 长度域(Length)
  • 内容域(Value)

其中类型(Type)部分又有三部分组成:

  • 标签类型(Class)
  • 构造类型(P/C)
  • 标签号(Tag)

其中数据域(Value)也存在两种情况:

  • 单一的数据类型,如intDouble类型;
  • 复杂的自定义类型结构,对于一种数据结构中包含了其它数据结构,往往就是一些简单类型。类似C语言中的结构体。如下:

类型域

类型域(Type)用于标识数据的类型和类别。类型域编码包含三个部分:类(Class)、构造类型(PC, Primitive/Constructed)、和标签号(Tag Number)。下面详细说明这三个部分的编码:

类型域字节的格式如下:

分析:

标签类型有四种,分别是:

  • Universal(通用类别):值为 00。这类数据类型是由ASN.1标准定义的,如整数、布尔值、位串等。它们是通用的,可以在任何上下文中使用。
  • Application(应用类别):值为 01。这类数据类型是特定应用程序定义的,它们在特定的应用上下文中具有特殊的含义。
  • Context-specific(上下文相关类别):值为 10。这类数据类型通常用于结构化数据中的标记字段,它们的含义依赖于特定的上下文或结构。
  • Private(私有类别):值为 11。这类数据类型是为私有的或组织特定的用途而定义的,它们在标准中没有定义,通常由个别组织内部使用。

构造类型有两种,分别是:

  • 原始类型(Primitive):值为0。用于表示ASN.1中的基本数据类型,如INTEGER、OCTET STRING、BOOLEAN等
  • 构造类型(Constructed):值为1。用于表示ASN.1中的复合数据类型,如SEQUENCESETSEQUENCE OFSET OF等。或创建复杂的数据结构。

标签号的作用表示具体的数据类型或结构。它的取值范围有两种:

  1. 简单标签号(0~30)。对于标签号在 0 到 30 之间的情况,直接在类型域的第4-8位表示。例如:
通用类(Universal)布尔类型(Boolean):0000 0001,即 0x01
应用类(Application)整数类型(Integer):0100 0010,即 0x42
  • 1
  • 2
  1. 复杂标签号(>=31)。对于标签号大于等于 31 的情况,第4-8位全为 1(即 0b11111),并且标签号以基于 7 位的块形式在后续字节中表示,每个字节的最高位为 1,表示后续有更多字节,最后一个字节的最高位为 0。例如:
标签号 31:0b1111 1111 0011 1111,即 0x1F 0x1F

标签号 128:0b1111 1111 1000 0001 0000 0000,即 0x1F 0x81 0x00
  • 1
  • 2
  • 3

ASN.1 已定义的标签号如下:

类型标签号
BIT STRING00000011(0x03)
BOOLEAN00000001(0x01)
INTEGER00000010(0x02)
Null00000101(0x05)
对象标识符00000110(0x06)
八进制字符串00000100(0x04)
BMPString00011110(0x1E)
IA5String00010110(0x16)
PrintableString00010011(0x13)
TeletexString00010100(0x14)
UTF8String00001100(0x0C)
SEQUENCE00110000(0x30)
序列00110000(0x30)
SET00110001(0x31)
SET OF00110001(0x31)

长度域

在 BER(Basic Encoding Rules)编码中,长度域用于指示随后的值域(Value)的长度。长度域的编码有主要两种形式:短形式长形式。下面是对这两种形式的详细说明:

  • 短形式

短形式用于表示长度小于 128 字节(即 0 到 127)的情况。在这种形式中,长度域仅占一个字节。该字节的最高位(第八位)为 0,低七位表示长度的值。例如:

若长度为 5,则长度域为 0000 0101(即 0x05)。

若长度为 127,则长度域为 0111 1111(即 0x7F)。
  • 1
  • 2
  • 3
  • 长形式

长形式用于表示长度大于等于 128 字节的情况。在这种形式中,长度域的第一个字节的最高位(第八位)为 1,低七位表示后续长度字节的个数。例如:

若长度为 128,则长度域为 1000 0001(表示后续有 1 个字节)加上 1000 0000(表示长度为 128),即 0x81 0x80。

若长度为 300,则长度域为 1000 0010(表示后续有 2 个字节)加上 0000 0001 0010 1100(即 300),即 0x82 0x01 0x2C。
  • 1
  • 2
  • 3

内容域

在 BER(Basic Encoding Rules)编码中,内容域(Value)包含实际的数据信息,其编码方式取决于数据的类型。不同数据类型有不同的编码规则。以下是一些常见数据类型的编码方式:

  • 布尔型(BOOLEAN)

布尔型值使用一个字节表示:TRUE 编码为 0xFF、FALSE 编码为 0x00;

  • 整型(INTEGER)

整型值以大端顺序(高字节在前)编码,使用最少的字节数来表示值。如果最高有效位为 1,则需要在前面加一个 0x00 以避免符号扩展。例如:

0 编码为 0x00
127 编码为 0x7F
128 编码为 0x00 0x80
-1 编码为 0xFF
  • 1
  • 2
  • 3
  • 4
  • 位串(BIT STRING)

位串由一个初始字节和实际数据组成。初始字节表示未使用的位数。实际数据按字节顺序排列。例如:

0x01101011(假设全用)编码为 0x00 0x6B
0x01101010(未使用1位)编码为 0x01 0x6A
  • 1
  • 2
  • 字符串(OCTET STRING)

字符串(八位字节串)按字节顺序直接编码。例如:

"Hello" 编码为 0x48 0x65 0x6C 0x6C 0x6F
  • 1
  • NULL

NULL 值没有内容,其长度为 0。因此,NULL 值的编码只是标记和长度,值为空。例如:

NULL 编码为 0x05 0x00
  • 1
  • 对象标识符(OBJECT IDENTIFIER)

对象标识符使用变量长度编码。前两个节点由 (X * 40) + Y 公式表示,后续节点使用基于 7 位的块形式编码,最高位为 1 表示有后续字节。例如:

1.2.840.113549 编码为 0x2A 0x86 0x48 0x86 F7 0x0D(1*40 + 2 = 42, 840 = 0x86 0x48, 113549 = 0x86 0xF7 0x0D)
  • 1
  • 序列(SEQUENCE)

序列包含一个或多个元素,每个元素按其类型编码,然后依次排列。例如,一个包含一个整数和一个字符串的序列:

整数:42 编码为 0x02 0x01 0x2A
字符串:"Hi" 编码为 0x04 0x02 0x48 0x69
整个序列编码为:0x30(标记) 0x07(长度) 0x02 0x01 0x2A(整数) 0x04 0x02 0x48 0x69(字符串)
  • 1
  • 2
  • 3

分析

通过以上基础概念的了解,我们可以尝试分析pem文件了。

  1. 查看pem文件内容,通过base64反编码,获取der数据。在线base64编解码

即der数据为:

308193020100301306072A8648CE3D020106082A811CCF5501822D047930770201010420B4936A77
871CD86B7386147F529EF7FC1E5A3D76A29CD5424F79BD3F3D3D9B6CA00A06082A811CCF5501822D
A14403420004D7DE65FC282BC5CDC3CCEEF07762CEB6528245371ADE3344700F14C2049D345219DF
2D3377E3B10B390A71782B961B9B291B269CBEF9677A13779CDC2D7A6F95
  • 1
  • 2
  • 3
  • 4
  1. 根据BER编码格式进行解析

因为数据域是构造类型,因此需要再解析,以此往复,最终解析格式如下:

30 类型域 构造类型 
81 93 长度域 0x93 = 147
    02	通用类型	INTEGER
    01	长度为1
    00	数据域	
    30	构造类型
    13	长度19
            06	通用类型	对象标识符
            07	长度 7
            2A8648CE3D0201	数据域
            06	通用类型	对象标识符
            08	长度 8
            2A811CCF5501822D	数据域
    04	通用类型	八进制字符串
    79	长度 121
    
            30	构造类型
            77	长度 119
            
                  02	通用类型	INTEGER
                  01	长度 1
                  01	数据域
                  
                  04	通用类型	八进制字符串
                  20	长度 32
                  B4936A77871CD86B7386147F529EF7FC1E5A3D76A29CD5424F79BD3F3D3D9B6C	数据域
                A0	构造类型
                0A	长度 10
                    06	通用类型	对象标识符
                    08	长度 8
                    2A811CCF5501822D	数据域
                A1	构造类型
                44	长度 68
                    03	通用类型	BIT STRING
                    42	长度 66
                    0004D7DE65FC282BC5CDC3CCEEF07762CEB6528245371ADE3344700F14C2049D345219DF2D3377E3B10B390A71782B961B9B291B269CBEF9677A13779CDC2D7A6F95	数据域
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  1. 根据各BER编码格式解析各数据域(建议自己手动计算一遍),得到:
---0    //指定私钥信息(PrivateKeyInfo)结构中的版本号
------1.2.840.10045.2.1 // 公钥对象标识符
-------1.2.156.10179.1.301  //SM2 算法对象标识符
-------1        //椭圆曲线(Elliptic Curve,简称 EC)私钥结构的版本号
-------B4936A77871CD86B7386147F529EF7FC1E5A3D76A29CD5424F79BD3F3D3D9B6C//私钥
----------1.2.156.10179.1.301//SM2 算法对象标识符
----------04D7DE65FC282BC5CDC3CCEEF07762CEB6528245371ADE3344700F14C2049D345219DF2D3377E3B10B390A71782B961B9B291B269CBEF9677A13779CDC2D7A6F95   //公钥,其中首字节04 并不是公钥信息,而是表示随后的数据是一个非压缩的点
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

至此,心中一阵舒畅。终于解决了心中的不快。还请注意,该pem私钥文件,并没有加密。所以能够解析出实际数据内容

总结

详细介绍了如何从.pem格式的私钥证书中提取公钥和私钥信息。.pem文件是私钥证书的常见格式,而der格式是.pem文件中数据的基本格式。文章通过介绍pem和der的关系、ASN.1、BER编码格式等基础知识,帮助读者理解如何解析.pem文件中的数据。

若我的内容对您有所帮助,还请关注我的公众号。不定期分享干活,剖析案例,也可以一起讨论分享。
我的宗旨:
踩完您工作中的所有坑并分享给您,让你的工作无bug,人生尽是坦途
在这里插入图片描述
在这里插入图片描述

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/你好赵伟/article/detail/992755
推荐阅读
相关标签
  

闽ICP备14008679号