ASN.1简介及OpenSSL中ASN.1接口使用举例
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
相关文章:
谁是当今最顶级的技术?SQL、Java、Python、C++ 皆上榜!
【12月公开课预告】,入群直接获取报名地址12月11日晚8点直播主题:人工智能消化道病理辅助诊断平台——从方法到落地12月12日晚8点直播:利用容器技术打造AI公司技术中台12月17日晚8点直播主题:可重构计算:能效比、通用性…

将表里的数据批量生成INSERT语句的存储过程 增强版
原文:将表里的数据批量生成INSERT语句的存储过程 增强版将表里的数据批量生成INSERT语句的存储过程 增强版 有时候,我们需要将某个表里的数据全部或者根据查询条件导出来,迁移到另一个相同结构的库中 目前SQL Server里面是没有相关的工具根据查询条件来生…

通过OpenSSL的接口实现Base64编解码
对openssl genrsa产生的rsa私钥pem文件,使用普通的base64解码会有问题,如使用https://blog.csdn.net/fengbingchun/article/details/85218653 中介绍的方法,一是有可能不能从返回的结果中直接使用strlen来获得最终字符的大小,因为…
激辩:机器究竟能否理解常识?
【12月公开课预告】,入群直接获取报名地址12月11日晚8点直播主题:人工智能消化道病理辅助诊断平台——从方法到落地12月12日晚8点直播:利用容器技术打造AI公司技术中台12月17日晚8点直播主题:可重构计算:能效比、通用性…

Mac OS X 下Node.js开发环境的搭建
1.安装Xcode2.安装Homebrew 谷歌搜索Homebrew 复制命令行 打开终端 粘贴命令行 点击回车 安装 输入密码等2.安装Nodejs利用Homebrew安装nodejs打开终端 输入 :brew install nodejs 回车查询nodejs版本:node --version3.安装文档数据库 MongoDB打开终…

.NET 使用 MySql.Data.dll 动态库操作MySql的帮助类--MySqlHelper
.NET 使用 MySql.Data.dll 动态库操作MySql的帮助类--MySqlHelper 參考演示样例代码,例如以下所看到的: /// <summary>/// MySql 数据库操作类/// </summary>public class MySqlHelper{/// <summary>/// MysqlConnection/// </summ…
Instagram个性化推荐工程中三个关键技术是什么?
作者 | Ivan Medvedev,Haotian Wu,Taylor Gordon译者 | 陆离编辑 | Jane出品 | AI科技大本营(ID:rgznai100) 【导语】近期,Facebook 在博客上分享了第一篇详细介绍 Explore 系统关键技术,以及 I…

iOS UIbutton 点击无反应的几种情况
1、UIButton不能点击情况的第一种是,你将button添加到一个不能响应点击事件的View里。如你将button添加到UIImageView中,解决办法只需将UIImageView的 userInteractionEnabled设为YES即可。 例如: self.headImgV [[UIImageView alloc] ini…

C和C++安全编码笔记:格式化输出
C标准中定义了一些可以接受可变数量参数的格式化输出参数,参数中包括一个格式字符串。printf()和sprintf()都是格式化输出函数的例子。格式化输出函数是由一个格式字符串和可变数目的参数构成的。在效果上,格式化字符串提供了一组可以由格式化输出函数解…
谈谈UI架构设计的演化
谈谈UI架构设计的演化 经典MVC 在1979年,经典MVC模式被提出。 在当时,人们一直试图将纯粹描述思维中的对象与跟计算机环境打交道的代码隔离开来,而Trygve Reenskaug在跟一些人的讨论中,逐渐剥离出一系列的概念,最初是T…

JWT(JSON Web Token)简介及实现
JWT(JSON Web Token):是一个开放标准(RFC 7519),它定义了一种紧凑且自包含的方式,用于在各方之间作为Json对象安全地传输信息。由于此信息是经过数字签名的,因此可以被验证和信任。可以使用HMAC SHA256或RSA等对JWT进行签名。 JW…

iOS UIImageView 加载含有汉字的url处理方法
NSString *url [model.pic stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]]; [self.headImgView sd_setImageWithURL:[NSURL URLWithString:url]];

《评人工智能如何走向新阶段》后记
由AI科技大本营下载自视觉中国自《评人工智能如何走向新阶段》一文发表(在内部)后,引来了中外专家、草根们的广泛议论,有深有浅,其中似有一些思考价值,故录入本文后记: 中外专家、草根们23条议…
用XCA(X Certificate and key management)可视化程序管理SSL 证书(3)--创建自己定义的凭证管理中心(Certificate Authority)...
在第“用XCA(X Certificate and key management)可视化程序管理SSL 证书(2)---创建证书请求”章节中,我们介绍了怎样用XCA创建SSL证书请求(Certificate Request),在一章节中…

C和C++安全编码笔记:并发
并发是一种系统属性,它是指系统中几个计算同时执行,并可能彼此交互。一个并发程序通常使用顺序线程和(或)进程的一些组合来执行计算,其中每个线程和进程执行可以在逻辑上并行执行的计算。这些进程和(或)线程可以在单处理器系统上使用分时抢占…

