RSA密钥文件格式

RSA密钥文件格式

前言

各类证书密钥虽然都是采用RSA算法,但存储的文件格式也是多种多样,常见的有PEM,OpenSSH;而编码格式又有BER,DER;各类名词SSH,SSL,OpenSSH,OpenSSL,PKCS也容易让人混淆;本文将尝试把这些概念一一缕清~~

名词解释

  1. PKCS:公钥加密标准(Public Key Cryptography Standards, PKCS
  2. PKIX:公钥基础设施 (Public-Key Infrastructure X.509)
  3. ASN.1:一种结构化的描述语言(Abstract Syntax Notation One)
  4. BER:基本编码规则(Basic Encoding Rules)
  5. CER:规范编码规则(Canonical Encoding Rules)
  6. DER:可视化编码规则(Distinguished Encoding Rules)
  7. XER:XML编码规则(XML Encoding Rules)
  8. JER:Json编码规则(JSON Encoding Rules)
  9. PEM:隐私增强的邮件(Privacy Enhanced Mail)
  10. SSL:安全套接层(Secure Sockets Layer)
  11. TLS:安全传输层协议(Transport Layer Security)
  12. SSH:安全命令行链接(​Secure Shell
  13. OpenSSH:SSH协议的免费开源实现(OpenBSD Secure Shell)
  14. OID:对象标识符(Object Identifier)

格式介绍

简介
  1. ASN.1 是一种结构化的描述语言,,具有类型(type)和符号(notation);
  2. BER和DER都是二进制的编码规则,使用《Type,Length,Value》的三元组标识一组数据,使用ASN.1来描述编码;
  3. DER是BER的子集,具有更多的规则限制;
  4. DER格式文件是二进制存储,而PEM是文本形式;
  5. 实际上,PEM就是把DER格式的数据用base64编码后,然后再在头尾各加上一段“-----”开始的标记而已。

ASN.1

ASN.1 和 DER:https://letsencrypt.org/docs/a-warm-welcome-to-asn1-and-der/

ASN.1解码器:https://lapo.it/asn1js/

格式和符号(tag)

ASN.1的数据结构类似C语言的结构体,支持将对象和数组等序列化为ASN.1格式的二进制,并反序列化为变量

// ASN.1
Point ::= SEQUENCE {
  x INTEGER,
  y INTEGER,
  label UTF8String
}

// C语言结构体
struct point {
  int x, y;
  char label[10];
};

Tag(decimal)

Tag(Hex)

Type

2

0x02

INTEGER

整数

3

0x03

BIT STRING

位串

4

0x04

OCTET STRING

八位字节字符串

5

0x05

NULL

空类型

6

0x06

OBJECT IDENTIFIER

对象标识符

12

0x00C

UTF8String

UTF8字符串

16

0x10 (and 0x30)*

SEQUENCE and SEQUENCE OF

序列

17

0x11 (and 0x31)*

SET and SET OF

有序序列

19

0x13

PrintableString

可打印字符串

22

0x16

IA5String

IA5字符串

23

0x17

UTCTime

UTC时间

24

0x18

GeneralizedTime

广义时间

编码

ASN.1 与许多编码相关联:BER、DER、PER、XER,这里以DER编码举例,采用《类型-长度-值》的方式标记,如十六进制 02 03 01 00 01 表示整数65537,第一个02表示类型为整数,03表示长度为3个字节,01 00 01表示十六进制,二进制位0000 0001 0000 0000 0000 1 = 65537

长度字段有两种规格:短格式和长格式

小于等于127的用短格式,即该字节表示实际长度

当长度大于127的时,使用长格式,长格式在下面介绍

Type

Length

Value

02

03

01 00 01

而作为标记的tag,也有其编码规则,如0x02 = 0000 0010,左数的2bit表示这是一种通用编码

Class

Bit 8

Bit 7

Universal

0

0

Application

0

1

Context-specific

1

0

Private

1

1

举一个ASN.1完整的例子,可以点这里​​查看解码​​,下面的一串十六进制编码表示了一个坐标(9,9),最开始的30表示接下来是ASN.1序列,06表示这个序列有6个字节;而80表示这是一个具有上下文意义的值,后面的01表示这个值有一个字节,09表示这个值是9;后面的81 01 09含义类似

Base64编码对照表:https://tooltt.com/base64-table/

​30 06 80 01 09 81 01 09​

Point ::= SEQUENCE {
  x [APPLICATION 0] INTEGER OPTIONAL,
  y [APPLICATION 1] INTEGER OPTIONAL
}

长度

短格式:用一个字节直接表示长度,0~127

当需要表示128以上长度时,长度部分至少由两个字节表示,并且第一个字节的第 8 位(左数第一位)设置为 1。第一个字节的第 7-1 位表示长度字段本身还有n个字节。然后后面的n个字节用来表示总长度。如0x82 =1000 0010,表示后面还有2字节,而这两个字节是09 42,相当于十进制的2370,则表示后面的value是有2370字节

30 82 09 42……

因此可以表示的长度是非常大的,最长时首字节为254 (255 是为将来的扩展保留的),用后续的126个字节表示长度,如果每个字节都是255,则总长度为:[ 2^(1008) ]-1(单位字节)

长度字节起始

长度字节截止

最大值

短格式

0x00

0x7F

127字节

长格式

0x80

0xFE FF FF ……

[ 2^(1008) ]-1 字节

ASN.1与DER的应用将在下面的RSA密钥文件中展开

PEM密钥文件(RFC8017,PKCS #1)

PCKS#1标准的RSA密钥文件:https://www.rfc-editor.org/rfc/rfc8017#page-54

PKCS1与PKCS8的小知识:https://www.jianshu.com/p/a428e183e72e

标记介绍

以RFC8017为例,采用的是PKCS #1标准,DER编码格式,下面是ASN.1的tag,分别对应RSA算法的各个参数,可以参考​​这个​

# 公钥
 RSAPublicKey ::= SEQUENCE {
        modulus           INTEGER,  -- n
        publicExponent    INTEGER   -- e
 }
 
 # 私钥       
 RSAPrivateKey ::= SEQUENCE {
        version           Version,
        modulus           INTEGER,  -- n,模数n
        publicExponent    INTEGER,  -- e,公钥指数
        privateExponent   INTEGER,  -- d,私钥指数
        prime1            INTEGER,  -- p,大素数1
        prime2            INTEGER,  -- q,大素数2
        exponent1         INTEGER,  -- d mod (p-1)
        exponent2         INTEGER,  -- d mod (q-1)
        coefficient       INTEGER,  -- (inverse of q) mod p
        otherPrimeInfos   OtherPrimeInfos OPTIONAL
}

使用以下命令,通过openssl新建公钥和私钥文件

使用其他命令,其他工具,或者其他版本的openssl生成的密钥文件可能格式不同

我这里默认生成的是PKCS#1

openssl genrsa -out prikey.p1 2048
openssl rsa -in prikey.p1 -pubout -RSAPublicKey_out > pubkey.p1

# openssl版本
[root@localhost tmp]# openssl version -a
OpenSSL 1.0.2k-fips  26 Jan 2017
built on: reproducible build, date unspecified
platform: linux-x86_64
options:  bn(64,64) md2(int) rc4(8x,int) des(idx,cisc,16,int) idea(int) blowfish(idx) 
compiler: gcc -I. -I.. -I../include  -fPIC -DOPENSSL_PIC -DZLIB -DOPENSSL_THREADS -D_REENTRANT -DDSO_DLFCN -DHAVE_DLFCN_H -DKRB5_MIT -m64 -DL_ENDIAN -Wall -O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector-strong --param=ssp-buffer-size=4 -grecord-gcc-switches   -m64 -mtune=generic -Wa,--noexecstack -DPURIFY -DOPENSSL_IA32_SSE2 -DOPENSSL_BN_ASM_MONT -DOPENSSL_BN_ASM_MONT5 -DOPENSSL_BN_ASM_GF2m -DRC4_ASM -DSHA1_ASM -DSHA256_ASM -DSHA512_ASM -DMD5_ASM -DAES_ASM -DVPAES_ASM -DBSAES_ASM -DWHIRLPOOL_ASM -DGHASH_ASM -DECP_NISTZ256_ASM
OPENSSLDIR: "/etc/pki/tls"
engines:  rdrand dynamic

# 查看文件格式
[root@localhost tmp]# file prikey.p1 
prikey.p1: PEM RSA private key
[root@localhost tmp]# file pubkey.p1 
pubkey.p1: ASCII text
私钥

可以看到生成的私钥文件是PEM格式的,还可以通过前面介绍的​​ASN.1解码器​​查看数据的结构,

[root@localhost tmp]# cat prikey.p1 
-----BEGIN RSA PRIVATE KEY-----
MIIEpQIBAAKCAQEA4sOucnAjCqCyN9DwTHws7Hnb9v9IK4eQn6SA5IJWqaIjnJl9
vEdQHOYs5fs9PcHw1a3NFIaKLRh8+oA/TDP7ejW9eb8mXj8hLQgQykdsx+K5h1WK
MDFA1024hVigvQ0Gwi+iSQcMsD5EMySX47QulnCclUT2eqR3msbxbhwgt81F+O+W
Nzh/YcNseU09ao4dvSrKFFtR5MrGI3MBo7AaCZi2dVZrY+6yW1aa5LSSwqc9j10w
YWqqkI8tQpNSw67NJln6N6ZPylKT+SGABl2IBZDJaua8D6b18j+74VUKx180KiI+
sxtePcEPwv3YZVj8k9JH2HLSuXQvocLgxJGnpQIDAQABAoIBAQC3892T3/vNJ0br
mIcWxLT8sG4MFFLLXRGpL52PwVvkCC+zXaBuF7uZde9+MS0g6C8iZ/cxa2Kz6mWY
IGk4vAKIxQeLt9C8K6ISEbTtIrvP+tBeRLnxFkNvZREGuzrJocOVy2Zur1VPs3NH
uautgAE68OdU/7ijDMApwrEfjZnM/yV6v0hAtQLVN4KzQJ5/LmbvKQA4lPLB43ag
6/Us+NUGkzKIOiX6TfkD4+ohQDjRexXYhBhNELT/2T7u1yhKsMUEqTzQTcC4KEan
qc2IfEaldEfNR0HYLP+7BHn0xO0q+tZLe6dGwtANLzos+htFKknBm7s4+3qZmj0Z
1AJtZQkBAoGBAPbK4f+idMPrbuIBAaT9FsNGPIRV2/kP8rXevScd1xuanCTCuqXc
F0HX9lkMi4durx+NSDiOm3nGX2wlyxLD+tB2C0WnlBEsCEWY8wjOqPog3kd9eCx6
cMZr5vV/kKhlb3NRjAJmepexMfFvV7L1tpj+cueLFMNPXtTVJXkK90FxAoGBAOs5
goIbR0TXYDuZ95HhOK/IsdV7hVI2EEjMS+NNK/EqW6f3YwSDLjvJU/pmLkX4UznI
3e9cu83N1dlhjcIPffrt1gIBJCIfM0t3snnTexVKeniFQLk6a48KcFOeMdr6I133
pU3B8Ap7sJQC7JVFFDVWhCk2PbQXkIk35Oa+3S91AoGBAMkcX7rQClM14jAuHcbz
r78F138H8LAv/NuUXZQozwhhn+ic/zj198XiVZAPlwyQeAlAOqBMJgrASzZ4PdpW
9mc2Ks1iWFj+BuegUjzVHAJpEnozluC4DnrDp3xbrehscLwKsguqszA2Z7qVv2PI
tDpM8BobzpmyblcGMvlGpGMhAoGBAJWN5xBZz6JRXXoHloRFsgjxjT+yXT276faf
DdDjDAE1cTkF7Qq9bPPOOXqG5PzPufqPX6wERdO//ytK/xpXRz33YY28tACN3csp
u2wBrcwHon5mGpSqNCCp/mjrt78GMt5Xyd36ZGgF5Gr2wPzXAtY2v6iEOA78vITB
6ONcjrzRAoGAZTdGwLWQYqtyZOYpwp6woJBsn4krN6YFmXKFCWUHY7LvqFDwkPBQ
VxmNSY786+LMOCfoTe/bcEMs+Tr4IIHYmVo+R2re2HPCf5BMT0XndNLSA9jOZVlP
lk1GlSCN+BuC6ZowlRlk12KjRnYOvbCIhEURY3lUPZaXhvSnb3ckQDk=
-----END RSA PRIVATE KEY-----

或者通过​​base64解码器​​查看,这个私钥文件包含了公钥和私钥,采用的是ASN.1格式

以----分割的中间部分才是base64编码哦! 

下面以解码器为参照进行介绍,由于整个密钥文件超过了127字节,因此采用了长格式介绍,0x82表示后面用2个字节表示长度,而整个文件的总长度是:4+1189,前面的4字节是ASN.1的结构,后面的1189是value的长度

版本

标记的顺序为:version, n, e, d, p, q……可以参考上面的私钥标记

关于版本的说明可以参考​​RFC8017​​,简单来说,就是默认两个素数的版本就是0,多组素数的版本就是1

version is the version number, for compatibility with future
revisions of this document.  It SHALL be 0 for this version of the
document, unless multi-prime is used; in which case, it SHALL be
1.
    Version ::= INTEGER { two-prime(0), multi(1) }
       (CONSTRAINED BY
       {-- version must be multi if otherPrimeInfos present --})

模数(modulus,n)

这个参数就是RSA算法加密/解密时的模数,在文件中也是采用了长格式存储,因此使用两个字节表示长度,2048bit是前面创建文件的时候指定的位数

公钥(publicExponent,e)

同理,02 03 01 00 01就是公钥的65537,实际生产中常用这个值作为公钥的指数,有兴趣的小伙伴可以自行去​​扩展​​了解

私钥(privateExponent,d)

素数(prime1,prime2,即p,q)

也可以借助openssl库的命令来解析,可以看到和我们上面借助解码器手动分析的结果是一致的

[root@localhost tmp]# openssl rsa -text -noout < prikey.p1 
Private-Key: (2048 bit)
modulus:
    00:e2:c3:ae:72:70:23:0a:a0:b2:37:d0:f0:4c:7c:
    2c:ec:79:db:f6:ff:48:2b:87:90:9f:a4:80:e4:82:
    56:a9:a2:23:9c:99:7d:bc:47:50:1c:e6:2c:e5:fb:
    3d:3d:c1:f0:d5:ad:cd:14:86:8a:2d:18:7c:fa:80:
    3f:4c:33:fb:7a:35:bd:79:bf:26:5e:3f:21:2d:08:
    10:ca:47:6c:c7:e2:b9:87:55:8a:30:31:40:d7:4d:
    b8:85:58:a0:bd:0d:06:c2:2f:a2:49:07:0c:b0:3e:
    44:33:24:97:e3:b4:2e:96:70:9c:95:44:f6:7a:a4:
    77:9a:c6:f1:6e:1c:20:b7:cd:45:f8:ef:96:37:38:
    7f:61:c3:6c:79:4d:3d:6a:8e:1d:bd:2a:ca:14:5b:
    51:e4:ca:c6:23:73:01:a3:b0:1a:09:98:b6:75:56:
    6b:63:ee:b2:5b:56:9a:e4:b4:92:c2:a7:3d:8f:5d:
    30:61:6a:aa:90:8f:2d:42:93:52:c3:ae:cd:26:59:
    fa:37:a6:4f:ca:52:93:f9:21:80:06:5d:88:05:90:
    c9:6a:e6:bc:0f:a6:f5:f2:3f:bb:e1:55:0a:c7:5f:
    34:2a:22:3e:b3:1b:5e:3d:c1:0f:c2:fd:d8:65:58:
    fc:93:d2:47:d8:72:d2:b9:74:2f:a1:c2:e0:c4:91:
    a7:a5
publicExponent: 65537 (0x10001)
privateExponent:
    00:b7:f3:dd:93:df:fb:cd:27:46:eb:98:87:16:c4:
    b4:fc:b0:6e:0c:14:52:cb:5d:11:a9:2f:9d:8f:c1:
    5b:e4:08:2f:b3:5d:a0:6e:17:bb:99:75:ef:7e:31:
    2d:20:e8:2f:22:67:f7:31:6b:62:b3:ea:65:98:20:
    69:38:bc:02:88:c5:07:8b:b7:d0:bc:2b:a2:12:11:
    b4:ed:22:bb:cf:fa:d0:5e:44:b9:f1:16:43:6f:65:
    11:06:bb:3a:c9:a1:c3:95:cb:66:6e:af:55:4f:b3:
    73:47:b9:ab:ad:80:01:3a:f0:e7:54:ff:b8:a3:0c:
    c0:29:c2:b1:1f:8d:99:cc:ff:25:7a:bf:48:40:b5:
    02:d5:37:82:b3:40:9e:7f:2e:66:ef:29:00:38:94:
    f2:c1:e3:76:a0:eb:f5:2c:f8:d5:06:93:32:88:3a:
    25:fa:4d:f9:03:e3:ea:21:40:38:d1:7b:15:d8:84:
    18:4d:10:b4:ff:d9:3e:ee:d7:28:4a:b0:c5:04:a9:
    3c:d0:4d:c0:b8:28:46:a7:a9:cd:88:7c:46:a5:74:
    47:cd:47:41:d8:2c:ff:bb:04:79:f4:c4:ed:2a:fa:
    d6:4b:7b:a7:46:c2:d0:0d:2f:3a:2c:fa:1b:45:2a:
    49:c1:9b:bb:38:fb:7a:99:9a:3d:19:d4:02:6d:65:
    09:01
prime1:
    00:f6:ca:e1:ff:a2:74:c3:eb:6e:e2:01:01:a4:fd:
    16:c3:46:3c:84:55:db:f9:0f:f2:b5:de:bd:27:1d:
    d7:1b:9a:9c:24:c2:ba:a5:dc:17:41:d7:f6:59:0c:
    8b:87:6e:af:1f:8d:48:38:8e:9b:79:c6:5f:6c:25:
    cb:12:c3:fa:d0:76:0b:45:a7:94:11:2c:08:45:98:
    f3:08:ce:a8:fa:20:de:47:7d:78:2c:7a:70:c6:6b:
    e6:f5:7f:90:a8:65:6f:73:51:8c:02:66:7a:97:b1:
    31:f1:6f:57:b2:f5:b6:98:fe:72:e7:8b:14:c3:4f:
    5e:d4:d5:25:79:0a:f7:41:71
prime2:
    00:eb:39:82:82:1b:47:44:d7:60:3b:99:f7:91:e1:
    38:af:c8:b1:d5:7b:85:52:36:10:48:cc:4b:e3:4d:
    2b:f1:2a:5b:a7:f7:63:04:83:2e:3b:c9:53:fa:66:
    2e:45:f8:53:39:c8:dd:ef:5c:bb:cd:cd:d5:d9:61:
    8d:c2:0f:7d:fa:ed:d6:02:01:24:22:1f:33:4b:77:
    b2:79:d3:7b:15:4a:7a:78:85:40:b9:3a:6b:8f:0a:
    70:53:9e:31:da:fa:23:5d:f7:a5:4d:c1:f0:0a:7b:
    b0:94:02:ec:95:45:14:35:56:84:29:36:3d:b4:17:
    90:89:37:e4:e6:be:dd:2f:75
exponent1:
    00:c9:1c:5f:ba:d0:0a:53:35:e2:30:2e:1d:c6:f3:
    af:bf:05:d7:7f:07:f0:b0:2f:fc:db:94:5d:94:28:
    cf:08:61:9f:e8:9c:ff:38:f5:f7:c5:e2:55:90:0f:
    97:0c:90:78:09:40:3a:a0:4c:26:0a:c0:4b:36:78:
    3d:da:56:f6:67:36:2a:cd:62:58:58:fe:06:e7:a0:
    52:3c:d5:1c:02:69:12:7a:33:96:e0:b8:0e:7a:c3:
    a7:7c:5b:ad:e8:6c:70:bc:0a:b2:0b:aa:b3:30:36:
    67:ba:95:bf:63:c8:b4:3a:4c:f0:1a:1b:ce:99:b2:
    6e:57:06:32:f9:46:a4:63:21
exponent2:
    00:95:8d:e7:10:59:cf:a2:51:5d:7a:07:96:84:45:
    b2:08:f1:8d:3f:b2:5d:3d:bb:e9:f6:9f:0d:d0:e3:
    0c:01:35:71:39:05:ed:0a:bd:6c:f3:ce:39:7a:86:
    e4:fc:cf:b9:fa:8f:5f:ac:04:45:d3:bf:ff:2b:4a:
    ff:1a:57:47:3d:f7:61:8d:bc:b4:00:8d:dd:cb:29:
    bb:6c:01:ad:cc:07:a2:7e:66:1a:94:aa:34:20:a9:
    fe:68:eb:b7:bf:06:32:de:57:c9:dd:fa:64:68:05:
    e4:6a:f6:c0:fc:d7:02:d6:36:bf:a8:84:38:0e:fc:
    bc:84:c1:e8:e3:5c:8e:bc:d1
coefficient:
    65:37:46:c0:b5:90:62:ab:72:64:e6:29:c2:9e:b0:
    a0:90:6c:9f:89:2b:37:a6:05:99:72:85:09:65:07:
    63:b2:ef:a8:50:f0:90:f0:50:57:19:8d:49:8e:fc:
    eb:e2:cc:38:27:e8:4d:ef:db:70:43:2c:f9:3a:f8:
    20:81:d8:99:5a:3e:47:6a:de:d8:73:c2:7f:90:4c:
    4f:45:e7:74:d2:d2:03:d8:ce:65:59:4f:96:4d:46:
    95:20:8d:f8:1b:82:e9:9a:30:95:19:64:d7:62:a3:
    46:76:0e:bd:b0:88:84:45:11:63:79:54:3d:96:97:
    86:f4:a7:6f:77:24:40:39
公钥

ssh-keygen和openssl:https://flyingice.github.io/2020/07/09/encryption.html

平时生成密钥常用的命令是ssh-keygen和openssl,其实ssh-keygen也是调用openssl库来生执行RSA算法,默认生成的私钥文件也是PEM格式

[root@localhost tmp]# ssh-keygen -f id_rsa
Generating public/private rsa key pair.
Enter passphrase (empty for no passphrase): 
Enter same passphrase again: 
Your identification has been saved in id_rsa.
Your public key has been saved in id_rsa.pub.
The key fingerprint is:
SHA256:ar6gFQWu5K7utU2HiHn9+iIRwwBR14gjzf8QDdWB1F8 root@localhost.localdomain
The key's randomart image is:
+---[RSA 2048]----+
|+=..+Bo+..       |
|..=oo.+ o   E    |
| .+o...  . .     |
| o =o.    .      |
|  o +o  S        |
| .o.o.o.         |
| o.+++o.         |
| .o++=o          |
|+o...o*+         |
+----[SHA256]-----+

但通过file命令可以看到,ssh-keygen命令生成的公钥文件和openssl生成的有些不同,是SSH格式(见 ​​[rfc4251]​​),没有采用ASN.1和DER编码

直接查看,ssh-keygen生成的公钥是OpenSSH格式,内容分为三部分,用空格隔开:算法名称,公钥,注释

公钥部分包括指数e和模数n

将OpenSSH格式的公钥转成PKCS格式

ssh-keygen -e -f id_rsa.pub -m pem > newkey.pub

[root@localhost tmp]# cat id_rsa.pub 
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDrgoJ9ZYa5Nwe2aSpK0uL6bIzK+2ctVSTrgAJ6CJ7zTBYsmeRvACLnPjqFfVmi2ZqGLQlgP2tJTLZ8v8UvOhWMc7gF1g52hBJIU4Fe8t+tWd58P+X77CTpPX4OI7bEnoQ4OXCBlr8yTmiLQPjTigvo8Xzs7zSFqtIrj0mrn7E/T4+oWFfbBTz1PFW1rSN+Z/MPpInoIC5q1MjRr9hAxLAClNDvkl24OcqKRB2Mcyjf1n62kDapyz5X9wmWzHqy6VcAEvW+W+YsWR7i4JIistJDbZo6D9nWPmMvpwdZoVwxPnvzk+EQymcecIGiIggqnZXVea/CofvgbiEKRQ1REf6b root@localhost.localdomain

[root@localhost tmp]# cat newkey.pub 
-----BEGIN RSA PUBLIC KEY-----
MIIBCgKCAQEA64KCfWWGuTcHtmkqStLi+myMyvtnLVUk64ACegie80wWLJnkbwAi
5z46hX1Zotmahi0JYD9rSUy2fL/FLzoVjHO4BdYOdoQSSFOBXvLfrVnefD/l++wk
6T1+DiO2xJ6EODlwgZa/Mk5oi0D404oL6PF87O80harSK49Jq5+xP0+PqFhX2wU8
9TxVta0jfmfzD6SJ6CAuatTI0a/YQMSwApTQ75JduDnKikQdjHMo39Z+tpA2qcs+
V/cJlsx6sulXABL1vlvmLFke4uCSIrLSQ22aOg/Z1j5jL6cHWaFcMT5785PhEMpn
HnCBoiIIKp2V1XmvwqH74G4hCkUNURH+mwIDAQAB
-----END RSA PUBLIC KEY-----

对比编码,可以发现两者都包含模数n和指数e

扩展
  1. 不知道各位小伙伴有没有注意到,PEM格式的证书文件,总是“MII”开头?

还记得前面提到的长格式存储嘛,以pubkey.p1公钥为例

前面的三个字节30 82 01 ,第一个字节是ASN.1的sequence开头,而0x82中的2表明后面用两个字节表示长度,0x01是长度的第一个字节,对比二进制和​​base64编码表​​,以6bit对应一个字符,可以得知base64编码为MIIB;

之所以是0x82,是由于用两个字节表示长度,已经足够表示128~65535,而几乎所有证书的长度都在这个范围;而且,只要证书的长度小于16384字节,则表示长度的第一个字节的前两位(箭头所指)即为00,转成base64后就对应了“MII” !!!

在linux中,可以使用xxd命令将十六进制转成二进制

[root@localhost tmp]# xxd -r -p <<<308201 | base64
MIIB
PKCS#1和PKCS#8

PKCS1与PKCS8的小知识:https://www.jianshu.com/p/a428e183e72e

PKCS维基百科:https://en.wikipedia.org/wiki/PKCS

PKCS百度百科:https://baike.baidu.com/item/PKCS/1042350

PKCS#1:全名《Public-Key Cryptography Standards (PKCS) #1: RSA Cryptography Specifications》,是RSA的加密标准,最新的文档是RFC8017,版本为2.2,其中也包括了私钥的定义规则;

PKCS#8:全名《Public-Key Cryptography Standards (PKCS) #8: Private-Key Information Syntax Specification》最新文档是RFC5958,版本1.2,是专门描述私钥的格式,和上面的PKCS#1有重叠的部分

创建两个版本的私钥文件

# 创建pkcs#1
openssl genrsa -out pkcs1.pem 1024
# 将pkcs#1转为pkcs#8
openssl pkcs8 -topk8 -inform PEM -in pkcs1.pem -outform PEM -nocrypt -out pkcs8.pem

[root@localhost tmp]# cat pkcs8.pem 
-----BEGIN PRIVATE KEY-----
MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAMrtbVNauF7zJLnC
nhEnD5mgXX/rIFNvyBH8RbUoWs8KVS+W5fweUY0jxuKA/doqMV1y8aEtrI8ShVd4
nUgtCPJntmPdgXG64zztI6m/RiSuiUkhHUcQo04votPtGcAgyi3FjqZWfLUdTtH8
l6AMoectBSWGydy5wzw5rXJOE1CtAgMBAAECgYAkHPK6xcdP5ndfs5nLtabaESby
j9Z14su5H6sMHT1j4RPd3z3Yi1C6DGblgk0q2R1l4wHLTESMRaCDO4zJ/nG72McJ
IAp5FAlRw0oj348wjHR3lqlKVuPkFQ24Mr5oDw4Vz4rrJUkl05pSvS2crOdbWwlT
ivopd780hXizr/7cpQJBAOUoMc9/jKJwPGxmUI2SuqIqQHCEZKAtqimbo0JAWw9f
Vd1Ef5bnCrClu5oN7PpWBsgO8muV25tF9RAASN/44I8CQQDisq2bs69wakU21kH/
9hhhjtFOyrBCGLpenySKe2NpY4l9M+axyzLens61PKMaSQ+v/jLWEWE9NEoF+zCS
BeEDAkAh1xOz/rP38PcbUuimX3qsI+0sMIDjnpq4VkF/sR1BYeS+LjVhRXEwsOjJ
UTEjrryFop9t/81UWrpbyI76dDhFAkBXuAnjWLkHB6f2mxt4vrIaddfZ5CYlvUnI
OU47Y5lI+2IFF2Mp4tWxPMQBbxAxqJmwYO0s1hbJRwVpoRPfPPUfAkEAi+aD1tyS
Tu9k1BANDiYPnNbOi42/x3xDZEeSUy2pfDIrQiOezUaXGwDxWJBngQROAkNax96f
NwyBb+a50ab25w==
-----END PRIVATE KEY-----
[root@localhost tmp]# cat pkcs1.pem 
-----BEGIN RSA PRIVATE KEY-----
MIICXAIBAAKBgQDK7W1TWrhe8yS5wp4RJw+ZoF1/6yBTb8gR/EW1KFrPClUvluX8
HlGNI8bigP3aKjFdcvGhLayPEoVXeJ1ILQjyZ7Zj3YFxuuM87SOpv0YkrolJIR1H
EKNOL6LT7RnAIMotxY6mVny1HU7R/JegDKHnLQUlhsncucM8Oa1yThNQrQIDAQAB
AoGAJBzyusXHT+Z3X7OZy7Wm2hEm8o/WdeLLuR+rDB09Y+ET3d892ItQugxm5YJN
KtkdZeMBy0xEjEWggzuMyf5xu9jHCSAKeRQJUcNKI9+PMIx0d5apSlbj5BUNuDK+
aA8OFc+K6yVJJdOaUr0tnKznW1sJU4r6KXe/NIV4s6/+3KUCQQDlKDHPf4yicDxs
ZlCNkrqiKkBwhGSgLaopm6NCQFsPX1XdRH+W5wqwpbuaDez6VgbIDvJrldubRfUQ
AEjf+OCPAkEA4rKtm7OvcGpFNtZB//YYYY7RTsqwQhi6Xp8kintjaWOJfTPmscsy
3p7OtTyjGkkPr/4y1hFhPTRKBfswkgXhAwJAIdcTs/6z9/D3G1Lopl96rCPtLDCA
456auFZBf7EdQWHkvi41YUVxMLDoyVExI668haKfbf/NVFq6W8iO+nQ4RQJAV7gJ
41i5Bwen9psbeL6yGnXX2eQmJb1JyDlOO2OZSPtiBRdjKeLVsTzEAW8QMaiZsGDt
LNYWyUcFaaET3zz1HwJBAIvmg9bckk7vZNQQDQ4mD5zWzouNv8d8Q2RHklMtqXwy
K0Ijns1GlxsA8ViQZ4EETgJDWsfenzcMgW/mudGm9uc=
-----END RSA PRIVATE KEY-----

首先直接对比私钥的参数(n, e, d, p, q……),可以看出所包含的元素是一样的

这里也可以使用ASN.1解码器解析对比,可以发现PKCS#8比PKCS#1多了一部分投信息,这部分头信息的值表示OID 1.2.840.113549.1.1.11,表明本证书使用的算法是​​sha256WithRSAEncryption​​,,而OID也有其自身的编码,这里不展开,感兴趣的小伙伴可以自行​​扩展​

综上所述,PKCS#8比PKCS#1多出一部分头信息,可以用来描述证书所使用的算法,相比于PKCS#1只能描述RSA算法,PKCS#8还可以描述诸如​​ECC​​(椭圆曲线加密算法)的私钥

这串编码的解释:https://www.rfc-editor.org/rfc/rfc5754

30 0D 06 09 2A 86 48 86 F7 0D 01 01 01 05 00 04

小结
  1. 这一节以RSA算法常见的PEM私钥文件为例,讲述了基于DER编码规则的PKCS #1标准,文档为RFC8017
  2. 而DER编码是BER的子集,都是基于ASN.1数据结构,都是以《Type,Length,Value》的三元组标识数据
  3. ssh-keygen和openssl命令创建的公钥文件有些不同,ssh-keygen创建的公钥是OpenSSH格式,以空格分割三部分:算法名称,公钥,注释
  4. 采用PEM格式的RSA证书,几乎都是以“MII”开头,原因和DER编码转换为base64的对应关系有关
  5. PKCS#8相对于PKCS#1是扩展增强的关系,PKCS#1推出的时候只有RSA算法相对成熟,因此只能用来描述RSA算法;而PKCS#8可以描述其他算法的私钥

概念梳理

读到这里相信各位小伙伴已经对RSA密钥的文件格式有一定了解,对于PKCS#1,PKCS#8,ASN.1,BER,DER等概念也有一定认识,下面让我们来再梳理一下容易混淆的几个概念。

回答几个问题:
  1. ASN.1和DER,PEM,RSA,openssl之间的区别和联系是怎样的?
  2. 为什么有的公钥以“-----BEGIN PUBLIC KEY-----”开头,而有的以“-----BEGIN RSA PUBLIC KEY-----”,私钥同问?

各名词之间的区别与联系
  1. ASN.1定义了数据结构,但没有定义如何编码;
  2. 而DER则是编码规则,以<Type, Length, Value>的三元组标识一个数据;
  3. PEM则是为了方便传输和阅读,对数据进行base64编码;
  4. RSA是一种非对称加密算法,PEM证书文件的内容是RSA算法的参数,包括n,e,d,p,q等;
  5. PKCS是密钥存储标准,公私钥都可,定义了哪部分是n,哪部分是e,d,间隔的规则就是DER和ASN.1;
  6. PKCS#1只能描述RSA算法,而PKCS#8是增强版,可以描述其他算法的私钥钥;
  7. PKCS#8定义了通用的私钥格式,而PKIX定义了通用的公钥格式;
  8. SSL和TLS都是安全认证协议,TLS是SSL的升级,之所以现在还叫SSL证书,是历史名称,实际上已经是“TLS”证书了;
  9. SSH是加密的shell,OpenSSH是对SSH协议的免费开源实现,OpenSSH使用OpenSSL提供的库;
  10. OpenSSL是一个C语言函数库,是对SSL协议的实现,linux中有个命令也叫openssl,可以用来生成证书文件;
# ASN.1
Point ::= SEQUENCE {
  x INTEGER,
  y INTEGER,
  label UTF8String
}

# DER编码,坐标(9,9)
30 06 80 01 09 81 01 09

证书文件的开头

常见的私钥开头有“-----BEGIN RSA PRIVATE KEY-----”和“-----BEGIN PRIVATE KEY-----”两种,从字面上来看,前者特指RSA私钥,而后者只是表明这是个私钥而已;

# PKCS#1私钥
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEApwh…………
-----END RSA PRIVATE KEY-----

# PKCS#1公钥
-----BEGIN RSA PUBLIC KEY-----
MIIBCgKCAQEApwhprohZAq4……
-----END RSA PUBLIC KEY-----

# PKCS#8私钥
-----BEGIN PRIVATE KEY-----
MIICdwIBADANBgkqh…………
-----END PRIVATE KEY-----

# PKIX公钥
-----BEGIN PUBLIC KEY-----
MIIGfMA0GCSqGSIb3D……
-----END PUBLIC KEY-----

# SSH公钥
ssh-rsa AAAAB3NXRvRv9mw== vagrant@stretch

从解码器上直接对比,可以看到PKCS#8多一个算法标识部分,这也是它可以存储其他算法秘钥的原因;

同理,公钥文件采用PKCS#1格式和PKIX格式的区别也是类似,可以通过开头的注释判断;而ssh-keygen创建的是SSH格式的公钥,没有使用ASN.1和DER编码

总结

  1. RSA算法是常用的非对称加密算法,在​​上一篇​​中我们了解了RSA算法的原理和简单实现,而在这一篇中我们了解了RSA证书的文件格式;
  2. 一个证书文件需要涉及到数据结构(ASN.1)和编码规则(DER),为了方便使用还需要指定各参数的位置(PKCS,PKIX);
  3. 而为了方便阅读和传输,还需要对二进制数据进行编码(DER,base64);
  4. 我们平时会使用一些工具和命令来生成证书文件(ssh-keygen,openssl),在互联网上有许多协议会使用这些证书文件做加解密和签名验证(SSL,TLS,SSH,OpenSSH,HTTPS);
  5. 下一篇我们将以常见的HTTPS协议为例,讲解为什么要使用非对称加密,以及如何使用,敬请期待~~

PS:这两篇前前后后写了好几天,原本是打算直接写HTTPS的,但发现相关概念有些多,还是先梳理梳理

参考文档

  1. 密码学基础3:密钥文件格式完全解析:https://www.jianshu.com/p/ce7ab5f3f33a
  2. RSA密钥、加密和数字签名:https://juejin.cn/post/6844903881655369742
  3. ASN.1 和 DER:https://letsencrypt.org/docs/a-warm-welcome-to-asn1-and-der/
  4. RFC8017:https://www.rfc-editor.org/rfc/rfc8017
  5. RFC5958:https://www.rfc-editor.org/rfc/rfc5958
  6. RFC5754:https://www.rfc-editor.org/rfc/rfc5754
  7. ASN.1解码器:https://lapo.it/asn1js/
  8. base64编码解码器:https://the-x.cn/base64
  9. RSA密钥在线生成器:https://www.bejson.com/enc/rsa/
  10. 解读RSA公钥私钥储存格式:https://www.modb.pro/db/126884
  11. ssh-keygen生成的id_rsa文件的格式:https://zhuanlan.zhihu.com/p/33949377
  12. 秘钥格式证书:http://douxinchun.github.io/blog/20180718/rsa-key-structuress.html
  13. RSA密钥长度、明文长度和密文长度:​​https://cloud.tencent.com/developer/article/1199963​
  14. 为什么RSA 公钥指数(e=65537):​​https://blog.csdn.net/hherima/article/details/52461759​
  15. PKCS1与PKCS8的小知识:https://www.jianshu.com/p/a428e183e72e
  16. 各种格式SSH 公钥和私钥之间的转换:https://blog.csdn.net/stevensxiao/article/details/109381001
  17. RSA公私钥pkcs1与pkcs8格式的转换:https://blog.csdn.net/boweiqiang/article/details/116309452
  18. SSL和SSH和OpenSSH,OpenSSL有什么区别:​​https://www.cnblogs.com/foohack/p/4103212.html​
  19. TLS与SSL有什么区别?你应该使用哪一个?:https://www.wbolt.com/tls-vs-ssl.html
  20. X.690:https://www.itu.int/rec/T-REC-X.690
  21. 椭圆曲线ECC密钥:https://crypto.stackexchange.com/questions/68099/how-is-ec-key-encoded-in-pkcs8

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值