ASN.1(Abstract Syntax Notation One)是一套标准,是描述数据的表示、编码传输、解码的灵活的记法。它提供了一套正式、无歧义和精确的规则以描述独立于特定计算机硬件的对象结构。OpenSSL的编码方法就是基于该标准。ASN.1是一种结构化的数字对象描述语言,它包括两部分:数据描述语言和数据编码规则。ASN.1的数据描述语言允许用户自定义基本的数据类型,并可以通过简单的数据类型组成更复杂的数据类型。

ASN.1是ISO和ITU-T的联合标准,它本身只定义了表示信息的抽象句法,但是没有限定其编码的方法。各种ASN.1编码规则提供了由ASN.1描述其抽象句法的数据的值的传送语法(具体表达)。标准的ASN.1编码规则有基本编码规则(BER,Basic Encoding Rules)、规范编码规则(CER,Canonical Encoding Rules)、唯一编码规则(DER,Distinguished Encoding Rules)、压缩编码规则(PER,Packed Encoding Rules)和XML编码规则(XER,XML Encoding Rules)。这些编码方法规定了将数字对象转换成应用程序能够处理、保存和网络传输的二进制编码形式的一组规则。PEM编码全称是Privacy Enhanced Mail,是一种保密邮件的编码标准。ASN.1与特定的ASN.1编码规则一起通过使用独立于计算机架构和编程语言的方法来描述数据结构,为结构化数据的交互提供了手段,特别是在网络环境的应用程序。

OpenSSL的PEM编码就是在DER编码基础上进行BASE64编码,然后添加一些头尾信息组成的,如在生成的rsa private.pem中头信息为-----BEGIN RSA PRIVATE KEY-----,尾信息为-----END RSA PRIVATE KEY-----,中间的数据部分即是对DER进行base64编码后的结果。

注:以上内容主要整理自网络。

ASN.1里定义的每个基本对象都有一个对应的数字标识tag,在进行二进制编码的时候需要使用该标识,定义的数字标识在asn1.h头文件中,如下所示:

/* ASN.1 tag values */
# define V_ASN1_EOC                      0
# define V_ASN1_BOOLEAN                  1 /**/
# define V_ASN1_INTEGER                  2
# define V_ASN1_BIT_STRING               3
# define V_ASN1_OCTET_STRING             4
# define V_ASN1_NULL                     5
# define V_ASN1_OBJECT                   6
# define V_ASN1_OBJECT_DESCRIPTOR        7
# define V_ASN1_EXTERNAL                 8
# define V_ASN1_REAL                     9
# define V_ASN1_ENUMERATED               10
# define V_ASN1_UTF8STRING               12
# define V_ASN1_SEQUENCE                 16
# define V_ASN1_SET                      17
# define V_ASN1_NUMERICSTRING            18 /**/
# define V_ASN1_PRINTABLESTRING          19
# define V_ASN1_T61STRING                20
# define V_ASN1_TELETEXSTRING            20/* alias */
# define V_ASN1_VIDEOTEXSTRING           21 /**/
# define V_ASN1_IA5STRING                22
# define V_ASN1_UTCTIME                  23
# define V_ASN1_GENERALIZEDTIME          24 /**/
# define V_ASN1_GRAPHICSTRING            25 /**/
# define V_ASN1_ISO64STRING              26 /**/
# define V_ASN1_VISIBLESTRING            26/* alias */
# define V_ASN1_GENERALSTRING            27 /**/
# define V_ASN1_UNIVERSALSTRING          28 /**/
# define V_ASN1_BMPSTRING                30

ASN.1编码:编码的实际数据由字符串和配置信息确定。字符串的一般格式是:零个或多个逗号分隔的修饰符,后跟一个类型,后跟一个可选的冒号和一个值。

[modifier,]type[:value]

modeifier:修饰符,支持的类型包括EXPLICIT、IMPLICIT、SEQWRAP、FORMAT等。

type:支持的类型包括BOOLEAN、INTEGER、ENUMERATED、BITSTRING等。

value:数据。

关于配置文件的更多介绍可以参考:https://www.openssl.org/docs/manmaster/man3/ASN1_generate_v3.html

通过调用ASN1_generate_nconf函数接口,格式如:"IA5STRING:https://blog.csdn.net/fengbingchun";通过调用i2d_ASN1_OCTET_STRING方式接口,格式如:"https://blog.csdn.net/fengbingchun",测试代码如下:

int test_openssl_asn1_simple_encode()
{// test 1const char* src = "IA5STRING:https://blog.csdn.net/fengbingchun";CONF* nconf = nullptr;ASN1_TYPE* encoded = ASN1_generate_nconf(src, nconf);if (!encoded) {fprintf(stderr, "fail to asn1 encode: %s\n", src);return -1;}// test 2const char* src2 = "https://blog.csdn.net/fengbingchun";ASN1_STRING asn1str;memset(&asn1str, 0, sizeof(ASN1_STRING));ASN1_STRING_set(&asn1str, src2, strlen(src2));const char *value = reinterpret_cast<char*>(ASN1_STRING_data(&asn1str));fprintf(stdout, "the value is: %s, strlen: %u\n", value, strlen(value));std::unique_ptr<unsigned char[]> encoded2(new unsigned char[strlen(src2) + 2]);unsigned char* p = encoded2.get();int encoded2_len = i2d_ASN1_OCTET_STRING(&asn1str, &p);fprintf(stdout, "encoded length: %d\n", encoded2_len);#ifdef _MSC_VERconst char* name = "E:/GitCode/OpenSSL_Test/testdata/simple2.der";
#elseconst char* name = "testdata/simple2.der";
#endifFILE* fp = fopen(name, "wb");if (!fp) {fprintf(stderr, "fail to open file: %s\n", name);return -1;}fwrite(encoded2.get(), 1, strlen(src2) + 2, fp);fclose(fp);return 0;
}

通过配置文件生成,如simple.conf,内容如下:

asn1 = IA5STRING:https://blog.csdn.net/fengbingchun

通过openssl asn1parse命令将simple.conf进行asn1编码,编码后的输出文件为simple.der,执行结果如下:

一般情况下,ASN.1编码多数是通过命令直接生成,如生成rsa私钥的配置文件rsa_private_key.conf,格式如下:

asn1=SEQUENCE:private_key
[private_key]
version=INTEGER:0
n=INTEGER:0xBB6FE79432CC6EA2D8F970675A5A87BFBE1AFF0BE63E879F2AFFB93644D4D2C6D000430DEC66ABF47829E74B8C5108623A1C0EE8BE217B3AD8D36D5EB4FCA1D9
e=INTEGER:0x010001
d=INTEGER:0x6F05EAD2F27FFAEC84BEC360C4B928FD5F3A9865D0FCAAD291E2A52F4AF810DC6373278C006A0ABBA27DC8C63BF97F7E666E27C5284D7D3B1FFFE16B7A87B51D
p=INTEGER:0xF3929B9435608F8A22C208D86795271D54EBDFB09DDEF539AB083DA912D4BD57
q=INTEGER:0xC50016F89DFF2561347ED1186A46E150E28BF2D0F539A1594BBD7FE46746EC4F
exp1=INTEGER:0x9E7D4326C924AFC1DEA40B45650134966D6F9DFA3A7F9D698CD4ABEA9C0A39B9
exp2=INTEGER:0xBA84003BB95355AFB7C50DF140C60513D0BA51D637272E355E397779E7B2458F
coeff=INTEGER:0x30B9E4F2AFA5AC679F920FC83F1F2DF1BAF1779CF989447FABC2F5628657053A

通过openssl asn1parse命令将rsa_private_key.conf进行asn1编码,编码后的输出文件为rsa_private_key.der,执行结果如下:

ASN.1解码

对上面生成的simple.der进行解码,可调用d2i_ASN1_IA5STRING相关函数,测试代码如下:

int test_openssl_simple_decode()
{
#ifdef _MSC_VERconst char* name = "E:/GitCode/OpenSSL_Test/testdata/simple.der";
#elseconst char* name = "testdata/simple.der";
#endifFILE* fp = fopen(name, "rb");if (!fp) {fprintf(stderr, "fail to open file: %s\n", name);return -1;}fseek(fp, 0, SEEK_END);long length = ftell(fp);rewind(fp);std::unique_ptr<unsigned char[]> data(new unsigned char[length + 1]);data.get()[length] = '\0'; // in order to be correct fprintf %sfread(data.get(), 1, length, fp);fclose(fp);if (data.get()[0] != V_ASN1_IA5STRING) {fprintf(stderr, "fail to get asn1 tag value: %d, %d\n", data.get()[0], V_ASN1_IA5STRING);return -1;}fprintf(stdout, "decode data length: %d\n", data.get()[1]);fprintf(stdout, "decode data: %s\n", (char*)(data.get() + 2));const unsigned char* p = data.get();ASN1_IA5STRING* str = ASN1_IA5STRING_new();d2i_ASN1_IA5STRING(&str, &p, length);fprintf(stdout, "decode data: %s\n", str->data);ASN1_IA5STRING_free(str);return 0;
}

通过命令对上面生成的simple2.der进行解码,执行命令及结果如下:与原始数据一致

对上面生成的rsa_private_key.der进行解码,测试代码如下:

typedef struct RSA_PRIVATE_KEY_st {ASN1_INTEGER* version;ASN1_INTEGER* n;ASN1_INTEGER* e;ASN1_INTEGER* d;ASN1_INTEGER* p;ASN1_INTEGER* q;ASN1_INTEGER* exp1;ASN1_INTEGER* exp2;ASN1_INTEGER* coeff;
} RSA_PRIVATE_KEY;
DECLARE_ASN1_FUNCTIONS(RSA_PRIVATE_KEY);ASN1_SEQUENCE(RSA_PRIVATE_KEY) = {ASN1_SIMPLE(RSA_PRIVATE_KEY, version, ASN1_INTEGER),ASN1_SIMPLE(RSA_PRIVATE_KEY, n, ASN1_INTEGER),ASN1_SIMPLE(RSA_PRIVATE_KEY, e, ASN1_INTEGER),ASN1_SIMPLE(RSA_PRIVATE_KEY, d, ASN1_INTEGER),ASN1_SIMPLE(RSA_PRIVATE_KEY, p, ASN1_INTEGER),ASN1_SIMPLE(RSA_PRIVATE_KEY, q, ASN1_INTEGER),ASN1_SIMPLE(RSA_PRIVATE_KEY, exp1, ASN1_INTEGER),ASN1_SIMPLE(RSA_PRIVATE_KEY, exp2, ASN1_INTEGER),ASN1_SIMPLE(RSA_PRIVATE_KEY, coeff, ASN1_INTEGER)
} ASN1_SEQUENCE_END(RSA_PRIVATE_KEY)
IMPLEMENT_ASN1_FUNCTIONS(RSA_PRIVATE_KEY)void print(const ASN1_INTEGER* str, const char* item)
{fprintf(stdout, "name: %s, type: %d, length: %d, data: ", item, str->type, str->length);for (int i = 0; i < str->length; ++i) {fprintf(stdout, "%02X", str->data[i]);}fprintf(stdout, "\n");
}int test_openssl_asn1_complex_decode()
{
#ifdef _MSC_VERconst char* name = "E:/GitCode/OpenSSL_Test/testdata/rsa_private_key.der";
#elseconst char* name = "testdata/rsa_private_key.der";
#endifFILE* fp = fopen(name, "rb");if (!fp) {fprintf(stderr, "fail to open file: %s\n", name);return -1;}fseek(fp, 0, SEEK_END);long length = ftell(fp);rewind(fp);std::unique_ptr<unsigned char[]> data(new unsigned char[length]);fread(data.get(), 1, length, fp);fclose(fp);// data.get()[0]: type tag indicating SEQUENCE, 0x30if (data.get()[0] != 0x30) {fprintf(stderr, "it's type should be SEQUENCE: %s, %x\n", name, data.get()[0]);return -1;}const unsigned char* p = data.get();RSA_PRIVATE_KEY* key = d2i_RSA_PRIVATE_KEY(nullptr, &p, length);if (!key) {fprintf(stderr, "fail to d2i_RSA_PRIVATE_KEY\n");return -1;}print(key->version, "version");print(key->n, "n");print(key->e, "e");print(key->d, "d");print(key->p, "p");print(key->q, "q");print(key->exp1, "exp1");print(key->exp2, "exp2");print(key->coeff, "coeff");RSA_PRIVATE_KEY_free(key);return 0;
}

执行结果如下:与配置文件中的原始数据一致

通过命令对rsa_private_key.der进行解码,执行结果如下:与上面通过code执行的结果一致

通过UE打开rsa_private_key.der,结果如下:

对UE打开的rsa_private_key.der进行说明:

0x30:说明ASN.1 tag为SEQUENCE类型;

0x82:指后面两个字节的长度是long form;

0x013b:指明节点数据字节总长度;

0x02:version tag为INTEGER;

0x01:version长度为1;

0x00:version值为0x00;

0x02:n tag为INTEGER;

0x41:n长度为65,因为紧挨着的下一个字节是0x00,表示数据是正整数,所以n的实际长度为64,与code执行结果值一致;

通过命令对rsa_private_key.der解码进行说明:ASN.1 der结构格式,第一行

(1).0:表示节点在整个文件中的偏移长度;

(2).d=0:表示节点深度;

(3).hl=4:表示节点头字节长度;

(4).l=315:第一行中指所有节点数据字节长度;其它行表示当前节点数据字节长度;

(5).cons:表示该节点为结构节点,表示包含子节点或子结构体数据;其它行的prim表示该节点为原始节点,包含数据;

(6). SEQUENCE:表示ASN.1 tag类型;其它行的INTEGER也是ASN.1的一种tag类型;

(7).其它行的最后一列:表示节点数据。

以上代码段的完整code见:GitHub/OpenSSL_Test

GitHub:https://github.com//fengbingchun/OpenSSL_Test

