Bootstrap

ASN1编解码

ASN1编码

ASN.1(Abstract Syntax Notation One)是一种用于描述数据结构和编码规则的标记语言,它广泛应用于网络通信、加密和安全领域。ASN.1 定义了一种独立于具体编程语言的数据表示方法,可以确保不同计算机系统之间的数据交换的正确性和一致性。

ASN.1 编码的主要用途包括:

数据结构描述:ASN.1 提供了一种形式化的语法来描述复杂的数据结构,包括记录、序列、集合、数组等。这使得不同系统之间能够对数据结构进行准确的定义和解释,确保数据在传输和存储过程中不会出现歧义。

数据编码和解码:ASN.1 定义了多种编码规则,如基于二进制的 BER(Basic Encoding Rules)、DER(Distinguished Encoding Rules)、CER(Canonical Encoding Rules)等,以及基于 XML 的 XER(XML Encoding Rules)。ASN.1 编码规则将数据结构转换为字节流,方便在网络中传输和存储,并能够实现跨平台和跨语言的数据交换。

网络通信和协议定义:ASN.1 用于定义许多通信协议的数据格式,如 SNMP 数据包格式、LDAP 数据格式、X.509 证书、PKCS 密钥和证书等。ASN.1 的描述能力使得网络通信中的数据格式得以准确定义,并且能够方便地解析和处理。

安全和加密:ASN.1 用于描述和编码数字证书、公钥、私钥等安全相关的数据结构。ASN.1 结合公钥基础设施(PKI)和加密算法,可以支持诸如 SSL/TLS、SSH 和数字签名等安全通信协议,确保数据的机密性、完整性和身份验证。

总之,ASN.1 编码在网络通信、安全、加密以及协议定义等领域发挥着重要作用,通过提供统一的数据描述和编码规则,实现了不同系统间的数据交换和互操作。

数据类型

BIT STRING 位或二进制字符串是任意长度的位数组
BOOLEAN TRUE 或 FALSE
INTEGER 整数
Null
OBJECT 对象标识符
OCTET_STRING 八进制字符串是任意大的字节数组

字符串类型

BMPString
IA5String 国际字母数字 5 (IA5) 通常等效于 ASCII 字母表
PrintableString 可用于大型机输入终端的有限字符集
TeletexString
UTF8String 8 位 UCS/Unicode 转换格式 (UTF-8) 是一种长度可变的字符编码,可以将任何通用字符表示为 Unicode 字符,同时允许初始码位与 ASCII 保持一致

构造类型

SEQUENCE 和 SEQUENCE OF 包含一个或多个类型的有序字段系列 字段可以标记为OPTIONAL 或 DEFAULT

SET 和 SET OF	包含一个或多个类型的无序字段系列
X509.h
ASN1_INTEGER: 表示 ASN.1 编码中的整数类型。
ASN1_ENUMERATED: 表示 ASN.1 编码中的枚举类型。
ASN1_BIT_STRING: 表示 ASN.1 编码中的位串类型。
ASN1_OCTET_STRING: 表示 ASN.1 编码中的八位字节串类型。
ASN1_PRINTABLESTRING: 表示 ASN.1 编码中的可打印字符串类型。
ASN1_T61STRING: 表示 ASN.1 编码中的 T.61 字符串类型。
ASN1_IA5STRING: 表示 ASN.1 编码中的 IA5 字符串类型。
ASN1_GENERALSTRING: 表示 ASN.1 编码中的通用字符串类型。
ASN1_UNIVERSALSTRING: 表示 ASN.1 编码中的通用字符串类型。
ASN1_BMPSTRING: 表示 ASN.1 编码中的 BMP 字符串类型。
ASN1_UTCTIME: 表示 ASN.1 编码中的 UTC 时间类型。
ASN1_TIME: 表示 ASN.1 编码中的时间类型。
ASN1_GENERALIZEDTIME: 表示 ASN.1 编码中的广义时间类型。
ASN1_VISIBLESTRING: 表示 ASN.1 编码中的可见字符串类型。
ASN1_UTF8STRING: 表示 ASN.1 编码中的 UTF-8 字符串类型。
ASN1_STRING: 表示 ASN.1 编码中的字符串类型。
其他常用
X509_ALGOR 算法标识

