想要了解证书,必须先了解ASN.1和编码规则。这篇文章简单介绍ASN.1,不过分探讨细节,大家如果有兴趣可以继续深入研究。
一、ASN.1
ASN.1是Abstract Syntax Notation One(抽象文法描述语言)的缩写。计算机系统之间交换的数据消息如果用ASN.1进行描述,可以减少了双方的沟通成本。
ASN.1作为抽象描述文法,将现有的数据类型抽象描述成近20种数据类型。这些数据类型主要分为两大类:基本类型和结构类型。ASN.1的数据类型几乎概括了现实世界中存在的所有数据类型,具有相当的通用性。
基本类型又称为原子类型,是构成其他结构类型的成员类型,如
-
布尔值类型:BOOLEAN
-
整型:INTEGER
-
字符串:OCTET STRING
-
空:NULL
结构类型又称为复合类型,如
-
有序成员固定结构:SEQUENCE
-
无序成员固定结构:SET
1.1实战
通过一个实例我们看看怎样使用ASN.1。
我们先创建一个ASN1格式的schema,该schema表示用户信息,里面有用户名和用户描述。然后填写数据,将数据进行编解码。大家可以用https://asn1.io/asn1playground/default.aspx来做实验。
1.1.1schema
首先创建改结构对应的ASN.1描述。
--<ASN1.HugeInteger World-Schema.Rocket.range>--
World-Schema DEFINITIONS AUTOMATIC TAGS ::=
BEGIN
Rocket ::= SEQUENCE
{
name UTF8String (SIZE(1..16)),
message UTF8String (SIZE(1..16))
}
END
1.1.2设置数据
有了schema后,我们对该结构进行赋值,可以使用ASN.1格式或json格式赋值。
# ASN.1格式
value Rocket ::= {
name "Falcon",
message "hh"
}
# Json
{
"name":"Falcon",
"message":"hh"
}
1.1.3编码
然后我们将数据编码为需要的格式,如ber、der、per、uper、oer、coer、xml等,编码结果如下所示:
hh.PDU.ber
hh.PDU.der
hh.PDU.xml
链接: https://pan.baidu.com/s/1Zz8dJCErDe1Fiz57oQF4Pg 密码: 2wdr
1.1.4解码
我们尝试解码der文件,如果没有schema,解析的内容如下图所示:
如果有对应的schema,解析出的内容存放在txt或json中:
hh.PDU.der.json
hh.PDU.der.txt
链接: https://pan.baidu.com/s/1Zz8dJCErDe1Fiz57oQF4Pg 密码: 2wdr
1.2代码实现
如果用代码,如何实现上述功能呢?本次用php实现,大家需要安装https://github.com/fgrosse/PHPASN1。
代码主要包含4部分内容:
-
先对结构进行赋值
-
然后将数据进行der编码和base64编码
-
编写该结构对应的schema(这个其实可以放在最开始编写)
-
将编码后的数据进行解析(支持base64编码或der编码的数据),解析完成后,便可获取各个数据
<?php
#namespace FG\Test\ASN1;
namespace FG\Test\ASN1;
use FG\ASN1\Identifier;
use FG\ASN1\TemplateParser;
use FG\ASN1\Universal\BitString;
use FG\ASN1\Universal\Integer;
use FG\ASN1\Universal\ObjectIdentifier;
use FG\ASN1\Universal\Sequence;
use FG\ASN1\Universal\Set;
use FG\ASN1\Universal\CharacterString;
use FG\ASN1\Universal\UTF8String;
//use PHPUnit_Framework_TestCase;
class TemplateParserTest //extends PHPUnit_Framework_TestCase
{
public function testParseBase64()
{
#1.赋值
$sequence = new Sequence(
// new Set(
//new ObjectIdentifier('ID Number'),
new UTF8String("Falcon"),
new UTF8String("hh")
// )
);
#2.der编码后,做base64编码
$data = base64_encode($sequence->getBinary());
var_dump($sequence->getBinary(),$data);
#3.schema结构
$template = [
Identifier::SEQUENCE => [
// Identifier::SET => [
//Identifier::OBJECT_IDENTIFIER,
Identifier::UTF8_STRING,
Identifier::UTF8_STRING,
// ]
]
];
#4.解析
$parser = new TemplateParser();
$object = $parser->parseBase64($data, $template);
var_dump($object);
var_dump("-------------");
var_dump($object->getChildren()[0]->getContent());
var_dump("-------------");
// $this->assertInstanceOf(Set::class, $object[0]);
// $this->assertInstanceOf(ObjectIdentifier::class, $object[0][0]);
// $this->assertInstanceOf(Sequence::class, $object[0][1]);
// $this->assertInstanceOf(Integer::class, $object[0][1][0]);
// $this->assertInstanceOf(BitString::class, $object[0][1][1]);
$object = $parser->parseBinary($sequence->getBinary(),$template);
var_dump($object);
}
}
require_once __DIR__ . '/vendor/autoload.php';
$parse = new TemplateParserTest();
$parse->testParseBase64();
输出如下:
➜ myproject php asn1.php
string(14) "0
Falcon
hh"
string(20) "MAwMBkZhbGNvbgwCaGg="
object(FG\ASN1\Universal\Sequence)#8 (4) {
["children":protected]=>
array(2) {
[0]=>
object(FG\ASN1\Universal\UTF8String)#9 (5) {
["value":protected]=>
string(6) "Falcon"
["checkStringForIllegalChars":"FG\ASN1\AbstractString":private]=>
bool(false)
["allowedCharacters":"FG\ASN1\AbstractString":private]=>
array(0) {
}
["contentLength":"FG\ASN1\ASNObject":private]=>
int(6)
["nrOfLengthOctets":"FG\ASN1\ASNObject":private]=>
int(1)
}
[1]=>
object(FG\ASN1\Universal\UTF8String)#10 (5) {
["value":protected]=>
string(2) "hh"
["checkStringForIllegalChars":"FG\ASN1\AbstractString":private]=>
bool(false)
["allowedCharacters":"FG\ASN1\AbstractString":private]=>
array(0) {
}
["contentLength":"FG\ASN1\ASNObject":private]=>
int(2)
["nrOfLengthOctets":"FG\ASN1\ASNObject":private]=>
int(1)
}
}
["iteratorPosition":"FG\ASN1\Construct":private]=>
int(2)
["contentLength":"FG\ASN1\ASNObject":private]=>
int(12)
["nrOfLengthOctets":"FG\ASN1\ASNObject":private]=>
int(1)
}
string(13) "-------------"
string(6) "Falcon"
string(13) "-------------"
object(FG\ASN1\Universal\Sequence)#11 (4) {
["children":protected]=>
array(2) {
[0]=>
object(FG\ASN1\Universal\UTF8String)#12 (5) {
["value":protected]=>
string(6) "Falcon"
["checkStringForIllegalChars":"FG\ASN1\AbstractString":private]=>
bool(false)
["allowedCharacters":"FG\ASN1\AbstractString":private]=>
array(0) {
}
["contentLength":"FG\ASN1\ASNObject":private]=>
int(6)
["nrOfLengthOctets":"FG\ASN1\ASNObject":private]=>
int(1)
}
[1]=>
object(FG\ASN1\Universal\UTF8String)#13 (5) {
["value":protected]=>
string(2) "hh"
["checkStringForIllegalChars":"FG\ASN1\AbstractString":private]=>
bool(false)
["allowedCharacters":"FG\ASN1\AbstractString":private]=>
array(0) {
}
["contentLength":"FG\ASN1\ASNObject":private]=>
int(2)
["nrOfLengthOctets":"FG\ASN1\ASNObject":private]=>
int(1)
}
}
["iteratorPosition":"FG\ASN1\Construct":private]=>
int(2)
["contentLength":"FG\ASN1\ASNObject":private]=>
int(12)
["nrOfLengthOctets":"FG\ASN1\ASNObject":private]=>
int(1)
}
二、编码规则
ASN.1应用越来越广泛的主要原因之一是这种抽象文法描述与几种标准化编码规则联系在一起。这些编码规则规定了如何在非传输介质下实现ASN.1定义的数据类型与字节流的相互转换。因为编码规则是针对ASN.1的数据类型而制定的,因此可以称这些编码规则为ASN.1的编码规则。
ASN.1的编码规则是把使用ASN.1语言说明的数据转化成一种标准格式的系列规则,同时,保证转换后的数据在任意操作系统中,只要使用相同编码规则的解码器就可以解码获得原始数据。
目前,ASN.1的标准化编码规则有以下几种:BER(Basic Encoding Rules),DER(Distinguished Encoding Rules),PER(Packed Encoding Rules)和CER(Canonical Encoding Rules)等。我们简单介绍一下BER(基本编码规则)和DER(定长编码规则)。
2.1BER
BER编码信息由以下几部分组成:
① 标识串,表示要编码的 ASN.1 类型的标识类和标识码以及使用的编码方法(是简单型还是结构型)。
② 长度串,定长型编码方法中它表示内容串的长度,非定长编码方法中它表示长度不定。
③ 内容串,简单定长型编码方法中它表示要编码类型值的具体内容,结构型编码方法中表示各个成员编码的串联。
④ 内容结束串,只有在结构非定长型编码方法中表示内容串的结束,其他方法中该串省略。
对一个 ASN.1 对象的 BER 编码有三种模式,使用哪一种模式取决于该对象的类型和该类型数据的长度是否已知,
不同模式下编码信息中每个组成部分的编码规则不同。主要为基本类型定长模式、结构类型定长模式、结构类型非定长模式。
hh.PDU.ber
链接: https://pan.baidu.com/s/1Zz8dJCErDe1Fiz57oQF4Pg 密码: 2wdr
2.2DER
DER编码是ASN.1数据类型中具有唯一编码的编码规则。DER编码是BER编码的子集,是将每一个ASN.1抽象对象
类型表示成唯一的1和0码字符串的编码规则。这种编码规则是为需要编码成唯一比特串的应用系统而制定的,特
别是在应用安全技术的应用系统中,由于安全加密技术要求输入数据是字节流的形式,并且是与原数据唯一对应的
字节流,因此需要使用DER编码来实现数据结构的编码。
DER编码称为有关安全技术的应用系统的最佳选择。它基本上继承了BER编码规则,同样,也有三种编码方法。但
为了保证编码结果的唯一性,DER 编码在 BER 编码规则的基础上又附加了一些规则:
① 必须使用定长模式编码。
② 对于内容长度小于127的类型值,长度串编码必须采用短型。
③ 对于内容长度大于128的类型值,长度串编码必须采用长型,同时长度串编码的字节个数必须是最少的。
④ 对于简单字符串类型和从简单字符串类型通过隐式派生得到的类型,必须使用基本类型定长模式编码方法。
⑤ 对于结构类型、从结构类型通过隐式派生得到的类型,以及从任何类型通过显示派生得到的类型,必须使用结
构类型定长模式编码方法。
hh.PDU.der
链接: https://pan.baidu.com/s/1Zz8dJCErDe1Fiz57oQF4Pg 密码: 2wdr
如果Name类型ASN.1描述如下图:
将数据使用基本类型定长模式编码后的结果为:
三、使用
现在很多内容都使用ASN.1描述,如密码算法、数字证书等。
3.1密码算法ASN.1描述
PKCS #7 v1.5描述了密码消息的通用语法。该语法允许嵌套,如一个数字信封可以包含另一个数字信封,或可以对已做数字信封的数据进行签名;该语法也允许扩展各种属性,还可以用于分发证书和CRL。PKCS #7与PEM兼容,可以直接将加密的消息转换成PEM消息,反之亦然。PKCS #7支持多种基于证书的管理系统,PEM就是其中之一。在RFC 5652中有增强定义。该标准主要包括消息通用语法和6种内容类型(明文、签名、信封、签名信封、摘要、密文)。
① 消息通用语法用ASN.1描述如下:
Contentlnfo::= SEQUENCE{
contentType ContentType,
content
[0] EXPLICIT ANY DEFINED BY contentType OPTIONAL
}
ContentType:= OBJECT IDENTIFIER
② 明文消息内容用ASN.1描述如下:
Data ::= OCTET STRNG
③ 签名消息内容用ASN.1描述如下:
SignedData ::= SEQUENCE {
version Version,
digestAlgorithms DigestAlgorithmldentifiers,
contentInfo Contentlnfo,
certificates [0] IMPLICIT ExtendedCertificatesAndCertificates
OPTIONAL,
Crls [1] IMPLICIT CertificateRevocationLists OPTIONAL,
signerlnfos Signerlnfos
}
DigestAlgorithmldentifiers ::= SET OF DigestAlgorithmldentifier
Signerlnfos ::= SET OF Signerlnfo
Signerlnfo ::= SEQUENCE {
version Version,
issuerAndSeriaINumber IssuerAndSerialNumber,
digestAlgorithm DigestAlgorithmldentifier,
authenticatedAttributes [0] IMPLICIT Attributes OPTIONA L,
digestEncryptionAlgorithm DigestEncryptionAIgorithmldentitler,
encryptedDigest EncryptedDigest,
unauthenticatedAttributes [1] IMPLJC.IT Attributes OPTIONAL
}
EncryptedDigest ::= OC.TET STRING
Digestlnfo ::= SEQUENCE {
digestAlgorithum DigestAlgorithmldentifier,
digest Digest
}
Digest ::= OCTET STRING
④ 信封消息内容用ASN.1描述如下:
EnvelopedData ::= SEQUENCE {
version Version,
recipientlnfos Recipientlnfos.
encryptedContentlnfo EncryptedContentlnfo
}
Recipientlnlos ::= SET OF Recipientlnfo
EncryptedContentInfo ::= SEQUENCE {
contentType ContentType,
contentEncryptionAlgorithm
ContentEncryptionAlgorithmldentifier,
encryptedContent [0] IMPLICLT EncryptedContent OPTIONAL
}
EncryptcdContenr ::= OCTET STRING
Recipientlnfo ::= SEQUENCE {
version Version,
issuerAndSerialNumber IssuerAndSerialNumber,
keyEncryptionAlgorithm
KeyEncryptionAlgorithmldentifier,
encryptedKey EncryptedKey
}
EncryptedKey ::= OCTET STRING
⑤ 签名信封消息内容用 ASN.1描述如下:
SignedAndEnvelopedData ::= SEQUENCE {
version Version,
recipientlnfos Recipientlnfos,
digestAlgorithms DigestAlgorithmldetifiers,
encryptedContentlnfo EncryptedComentlnfo,
certificates [0] IMPLICIT ExtendedCertificatesAndCertificates
OPTIONAL,
Crls [1] IMPLICIT CertificateRevocationLists OPTIONAL,
signerlnfos Signerlnfos
}
⑥ 摘要消息内容用 ASN.1描述如下:
DigestedData ::= SEQUENCE{
version Version,
digestAlgorithm DigestAlgorithmldentifier,
contentlnfo Contentlnfo,
digest Digest
}
Digest ::= OCTET STRING
⑦ 密文消息内容用ASN.1描述如下:
EncryptedData ::= SEQUENCE {
version Version,
encryptedContentlnfo EncryptedContentlnfo
}
3.2数字证书ASN.1描述
X.509数字证书用ASN.1描述如下:
Certificate ::= SEQUENCE {
tbsCertificate TBSCertificate,
signatureAlgorithm AlgorithmIdentifier,
signature BIT STRING
}
TBSCertificate ::= SEQUENCE {
version [0] EXPLICIT Version DEFAULT v1,
serialNumber CertificateSerialNumber,
signature AlgorithmIdentifier,
issuer Name,
validity Validity,
subject Name,
subjectPublicKeyInfo SubjectPublicKeyInfo,
issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL,
-- If present, version must be v2or v3
subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL,
-- If present, version must be v2or v3
extensions [3] EXPLICIT Extensions OPTIONAL
-- If present, version must be v3
}
Version ::= INTEGER {
v1(0), v2(1), v3(2)
}
CertificateSerialNumber ::= INTEGER
Validity ::= SEQUENCE {
notBefore CertificateValidityDate,
notAfter CertificateValidityDate
}
CertificateValidityDate ::= CHOICE {
utcTime UTCTime,
generalTime GeneralizedTime
}
UniqueIdentifier ::= BIT STRING
SubjectPublicKeyInfo ::= SEQUENCE {
algorithm AlgorithmIdentifier,
subjectPublicKey BIT STRING
}
Extensions ::= SEQUENCE OF Extension
Extension ::= SEQUENCE {
extnID OBJECT IDENTIFIER,
critical BOOLEAN DEFAULT FALSE,
extnValue OCTET STRING
}
四、总结
通过这篇文章,大家应该能够了解ASN.1的价值和使用方法。
发送/接受方或标准化组织等制定好对应的schema,使用方按照该schema进行赋值,然后将数据转换为ber、der等格式,接收方根据指定的schema进行解析,就能获取到对应数据。
五、资料
-
ASN.1格式数据编解码总结
-
ASN.1描述与实例
-
ASN.1编码方式详解
-
Golang asn1.Marshal函数代码示例
-
高效的5G ASN.1编解码工具
-
网页版ASN1解码工具使用教程
-
https://asn1.io/asn1playground/
-
如何解析ASN.1值表示法格式
-
Go - 解码/编码asn.1
-
PHPASN1:一个PHP库,用于使用ITU-T X.690编码规则对任意ASN.1结构进行编码和解码-源码下载
-
php asn.1,A PHP library to encode and decode arbitrary ASN.1 structures using ITU-T X.690 encoding r…
-
GitHub - fgrosse/PHPASN1: A PHP library to encode and decode arbitrary ASN.1 structures using ITU-T
-
ThinkPHP5——引用vendor或extend里的第三方类(多种方法)
-
PKCS规范
-
x509及其ASN.1表示法
最后
大家如果喜欢我的文章,可以关注我的公众号(程序员麻辣烫)
我的个人博客为:https://shidawuhen.github.io/
往期文章回顾:
-
设计模式
-
招聘
-
思考
-
存储
-
算法系列
-
读书笔记
-
小工具
-
架构
-
网络
-
Go语言