ASN.1简介及OpenSSL中ASN.1接口使用举例相关推荐

  1. OpenSSL中的大数接口与基于其的自用RSA加密接口设计

    本文记录了初次接触OpenSSL中的大数模块,重温了RSA加密流程,使用OpenSSL的接口包装成自用RSA加密接口,并且利用自己的接口演示了Alice与Bob通过RSA加密进行通讯的一个示例. 概览 ...

  2. 非对称加密算法之RSA介绍及OpenSSL中RSA常用函数使用举例

    RSA算法,在1977年由Ron Rivest.Adi Shamirh和LenAdleman,在美国的麻省理工学院开发完成.这个算法的名字,来源于三位开发者的名字.RSA已经成为公钥数据加密标准. R ...

  3. 对称加密算法之RC4介绍及OpenSSL中RC4常用函数使用举例

    RC4是一种对称密码算法,它属于对称密码算法中的序列密码(streamcipher,也称为流密码),它是可变密钥长度,面向字节操作的流密码. RC4是流密码streamcipher中的一种,为序列密码 ...

  4. OpenSSL中的EVP接口

    索引 摘要算法(Digest) 获取EVP_MD Digest API 例子 对称加密(Cipher) 获取EVP_CIPHER Cipher API 上下文管理 Encrypt API Decryp ...

  5. 对称加密算法AES之GCM模式简介及在OpenSSL中使用举例

    AES(Advanced Encryption Standard)即高级加密标准,由美国国家标准和技术协会(NIST)于2000年公布,它是一种对称加密算法.关于AES的更多介绍可以参考:https: ...

  6. GIS讲堂第二课-地图切片简介以及OL中的调用

    概述: 在大家的支持与帮助下,"GIS讲堂"第二课已落下帷幕,在此对大家的支持与帮助表示衷心的感谢,同时呢,也给大家致歉,由于感冒的缘故,讲课的时候的各种毛病还请大家多多包涵,下面 ...

  7. 计算机视觉子方向,计算机视觉方向简介 | 人脸识别中的活体检测算法综述

    原标题:计算机视觉方向简介 | 人脸识别中的活体检测算法综述 本文转载自"SIGAI人工智能学习与实践平台"(ID:SIGAICN) 导言 1. 什么是活体检测? 判断捕捉到的人脸 ...

  8. bert模型简介、transformers中bert模型源码阅读、分类任务实战和难点总结

    bert模型简介.transformers中bert模型源码阅读.分类任务实战和难点总结:https://blog.csdn.net/HUSTHY/article/details/105882989 ...

  9. java程序设计专业介绍_简介Java编程中的Object类

    这篇文章主要介绍了简介Java编程中的Object类,是Java入门学习中的基础知识,需要的朋友可以参考下 Object 类位于 java.lang 包中,是所有 Java 类的祖先,Java 中的每 ...

最新文章

  1. 通过minify将项目中js和css文件的打包
  2. 从 vue-cli 源码中,我发现了27行读取 json 文件有趣的 npm 包
  3. 年底送书活动:送出6本技术书籍,价值372元!
  4. elasticsearch数据备份还原
  5. 【深度学习原理】交叉熵损失函数的实现
  6. 好看的电脑桌面悬浮时钟工具
  7. 申报软件著作权时,怎样快捷计算源代码行数
  8. 梅特勒托利多xk3124电子秤说明书_托利多电子秤完整操作手册
  9. socks5 转换为 http 代理(使用privoxy)
  10. 实现python源代码加密
  11. raw格式镜像文件转vmdk
  12. uniapp 获取网络状态_uni-app 获取网络状态
  13. 【学习记录2】数组里的字符串转换成数字或者把数字转换成字符串
  14. 两成开发者月薪超1.7万,算法工程师最紧缺
  15. 测试网络SNMP连接的几个方法(我平时调试SNMP程序时用到的几个解决方案)
  16. C语言编程规范之匈牙利命名法
  17. android studio开发工具新手入门
  18. Word编写VBA程序 hello world
  19. 罗永浩回应子弹短信、TNT、无限屏的一切质疑
  20. 花了小半个月工资,我还是对培训机构下了手

热门文章

  1. 关闭tomact被占用的进程
  2. C语言实现bmp图像几何变换(移动,旋转,镜像,转置,缩放)
  3. JS中编写函数去除HTML标签,js函数获取html中className所在的内容并去除标签
  4. linux arm中断跑马灯,S3C2410 MDK实验---ARM汇编语言实现跑马灯
  5. node 获取表单数据 为空_数据结构与算法(python)单向循环链表
  6. 头戴式AR/VR 光学标定
  7. 一行代码解决对象数组排序(sort)
  8. 个人建议:设置Alt+S快捷键来控制VSCode自动保存切换功能
  9. ATS名词术语(待续)
  10. ldconfig及 LD_LIBRARY_PATH