OpenSSL之配置文件用法

OpenSSL之配置文件用法,第1张

OpenSSL配置文件采用linux风格,其文件格式由分组字段名、属性名、属性值三部分组成。

如下面的例子:

其中,属性值还支持变量替换,如该例的属性:

log = $path/log

其值最终会被替换为./module_a/log

本文假设你已经安装好了OpenSSL,并且持有一份1.1.1的源码。

配置文件相关的头文件为conf.h、源文件实现在crypto/conf目录中。

这个结构定义了单个配置项的数据结构。主要字段含义:

section —— 表示一个配置段的名称。

name —— 表示在该配置段下的属性名称。

value —— 表示对应属性的值。

这个结构定义了配置抽象方法集合。主要字段含义:

name —— 配置方法名称。

create —— 根据配置抽象方法,创建一个配置项结构,返回其指针。

init —— 初使化配置。

destroy —— 释放所有配置。

destroy_data —— 仅释放所有配置项数据,不释放配置本身。

load_bio —— 以BIO的方式加载配置。

dump —— 打印配置项到BIO中。

is_number —— 判断指定字符是否数字。

to_int —— 将字符转换为数字。

load —— 从文件(派迹重新)加载所有配置。

这个结构定义了世激配置存储数据结构。主要字段含义:

meth —— 抽象方法集合。

meth_data —— 附加的内存数据,暂时还没有明确用途。

data —— 配置项哈希表。

在1.1.1中,大多数的数据结构已经不再向使用者开放,从封装的角度来看,这是更合理的。如果你在头文件中找不到结构定义,不妨去源码中搜一搜。

CONF *NCONF_new(CONF_METHOD *meth)

指定一个抽象方法集合,创建配置存储结构。

成功返回有效指针,失败返回NULL。

如果meth传入NULL,表示使用默认的抽象方法。

void NCONF_free(CONF *conf)

释放配置存储。

int NCONF_load(CONF *conf, const char *file, long *eline)

从指定文件加载配置,eline表示出错时输出的行号。

成功返回1,失败返回0。

int NCONF_load_bio(CONF *conf, BIO *bp, long *eline)

从BIO加载配置,eline表示出错搜羡袜时输出的行号。

成功返回1,失败返回0。

int NCONF_load_fp(CONF *conf, FILE *fp, long *eline)

NCONF_load_bio()的FILE版本。

char *NCONF_get_string(const CONF *conf, const char *group, const char *name)

从配置中,获取指定分组下的属性值。

成功返回C风格字符串,失败返回NULL。

若group传NULL,表示取全局属性。

int NCONF_get_number_e(const CONF *conf, const char *group, const char *name, long *result)

从配置中,获取指定分组下的数字属性值,数字值输出到result中。

成功返回1,失败返回0。

若group传NULL,表示取全局属性。

STACK_OF(CONF_VALUE) *NCONF_get_section(const CONF *conf, const char *section)

返回指定分组下的配置项堆栈集合。section不能为NULL。

成功返回有效堆栈指针,失败返回NULL。

下面这个例子演示了使用配置API进行基本的文件 *** 作。

test.cnf文件内容:

user = root

[module_a]

path = ./module_a

log = $path/log

filecount = 10

输出:

global user:[root]

module_a::path:[./module_a]

module_a::log:[./module_a/log]

ret:[1] module_a::filecount:[10]

下面这个例子演示了使用BIO读取配置文件,并且遍历指定的分组。

输出:

section:[module_a] name:[path] value:[./module_a]

section:[module_a] name:[log] value:[./module_a/log]

section:[module_a] name:[filecount] value:[10]

ASN.1 – Abstract Syntax Notation dot one,抽象记法1,在大部分的书写场合会简写为ASN1。它描述了一种对数据进行表示、编码、传输和解码的数据格式。它提供了一整套正规的格式用于描述对象的结构,而不管语言上如何执行及这些数据的具体指代,也不用去管到底是什么样的应用程序。

ASN1有很多实现版本,OpenSSL主要采用DER格式。

本文假设你已经安装好了OpenSSL,并且持有一份1.1.1的源码。

ASN1相关的头文件为asn1.h、asn1t.h、源文件在crypto/asn1目录中。

这个结芦轮神构负责管理ANS1大多数数据类型的内存组织方式,字段含义:

length —— 管理的数据长度。

type —— 管理的数据类型。

data —— 数据指针。

flags —— 标志位,跟具体数据类型有关。

管理的数据类型:

主要数据类型的取值:

其它一些类型的定义:

在OpenSSL的实现中,每个数据类型都有一个ITEM结构,它负责定义这个数据类型的编解码规则。