ASN1方法

ASN1_SEQUENCE 结构体定义

是一种宏定义,用于表示 ASN.1 编码中的序列类型

例子
ASN1_SEQUENCE 宏通常用于定义 ASN.1 编码中的序列类型,并将其映射到相应的结构体类型。以下是使用 ASN1_SEQUENCE 宏的一般步骤:

引入相关的头文件和库:首先,确保引入了适当的头文件和库,以便使用 ASN.1 编码和解码相关的函数和类型。

定义结构体类型:使用 ASN1_SEQUENCE 宏定义结构体类型,该结构体类型将表示 ASN.1 序列的字段和成员。在序列中每个字段都可以使用 ASN1_SIMPLE 宏进行定义,指定字段名称、字段类型和字段标签。

typedef struct {
  ASN1_INTEGER field1;
  ASN1_OCTET_STRING field2;
} MySequence;

ASN1_SEQUENCE(MySequence) = {
  ASN1_SIMPLE(MySequence, field1, ASN1_INTEGER),
  ASN1_SIMPLE(MySequence, field2, ASN1_OCTET_STRING),
} ASN1_SEQUENCE_END(MySequence);
DECLARE_ASN1_FUNCTIONS(MySequence);//头文件声明ASN1_SEQUENCE_END的函数
DEFINE_STACK_OF()//用于创建堆栈结构

DEFINE_STACK_OF(X509);
STACK_OF(X509) *certStack;  // 声明一个存储 X509 证书的堆栈对象

在上述示例中,定义了一个名为 MySequence 的结构体类型,其中包含两个字段 field1 和 field2,分别是 ASN1_INTEGER 类型和 ASN1_OCTET_STRING 类型。
编码和解码操作:使用提供的 ASN.1 编码和解码函数对 ASN.1 序列进行编码和解码操作。这些函数通常接受结构体类型的实例作为参数,并根据 ASN.1 规范将其转换为相应的二进制编码或解析二进制编码为结构体类型的实例。
例如,ASN.1 编码函数可能是 i2d_MySequence,用于将 MySequence 类型的实例编码为二进制数据。ASN.1 解码函数可能是 d2i_MySequence,用于将二进制数据解码为 MySequence 类型的实例。
这样,通过使用 ASN1_SEQUENCE 宏定义结构体类型,并使用相应的编码和解码函数,可以方便地进行 ASN.1 编码和解码操作,以便处理和传输符合 ASN.1 规范的数据。

ASN1_SIMPLE 用于定义 ASN.1 编码中的简单类型字段(整数,字符串)。针对 ASN.1 编码的数据传输、存储和处理。
编码:将结构体类型的实例编码为符合 ASN.1 标准的二进制数据。可以使用 OpenSSL 提供的编码函数(例如 i2d_MyStructure)将结构体实例转换为 ASN.1 编码的二进制数据。这是在数据传输和存储中常用的操作,以便将结构化数据转化为可传输的二进制形式。
解码:将 ASN.1 编码的二进制数据解码为对应的结构体类型的实例。使用 OpenSSL 提供的解码函数(例如 d2i_MyStructure)可以将二进制数据解析为结构体实例,以便进行进一步的处理和操作。

ASN1_SIMPLE(type, field, fieldtype)
type:定义包含字段的结构体类型。
field:字段名称。
fieldtype:字段的类型,通常是 ASN.1 中定义的简单类型。
IMPLEMENT_ASN1_FUNCTIONS 编码的结构体的方法定义
IMPLEMENT_ASN1_FUNCTIONS(X509_RSA_PUBLICKEY) 
是 OpenSSL 库中的宏,用于为 X509_RSA_PUBLICKEY 结构体类型实现 ASN.1 编码相关的函数。
这个宏会自动为 X509_RSA_PUBLICKEY 结构体类型生成以下函数:
X509_RSA_PUBLICKEY_new():创建一个新的 X509_RSA_PUBLICKEY 结构体实例,并分配内存空间。
X509_RSA_PUBLICKEY_free(x):释放 X509_RSA_PUBLICKEY 结构体实例 x 所占用的内存空间。
d2i_X509_RSA_PUBLICKEY(bp, x):从文件流 bp 中将 ASN.1 编码的 X509_RSA_PUBLICKEY 数据解码为 X509_RSA_PUBLICKEY 结构体实例 x。
i2d_X509_RSA_PUBLICKEY(bp, x):将 X509_RSA_PUBLICKEY 结构体实例 x 编码为 ASN.1 格式并写入文件流 bp。