《评人工智能如何走向新阶段》后记(再续1)
由AI科技大本营下载自视觉中国中外专家、草根对《评人工智能如何走向新阶段》一文进行广泛议论,已在《后记》中发表原创(未加修改)的23条议论,现再续发24-30条如下: 24.最近半年来,人工智能的发展重心逐渐…

iOS UITextView 随键盘弹出界面上移
- (void)textViewDidBeginEditing:(UITextView *)textView { CGRect frame textView.frame; int offSet frame.origin.y 70 - (self.view.frame.size.height - 216.0); //iphone键盘高度为216.iped键盘高度为352 [UIView beginAnimations:"ResizeForKeyboard" co…

H3C 交换机命名规则
例:H3C-S5500-28C-EIH3C:为固定值,就是“H3C”这个品牌S的位置:代表产品系列『S 代表交换机SR 代表业务路由器』第一个5的位置:代表产品子系列号『3系为千兆上行,百兆下行的合适交换机 例:S3600 S31005系为全千兆的盒式交换机 …

iOS 时间选择器封装(含三种模式)
#import <UIKit/UIKit.h> typedef enum : NSUInteger { DatePickerViewDateTimeMode,//年月日,时分 DatePickerViewDateMode,//年月日 DatePickerViewTimeMode//时分 } DatePickerViewMode; protocol DateTimePickerViewDelegate <NSObject> optional /** * 确定按…

C和C++安全编码笔记:总结
《C和C安全编码》(原书第2版)这本书是2013年出版的。 这里是基于之前所有笔记的简单总结,笔记列表如下: 字符串:https://blog.csdn.net/fengbingchun/article/details/105325508 指针诡计:https://blog.csdn.net/fengbingchun/…

《评人工智能如何走向新阶段》后记(再续2)
由AI科技大本营下载自视觉中国从朋友那里获知,有一块供大家自由议论人工智能的园地(内部的),我通过有关关系进入后,一览之余,果然生动活泼,没有学究气,从已发表的30条议论来看。有原…

Dokku和Docker的完美配合
看到一篇不错的文章,收藏一下: 【编者的话】本文作者介绍了如何在单机上将Dokku和Docker结合。Dokku是一个小型的PaaS平台,只需使用Git将代码push到对应的仓库上就能自动触发部署,构建过程非常简单。但是Dokku对于用户来说&#x…

iOS封装分页效果
#import <UIKit/UIKit.h> interface WPageTitleView : UIView property (nonatomic,assign) NSInteger selectedIndex; //添加参数数组 property (nonatomic,strong) NSArray *titles; property (nonatomic,copy) void (^buttonSelected)(NSInteger index); end #impo…

Windows/Linux TCP Socket网络编程简介及测试代码
典型的网络应用是由一对程序(即客户程序和服务器程序)组成的,它们位于两个不同的端系统中。当运行这两个程序时,创建了一个客户进程和一个服务器进程,同时它们通过从套接字(socket)读出和写入数据在彼此之间进行通信。开发者创建一个网络应用…

《评人工智能如何走向新阶段》后记(再续3)
由AI科技大本营下载自视觉中国35.阿里巴巴旗下芯片公司平头哥在乌镇互联网大会上宣布开源低功耗微控制芯片(MCU)设计平台,这一平台面向 AIoT 时代的定制化芯片设计需求,目标群体包括芯片设计公司、IP 供应商、高校及科研院所等&am…

ffmpeg 基本用法大全
FFmpegFFmpeg 基本用法本课要解决的问题1.FFmpeg的转码流程是什么?2.常见的视频格式包含哪些内容吗?3.如何把这些内容从视频文件中抽取出来?4.如何从一种格式转换为另一种格式?5.如何放大和缩小视频?6.如何旋转&#x…

快过年了,为过完年跳槽的人准备一份面试题
设计模式是什么? 你知道哪些设计模式,并简要叙述? 设计模式是一种编码经验,就是用比较成熟的逻辑去处理某一种类型的事情。 1). MVC模式:Model View Control,把模型 视图 控制器 层进行解耦合编写。 2). MV…

Ubuntu上Vim安装NERDTree插件操作步骤
NERDTree是Vim的文件系统浏览器,使用此插件,用户可以直观地浏览复杂的目录层次结构,快速打开文件以进行读取或编辑,以及执行基本的文件系统操作。NERDTree源码在https://github.com/preservim/nerdtree。 这里通过Vundle安装NERD…

《评人工智能如何走向新阶段》后记(再续4)
由AI科技大本营下载自视觉中国41. 在人工智能感知阶段,依靠数据驱动的深度学习算法。目前5种最流行的深度学习架构: ① 递归神经网络(RNN)② 长短期记忆 (LSTM)/门控递归单元(GRU)③卷积神经网络…

电视游戏会是未来客厅娱乐的主角吗?
在时下流行的多屏生态概念中,电视虽为最大屏幕,但与智能手机、平板等小屏相比,属于相对较弱的一环。无移动性、自身交互性不足,在一定程度上影响着它在移动时代的发展。而作为最能体现其“吸睛能力”的——大屏娱乐功能࿰…