相关字段含义如下:

itype —— 定义ITEM自身的类型,取值:

utype —— 定义管理的数据类型,取值前面有说明,例如V_ASN1_INTEGER。

templates —— 数据处理方法模板数组指针。

tcount —— 模板数组的个数。

funcs —— 回调方法指针。

size —— 负责数据结构的大小。

sname —— 指向数据结构的名称。

前面讲到,对每个数据类型来说,OpenSSL需要一个对应的ITEM结构,下面这几个宏为每个数据类型声明ITEM结构,同时也声明了相应的助记函数。如下:

拿 DECLARE_ASN1_FUNCTIONS(ASN1_INTEGER) 来说,宏定义展开形式为:

还有几个宏负责为数据类型实现相应的助记函数。如下:

拿 IMPLEMENT_ASN1_ITEM(ASN1_INTEGER) 来说,其展开形式为:

可以看到,展开形式所实现的助记函数实际上是对ASN1内部几个函数的封装调用,对提供上层数据类型与内部类型的转换,其中ASN1_VALUE是类型上下转换的纽带,它是个万能指针:

typedef struct ASN1_VALUE_st ASN1_VALUE

在asn1.h中,对基本数据类型声明了助记函数:

可是我暂时还没有找到对基本数据类型助记函数的宏实现。

OpenSSL的ASN1编码处理依赖ASN1_ITEM对象,对基本类型来桐郑说,OpenSSL本身已经做了声明和初使化,所以可以直接以相关函数中使用。然而对于自定义结构体,需要开发者来定义ASN1_ITEM对陪亏象和规则,有一些宏可以提供帮助,摘录部分如下:

在ASN1_ITEM结构中,ASN1_TEMPLATE的结构定义如下:

主要字段含义:

offset —— 字段在父结构中的偏移。

field_name —— 字段名称。

item —— 指向字段对应结构类型的ASN1_ITEM对象。

举例定义如下的自定义结构:

将宏展开,为:

ASN1_VALUE *ASN1_item_new(const ASN1_ITEM *it)

void ASN1_item_free(ASN1_VALUE *val, const ASN1_ITEM *it)

根据ITEM对象创建和释放对应的数据类型。

ASN1_VALUE *ASN1_item_d2i(ASN1_VALUE **val, const unsigned char **in, long len, const ASN1_ITEM *it)

根据ITEM对象将DER编码转换为二进制结构。

成功返回有效指针,成功返回NULL。

如果val为NULL,将内部分配内存,但必须由外部使用者释放。

其内部实现为:

可以看到内部做了为val传入NULL的兼容性处理。

int ASN1_item_i2d(ASN1_VALUE *val, unsigned char **out, const ASN1_ITEM *it)

根据ITEM对象将二进制结构转换为DER编码。

成功返回DER编码长度,失败返回负数。

如果*out为NULL,将内部分配内存,但内存必须由外部使用者释放。若**out为NULL,将仅返回长度,这用于试探长度的场景。

其内部实现为:

可以看到内部做了为out和*out传入NULL的兼容性处理。在out传入NULL的情况下,无法接收缓冲区,只能返回长度。

void *ASN1_item_d2i_bio(const ASN1_ITEM *it, BIO *in, void *x)

根据ITEM对象将DER编码转换为二进制结构,输入的DER编码来源于BIO对象。

成功返回有效指针,成功返回NULL。

事实上,本函数是对ASN1_item_d2i()的封装调用。

其内部实现为:

void *ASN1_item_d2i_fp(const ASN1_ITEM *it, FILE *in, void *x)

ASN1_item_d2i_bio()的FILE版本。

ASN1_item_d2i_bio()和ASN1_item_d2i_fp() 这两个函数关于输出参数x的类型定义是不恰当的,后面讲的 ASN1_d2i_bio()和ASN1_d2i_bio() 有修复这个问题。

int ASN1_item_i2d_bio(const ASN1_ITEM *it, BIO *out, void *x)

根据ITEM对象将二进制结构转换为DER编码,输出到BIO对象中。

成功返回DER编码长度,失败返回负数。

如果*out为NULL,将内部分配内存,但内存必须由外部使用者释放。事实上,本函数是对ASN1_item_i2d()的封装调用。

其内部实现为:

int ASN1_item_i2d_fp(const ASN1_ITEM *it, FILE *out, void *x)

ASN1_item_i2d_bio()的FILE版本。

其实上,以上函数由于直接需要ASN1_ITEM做为参数,很少在程序中直接使用,而是由DECLARE_ASN1_FUNCTIONS宏声明的变体,或者下面介绍的函数代替。