使用 IMPLEMENT_ASN1_FUNCTIONS(X509_RSA_PUBLICKEY) 宏可以简化 ASN.1 编码和解码操作的实现。你可以在代码中使用这些生成的函数来处理 X509_RSA_PUBLICKEY 类型的数据,如创建该结构体的实例、释放内存、解码和编码数据。

i2d 表示 “internal to der” d2i 表示 “der to internal”(Der 编码解码为内部数据格式)

举例 编解码使用

#include <stdio.h>
#include <stdlib.h>
#include <openssl/asn1.h>

int main() {
    ASN1_INTEGER *asnInt = NULL;
    unsigned char *derData = NULL;
    int derLen;
    
    // 创建一个 ASN1_INTEGER 对象
    asnInt = ASN1_INTEGER_new();
    if (asnInt == NULL) {
        printf("Failed to create ASN1_INTEGER\n");
        return 1;
    }
    
    // 设置整数值
    ASN1_INTEGER_set(asnInt, 12345);
    
    // 编码 ASN1_INTEGER 为 ASN.1 DER 格式
    derLen = i2d_ASN1_INTEGER(asnInt, &derData);
    if (derLen < 0) {
        printf("Failed to encode ASN1_INTEGER\n");
        return 1;
    }
    
    // 可以将 derData 存储、传输等等操作
    // ...
    
    // 解码 ASN.1 DER 数据为 ASN1_INTEGER 对象
    asnInt = d2i_ASN1_INTEGER(NULL, (const unsigned char **)&derData, derLen);
    if (asnInt == NULL) {
        printf("Failed to decode ASN1_INTEGER\n");
        return 1;
    }
    
    // 从 ASN1_INTEGER 对象中获取整数值
    long value = ASN1_INTEGER_get(asnInt);
    printf("Decoded integer: %ld\n", value);
    
    // 释放相关资源
    ASN1_INTEGER_free(asnInt);
    free(derData);
    
    return 0;
}

常用函数

OpenSSL 库提供了许多用于数据转换的函数。以下是其中一些常用的转换函数:

BIGNUM 转换
BIGNUM *BN_bin2bn(const unsigned char *s, int len, BIGNUM *ret); 将二进制数据转换为 BIGNUM 对象。

BN_bn2hex: 将 BIGNUM 对象转换为十六进制字符串。
BN_bn2dec: 将 BIGNUM 对象转换为十进制字符串。
BN_hex2bn: 将十六进制字符串转换为 BIGNUM 对象。
BN_dec2bn: 将十进制字符串转换为 BIGNUM 对象。
ASN.1 数据编码/解码

i2d_ASN1_INTEGER: 将 ASN1_INTEGER 对象编码为 ASN.1 DER 格式的数据。
d2i_ASN1_INTEGER: 将 ASN1_INTEGER 对象解码为 ASN.1 DER 格式的数据。
PEM 格式编码/解码

PEM_read_bio_PrivateKey: 从 BIO 中读取 PEM 格式的私钥。
PEM_write_bio_PrivateKey: 将私钥写入 BIO,以 PEM 格式保存。
PEM_read_bio_X509: 从 BIO 中读取 PEM 格式的 X.509 证书。
PEM_write_bio_X509: 将 X.509 证书写入 BIO,以 PEM 格式保存。
Base64 编码/解码

EVP_EncodeBlock: 将二进制数据编码为 Base64 字符串。
EVP_DecodeBlock: 将 Base64 字符串解码为二进制数据。
十六进制字符串转换

OPENSSL_hexstr2buf: 将十六进制字符串转换为二进制数据。
OPENSSL_buf2hexstr: 将二进制数据转换为十六进制字符串。
ASN1_INTEGER *BN_to_ASN1_INTEGER(const BIGNUM *bn, ASN1_INTEGER *ai);
将 大数(BIGNUM)转换为 ASN.1 INTEGER 对象
;