X.509证书的读取操作与分析(Python版)

X.509

1. 内容解析

该部分所使用知识均来自于维基百科CSDN


1.1 定义

X.509 是密码学里公钥证书的格式标准。

X.509 证书己应用在包括 TLS/SSL在内的众多 Intenet 协议里.同时它也用在很多非在线应用场景里,比如电子签名服务。

X.509证书里含有公钥、身份信息(比如网络主机名,组织的名称或个体名称等)和签名信息(可以是证书签发机构CA的签名,也可以是自签名)。对于一份经由可信的证书签发机构签名或者可以通过其它方式验证的证书,证书的拥有者就可以用证书及相应的私钥来创建安全的通信,对文档进行数字签名.

另外除了证书本身功能,X.509还附带了证书吊销列表和用于从最终对证书进行签名的证书签发机构直到最终可信点为止的证书合法性验证算法。


1.2 组成结构

  • 证书

    • 版本号

      • 作用:【标识证书的版本(版本1、版本2、或是版本3)】
    • 序列号

      • 作用:【标识证书的唯一整数,由证书颁发者分配的本证书的唯一标识符】
    • 签名算法

      • 作用:【由于签名书的算法标识,由对象标识符加上相关的参数组成,用于说明本证书所用的数字签名算法。例如,SHA-1 和 RSA 的对象标识符就用来说明该数字签名是利用 RSA 对SHA-1 杂凑加密】
    • 颁发者

      • 作用:【证书颁发者的可识别名】
    • 证书有效期

      • 作用:【证书的有效期时间段】
        • “Not Before” 此日期前无效
        • “Not After” 此日期后有效
        • 以上二者分别由 UTC时间或一般的时间表示
    • 主体

      • 作用:【证书拥有者的可识别名,这个字段必须是非空的,除非你在证书扩展中有别名】
    • 主体公钥信息

      • 作用:【标识主题的公钥以及算法标识符】
        • 公钥算法
        • 主题公钥
    • 颁发者唯一身份信息(可选项)

      • 作用:证书颁发者的唯一标识符,仅在版本 2 与版本 3 中有要求,属于可选项
    • 主题唯一身份信息

      • 作用:证书拥有者的唯一标识符,仅在版本 2 和版本 3 中有要求,属于可选项
    • 扩展信息 (次重点部分

      • 发行者秘钥标识符

        • 作用:【证书所含密钥的唯一标识符,用来区分同一证书拥有者的多对密钥】
      • 秘钥使用

        • 作用:【一个比特串,指明(限定)证书的公钥可以完成的功能或服务,如:证书签名、数据加密等。

          如果某一证书将 KeyUsage 扩展标记为“极重要”,而且设置为“keyCertSign”,则在 SSL 通信期间该证书出现时将被拒绝,因为该证书扩展表示相关私钥应只用于签写证书,而不应该用于 SSL。】

      • CRL 分布点

        • 作用:【指明 CRL 的分布地点】
      • 私钥的使用期

        • 作用:【指明证书中与公钥相联系的私钥的使用期限,它也由 “ Not Before “ 和" Not After "组成。若此项不存在时,公私钥的使用期是一样的】
      • 证书策略

        • 作用:【由对象标识符和限定符组成,这些对象标识符说明证书的颁发和使用策略有关】
      • 策略映射

        • 作用:【表明两个 CA 域之间的一个或多个策略对象标识符的等价关系,仅在 CA 证书里存在】
      • 主体别名

        • 作用:【指出证书拥有者的别名,如电子邮件地址、IP 地址等,别名是和 DN 绑定在一起的】
      • 颁发者别名

        • 作用:【指出证书颁发者的别名,如电子邮件地址、IP地址等,但颁发者的 DN 必须出现在证书的颁发者字段】
      • 主体目录属性

        • 作用:【指出证书拥有者的一系列属性。可以使用这一项来传递访问控制信息】
  • 证书签名算法

  • 数字签名


1.3 安全性

  • 采用黑名单方式的证书吊销列表 CRL 和在线证书状态协议( OCSP )
    • 如果客户端仅信任在CRL可用的时候信任证书,那就失去离线信任的需求。因此通常客户端会在CRL不可用的情况下信任证书,因而给了那些可以控制信道的攻击者可乘之机。如谷歌的Adam Langley所说,对CRL的检查有如你期望安全带在出事故事一定能正常使用的
  • 在大范围及复杂的分布模式下选用CRL并不明智
  • OCSP由于没有吊销状态的历史记录也会出现歧义
  • 聚合问题
  • 代表问题: 证书颁发机构事没办法限制其下属颁发的证书作出名字及属性方面的限制。而且在Internet上存在着相当多的证书颁发机构,想对他们进行分类和策略上的限制是一项不可能完成的任务。
  • 分布问题: 证书链引的下属颁发机构,桥接颁发机构以及交叉认证使得证书验证变得非常复杂,需要付出很大的代价。层次式的第三方信任模型作为一种唯一的模型的话,路径验证也可能出现含糊不明的情况岐义,这对于已经创建双边信任也很不方便。
  • 发布一个对主机名的扩展验证并不能防止再发布一个验证要求低一些的适用于同一个主机名的证书。这就造成了不能对中间人攻击的有效保护

1.4 证书文件名扩展类型

X.509有多种常用的扩展名。不过其中的一些还用于其它用途,就是说具有这个扩展名的文件可能并不是证书,比如说可能只是保存了私钥。

  • .pem– (隐私增强型电子邮件) DER编码的证书再进行 Base64 编码的数据存放在"-----BEGIN CERTIFICATE-----“和”-----END CERTIFICATE-----"之中
  • .cer, .crt, .der – 通常是DER二进制格式的,但 Base64 编码后也很常见。
  • .p7b, .p7cPKCS#7
    • 注:PKCS#7 是签名或加密数据的格式标准,官方称之为容器。由于证书是可验真的签名数据,所以可以用 SignedData 结构表述。
    • 注:P7C文件是退化的 SignedData 结构,没有包括签名的数据。
  • .p12PKCS#12格式,包含证书的同时可能还有带密码保护的私钥
    • 注:PKCS#12PFX 进化而来的用于交换公共的和私有的对象的标准格式。
  • .pfxPFX,PKCS#12之前的格式(通常用 PKCS#12 格式,比如那些由 IIS 产生的 PFX 文件)

2. 数据结构

2.1 编码

X.509证书的结构使用ASN1进行描述数据结构,并进行编码;

ASN1采用一个个的数据块来描述整个数据结构,整个数据块都由四部分组成:

  • 数据块数据类型标识(一个字节)

    • 数据类型包括简单类型和结构类型

      • 简单类型:整型、比特串、日期型等
      • 结构类型:顺序类型、选择类型、集合类型
    • 数据块数类型标识的一个字节结构如下:

      • bit8 - bit7 标识 TAG 类型 ,共四种类型,universal(00)、application(01)、context-specific(10)、private(11)
      • bit6 表示是否为结构类型
      • bit5 - bit1是类型的 TAG 值,根据bit8 - bit7值为universal(00)时,bit5-bit1表示不同的universal值,如图:
        • 在这里插入图片描述
        • 在这里插入图片描述
      • bit8-bit7context-specific时,bit5-bit1的值表示
        • [0] -> 证书的版本
        • [1] -> issuerUniqueID,表示证书发行者的唯一 id
        • [2] -> subjectUniqueID,表示证书主体的唯一 id
        • [3] -> 表示证书的扩展字段
    • 举例,如:

      ​ DER二进制文件的前4个字节构成具有剩余字节的ASN.1序列。

      ​ 如30 82 06 E1根据 Type-Length-Value表示,第一个字节30(00110000)表示是一个universal class type,后面接着一个结构化类型的SEQUENCE。

  • 数据块长度( 1 - 128 个字节),有三种编码格式

    • 长度小于等于 127 ,用一个字节表示
    • 长度大于 127 ,用多个字节表示
    • 字节为 0x80,表示数据块长度不定,由数据块结束标识结束数据块
  • 数据块的值

    • 存放数据块的值,具体编码随数据块类型的不同而不同
  • 数据块结束标识(可选)

    • 结束标示字段,两个字节(0x000),只有在长度不定时才会出现

2.2 主体结构

Certificate ::= SEQUENCE {
        
        tbsCertificate       TBSCertificate, -- 证书主体
        
        signatureAlgorithm   AlgorithmIdentifier, -- 证书签名算法标识
        
        signatureValue       BIT STRING --证书签名值,是使用signatureAlgorithm部分指定的签名算法对tbsCertificate证书主题部分签名后的值.
}

TBSCertificate ::= SEQUENCE {
        version         [0] EXPLICIT Version DEFAULT v1, -- 证书版本号
        
        serialNumber         CertificateSerialNumber, -- 证书序列号,对同一CA所颁发的证书,序列号唯一标识证书
        
        signature            AlgorithmIdentifier, --证书签名算法标识
        
        issuer               Name,                --证书发行者名称
        
        validity             Validity,            --证书有效期
        
        subject              Name,                --证书主体名称
        
        subjectPublicKeyInfo SubjectPublicKeyInfo,--证书公钥
        
        issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL,
                             -- 证书发行者ID(可选),只在证书版本2、3中才有
        
        subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL,
                             -- 证书主体ID(可选),只在证书版本2、3中才有
        
        extensions      [3] EXPLICIT Extensions OPTIONAL
                             -- 证书扩展段(可选),只在证书版本3中才有
}




2.3 Version

证书的版本号:0,1,2

Version ::= INTEGER { 
	v1(0), -- 对应值为 0 ,证书只有基本项时为V1
	v2(1), -- 对应值为 1 ,证书除了基本项之外,还包含了签发者 ID 与使用者 ID
	v3(2) -- 证书中包含扩展项
}

2.4 CertificateSerialNumber

证书的序列号:一串整数,对于每一张证书,证书序列号均唯一

CertificateSerialNumber ::= INTEGER 

2.5 AlgorithmIdentifier

算法结构标识,用来给定具体的算法类型。

AlgorithmIdentifier ::= SEQUENCE {
        algorithm               OBJECT IDENTIFIER,
        parameters              ANY DEFINED BY algorithm OPTIONAL 
}

2.6 Validity

有效期:起始与终止时间

Validity ::= SEQUENCE {
        notBefore      Time,  -- 证书有效期起始时间
        notAfter       Time  -- 证书有效期终止时间         
}  

2.7 PublicKey

公钥信息

RSAPublicKey ::= SEQUENCE { -- RSA算法时的公钥值
         modulus            INTEGER, -- n
         publicExponent     INTEGER -- e -- 
}

2.8 UniqueIdentifier

由于签发者和使用者的类型都是一样的,是一些DN项(Distinguish Name)的集合

AttributeTypeAndValue ::= SEQUENCE {
		 type OBJECT IDENTIFIER,
 		 value ANY DEFINED BY type 
}

常见的DN项有:

属性类型名称 含义 简写
Common Name 通用名称 CN
Organizational Unit name 机构单元名称 OU
Organization name 机构名 O
Locality 地理位置 L
State or province name 州/省名 S
Country 国名 C

2.9 SubjectPublicKeyInfo

定义公钥类型信息:

 SubjectPublicKeyInfo ::= SEQUENCE {
        algorithm            AlgorithmIdentifier, -- 公钥算法
        subjectPublicKey     BIT STRING            -- 公钥值
 }

2.10 Extension

可省略的扩展类型:

Extensions ::= SEQUENCE SIZE (1..MAX) OF Extension
Extension ::= SEQUENCE {
        extnID      OBJECT IDENTIFIER,
        critical    BOOLEAN DEFAULT FALSE,
        extnValue   OCTET STRING 
}

3. 读取操作程序

3.1 语言选择

语言:Python

选择原因:

  • 第一次在写的时候,由于直接使用了 Java的基本类库对证书进行操作,虽然能够正常输出所需要的结果,但是并没有达到完成这次作业应有的目的,也没有对X.509证书的数据结构有一个直观的认识。
  • 在查询了X.509证书的数据结构之后,从新使用python进行编程,不依附现有的类库,读入证书并进行文件解析。
  • 使用python的好处在于可以方便进行字符串的操作,同时使用Spyder编译器可以显示出程序的函数调用过程,方便debug

3.2 编译环境

操作系统: windows 10

条件:

  • 安装 Anaconda(一个大型的 python 集成环境,可以根据需要构建 python 2python 3独立的编译环境)
  • Spyder 编译器
  • 生成der类型的X.509证书

3.3 证书读取

使用pythonopen()函数,以可读方式打开证书:

filename = 'test.der'
f = open(filename,'rb')

3.4 类型判断

X.509证书在存储时候是按照字节存储的,同时我们需要根据2.1中的数据标识类型来对具体的某个段进行操作,所以方法具体如下:

data = f.read(1)
type = ord(date);#将字节转为对应的数字
if type < 0x80: #那么则证明满足 universal 类型,可采用对应操作方法
elif type >= 0x80 and type < 0xa0:#隐式 Tag 操作类型
elif type >= 0x00: #显式 Tag 操作类型 

3.5 待处理数据长度判断

这步主要是对X.509type > 127 的数据进行操作,比如:当我们解析公钥时候,就需要进行多字节操作

if type > 0x80: 
        type -= 0x80
        for i in range(type):
            length *= 256
            length += ord(f.read(1))
else:
        length = type

3.6 处理各种类型数据

我们需要根据数据类型的长度,来选定我们在2.3-2.10中使用的数据类型,进而完成对数据的输出:

比如,Interger类型:

res_string = ""
nextByte = ord(f.read(1))
length = parse_Length(nexttype)
for i in range(0, length):
  res_string += hex(ord(f.read(1)))[2:]

其他类型的转换类似。

3.7 程序输出

在上一步对所有的数据进行处理之后,我们可以将处理过的数据依次输出了,比如对序列号的输出:

choice = INTEGER_CHIOCE.get()

if choice == "Serial Number":
            temp = ""
            
            for i in range(0,len(parse_string)-3):
                
                temp +=  parse_string[i:i+2] + ":"
            
            temp += parse_string[len(parse_string)-2:len(parse_string)]
            
            print(choice,":\n",temp,"\n")
		
elif choice != "Default Version":
            
        	print(choice,": ",Version[parse_string],"\n")

4. 编译运行

4.1 Spyder下程序运行过程

在这里插入图片描述

上图为程序中的函数调用过程;

4.2 运行结果

4.2.1 证书生成

  • 生成一个2048位的密钥
    • 指令:openssl genrsa -des3 -out privkey.pem 2048
  • 生成一个证书请求
    • 指令:openssl req -new -key privkey.pem -out cert.csr
  • 生成cert.der证书
    • 指令:openssl x509 -outform der -in cert.pem -out cert.der

4.2.2 运行结果

对上一步生成的证书进行解析,结果如图:

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

注:

  • Version : 3
  • Serial number : 9d:dd:d1:14:4c:c7:7a:a8:85:55:5c:c0:0d:d6:69:97:7f:f9:92:22:22:26:64:48:84:47:7f:8f
  • Signature Algorithm : sha1WithRSAEncryption
  • Validity:
    • Not Before : 130514000000Z
    • Not After : 160518120000Z
  • Issuer:
    • Country Name:US
    • Organization Name:DigiCert Inc
    • Organizational Unit Name:www.digicert.com
    • Common Name:DigiCert High Assurance CA-3
  • Subject Public Key Info RSAEncryption:1.2.840.113549.1.1.1
  • Subject Public Key Encryption : 略
  • Signature Algorithm:sha1WithRSAEncryption
  • SIGNATURE Encryption : 略

5. 参考文献


猜你喜欢

转载自blog.csdn.net/wyxwyx469410930/article/details/85036178