void *ASN1_d2i_bio(void *(*xnew) (void), d2i_of_void *d2i, BIO *in, void **x)

从BIO对象读取DER编码数据,转换为对应的二进制结构,具体的转换依赖d2i函数指针。

成功返回有效指针,失改返回NULL。

其内部实现为:

void *ASN1_d2i_fp(void *(*xnew) (void), d2i_of_void *d2i, FILE *in, void **x)

ASN1_d2i_bio()的FILE版本。

int ASN1_i2d_bio(i2d_of_void *i2d, BIO *out, unsigned char *x)

将二进制结构转换为DER编码,输出到BIO中,具体的转换依赖i2d函数指针。

成功返回1,失败返回0。

其内部实现为:

int ASN1_i2d_fp(i2d_of_void *i2d, FILE *out, void *x)

ASN1_i2d_bio() 的FILE版本。

上面d2i_of_void 和 i2d_of_void 函数的签名为:

通常为数据类型的转换助记函数。

int ASN1_INTEGER_set(ASN1_INTEGER *a, long v)

将v值设置到a中。成功返回1,失败返回0。

long ASN1_INTEGER_get(const ASN1_INTEGER *a)

获取a中存储的整数值。失败返回-1。

int ASN1_ENUMERATED_set(ASN1_ENUMERATED *a, long v)

将v值设置到a中。成功返回1,失败返回0。

long ASN1_ENUMERATED_get(const ASN1_ENUMERATED *a)

获取a中存储的枚举值。失败返回-1。

int ASN1_STRING_set(ASN1_STRING *str, const void *data, int len)

将data和len指向的串值设置到str中。成功返回1,失败返回0。

unsigned char *ASN1_STRING_data(ASN1_STRING *x)

获取x的字符串首指针。成功返回有效指针,失败返回NULL。

int ASN1_STRING_length(const ASN1_STRING *x)

获取x的字符串长度。

int i2a_ASN1_INTEGER(BIO *bp, const ASN1_INTEGER *a)

将ASN1_INTEGER转换为ASC码,输出到bp中。成功返回1,失败返回0。

int a2i_ASN1_INTEGER(BIO *bp, ASN1_INTEGER *bs, char *buf, int size)

将bp中的ASC码转换为ASN1_INTEGER,buf存放BIO的ASC码。成功返回1,失败返回0。

int i2a_ASN1_ENUMERATED(BIO *bp, const ASN1_ENUMERATED *a)

将ASN1_ENUMERATED转换为ASC码,输出到bp中。成功返回1,失败返回0。

int a2i_ASN1_ENUMERATED(BIO *bp, ASN1_ENUMERATED *bs, char *buf, int size)

将bp中的ASC码转换为ASN1_ENUMERATED,buf存放BIO的ASC码。成功返回1,失败返回0。

int i2a_ASN1_STRING(BIO *bp, const ASN1_STRING *a, int type)

将a中的字符串转换为ASC码输出到bp中,type不起作用。返回转换的ASC字符串长度。

int a2i_ASN1_STRING(BIO *bp, ASN1_STRING *bs, char *buf, int size)

将bp中的ASC码转换为ASN1_STRING,buf存放BIO的ASC码。成功返回1,失败返回0。

ASN1_OBJECT *ASN1_OBJECT_new(void)

分配OID对象。

void ASN1_OBJECT_free(ASN1_OBJECT *a)

释放OID对象。

OID的编码规为:第一个八位组采用公式:first_arc* 40+second_arc。如果一个数大于127,就采用多个8位表示,最高位用1表示后续还有octet,用0表示后续没有。成功返回OID编码的字节数。

int a2d_ASN1_OBJECT(unsigned char *out, int olen, const char *buf, int num)

计算由ASC字符串指定的OID的DER编码。返回编码的字节数。所果事先需要知道编码的长度来分配内存,可以设置out为NULL,olen为0,获取编码字节长度,根据该长度再去分配内存。

ASN1_OBJECT *d2i_ASN1_OBJECT(ASN1_OBJECT **a, const unsigned char **pp, long length)

将DER编码转换为OID对象。成功返回有效指针。

int i2d_ASN1_OBJECT(const ASN1_OBJECT *a, unsigned char **pp)

将OID对象换转为DER编码。返回DER编码的长度。

int i2a_ASN1_OBJECT(BIO *bp, const ASN1_OBJECT *a)

将DER编码转换为ASC字符串,结果输出到bp中。返回ASC字符串长度。

下面这个例子测试了很多函数的用法,请自行放开注释并测试。


欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/tougao/8173334.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-04-14
下一篇 2023-04-14

发表评论

登录后才能评论

评论列表(0条)

保存