ASN.1 – Abstract Syntax Notation dot one,抽象记法1,在大部分的书写场合会简写为ASN1。它描述了一种对数据进行表示、编码、传输和解码的数据格式。它提供了一整套正规的格式用于描述对象的结构,而不管语言上如何执行及这些数据的具体指代,也不用去管到底是什么样的应用程序。
ASN1有很多实现版本,OpenSSL主要采用DER格式。
本文假设你已经安装好了OpenSSL,并且持有一份1.1.1的源码。
ASN1相关的头文件为asn1.h、asn1t.h、源文件在crypto/asn1目录中。
主要结构:
struct asn1_string_st {
int length;
int type;
unsigned char *data;
long flags;
};
这个结构负责管理ANS1大多数数据类型的内存组织方式,字段含义:
length —— 管理的数据长度。
type —— 管理的数据类型。
data —— 数据指针。
flags —— 标志位,跟具体数据类型有关。
管理的数据类型:
typedef struct asn1_string_st ASN1_INTEGER;
typedef struct asn1_string_st ASN1_ENUMERATED;
typedef struct asn1_string_st ASN1_BIT_STRING;
typedef struct asn1_string_st ASN1_OCTET_STRING;
typedef struct asn1_string_st ASN1_PRINTABLESTRING;
typedef struct asn1_string_st ASN1_T61STRING;
typedef struct asn1_string_st ASN1_IA5STRING;
typedef struct asn1_string_st ASN1_GENERALSTRING;
typedef struct asn1_string_st ASN1_UNIVERSALSTRING;
typedef struct asn1_string_st ASN1_BMPSTRING;
typedef struct asn1_string_st ASN1_UTCTIME;
typedef struct asn1_string_st ASN1_TIME;
typedef struct asn1_string_st ASN1_GENERALIZEDTIME;
typedef struct asn1_string_st ASN1_VISIBLESTRING;
typedef struct asn1_string_st ASN1_UTF8STRING;
typedef struct asn1_string_st ASN1_STRING;
主要数据类型的取值:
# 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
其它一些类型的定义:
typedef int ASN1_BOOLEAN;
typedef int ASN1_NULL;
struct asn1_object_st {
const char *sn, *ln;
int nid;
int length;
const unsigned char *data;
int flags;
};
typedef struct asn1_object_st ASN1_OBJECT;
数据类型转换的ITEM定义:
在OpenSSL的实现中,每个数据类型都有一个ITEM结构,它负责定义这个数据类型的编解码规则。
struct ASN1_ITEM_st {
char itype; /* The item type, primitive, SEQUENCE, CHOICE
* or extern */
long utype; /* underlying type */
const ASN1_TEMPLATE *templates; /* If SEQUENCE or CHOICE this contains
* the contents */
long tcount; /* Number of templates if SEQUENCE or CHOICE */
const void *funcs; /* functions that handle this type */
long size; /* Structure size (usually) */
const char *sname; /* Structure name */
};
typedef struct ASN1_ITEM_st ASN1_ITEM;
typedef const ASN1_ITEM ASN1_ITEM_EXP;
相关字段含义如下:
itype —— 定义ITEM自身的类型,取值:
#define ASN1_ITYPE_PRIMITIVE 0x0
#define ASN1_ITYPE_SEQUENCE 0x1
#define ASN1_ITYPE_CHOICE 0x2
#define ASN1_ITYPE_EXTERN 0x4
#define ASN1_ITYPE_MSTRING 0x5
#define ASN1_ITYPE_NDEF_SEQUENCE 0x6
utype —— 定义管理的数据类型,取值前面有说明,例如V_ASN1_INTEGER。
templates —— 数据处理方法模板数组指针。
tcount —— 模板数组的个数。
funcs —— 回调方法指针。
size —— 负责数据结构的大小。
sname —— 指向数据结构的名称。
数据类型的函数声明和实现:
前面讲到,对每个数据类型来说,OpenSSL需要一个对应的ITEM结构,下面这几个宏为每个数据类型声明ITEM结构,同时也声明了相应的助记函数。如下:
# define DECLARE_ASN1_FUNCTIONS(type) DECLARE_ASN1_FUNCTIONS_name(type, type)
# define DECLARE_ASN1_FUNCTIONS_name(type, name) \
DECLARE_ASN1_ALLOC_FUNCTIONS_name(type, name) \
DECLARE_ASN1_ENCODE_FUNCTIONS(type, name, name)
# define DECLARE_ASN1_ALLOC_FUNCTIONS_name(type, name) \
type *name##_new(void); \
void name##_free(type *a);
# define DECLARE_ASN1_ENCODE_FUNCTIONS(type, itname, name) \
type *d2i_##name(type **a, const unsigned char **in, long len); \
int i2d_##name(type *a, unsigned char **out); \
DECLARE_ASN1_ITEM(itname)
# define DECLARE_ASN1_ITEM(name) \
OPENSSL_EXTERN const ASN1_ITEM name##_it;
# define ASN1_ITEM_ref(iptr) (&(iptr##_it))
# define ASN1_ITEM_rptr(ref) (&(ref##_it))
拿 DECLARE_ASN1_FUNCTIONS(ASN1_INTEGER) 来说,宏定义展开形式为:
ASN1_INTEGER *ASN1_INTEGER_new(void);
void ASN1_INTEGER_free(ASN1_INTEGER *a);
type *d2i_ASN1_INTEGER(ASN1_INTEGER **a, const unsigned char **in, long len);
int i2d_ASN1_INTEGER(ASN1_INTEGER *a, unsigned char **out);
const ASN1_ITEM ASN1_INTEGER_it;
还有几个宏负责为数据类型实现相应的助记函数。如下:
# define IMPLEMENT_ASN1_FUNCTIONS(stname) IMPLEMENT_ASN1_FUNCTIONS_fname(stname, stname, stname)
# define IMPLEMENT_ASN1_FUNCTIONS_name(stname, itname) IMPLEMENT_ASN1_FUNCTIONS_fname(stname, itname, itname)
# define IMPLEMENT_ASN1_FUNCTIONS_fname(stname, itname, fname) \
IMPLEMENT_ASN1_ALLOC_FUNCTIONS_fname(stname, itname, fname) \
IMPLEMENT_ASN1_ENCODE_FUNCTIONS_fname(stname, itname, fname)
# define IMPLEMENT_ASN1_ALLOC_FUNCTIONS_fname(stname, itname, fname) \
stname *fname##_new(void) \
{ \
return (stname *)ASN1_item_new(ASN1_ITEM_rptr(itname)); \
} \
void fname##_free(stname *a) \
{ \
ASN1_item_free((ASN1_VALUE *)a, ASN1_ITEM_rptr(itname)); \
}
# define IMPLEMENT_ASN1_ENCODE_FUNCTIONS_fname(stname, itname, fname) \
stname *d2i_##fname(stname **a, const unsigned char **in, long len) \
{ \
return (stname *)ASN1_item_d2i((ASN1_VALUE **)a, in, len, ASN1_ITEM_rptr(itname));\
} \
int i2d_##fname(stname *a, unsigned char **out) \
{ \
return ASN1_item_i2d((ASN1_VALUE *)a, out, ASN1_ITEM_rptr(itname));\
}
拿 IMPLEMENT_ASN1_ITEM(ASN1_INTEGER) 来说,其展开形式为:
ASN1_INTEGER *ASN1_INTEGER_new(void)
{
return (ASN1_INTEGER*)ASN1_item_new(ASN1_ITEM_rptr(ASN1_INTEGER));
}
void ASN1_INTEGER_free(ASN1_INTEGER *a)
{
ASN1_item_free((ASN1_VALUE *)a, ASN1_ITEM_rptr(ASN1_INTEGER));
}
ASN1_INTEGER *d2i_ASN1_INTEGER(ASN1_INTEGER **a, const unsigned char **in, long len)
{
return (ASN1_INTEGER*)ASN1_item_d2i((ASN1_VALUE **)a, in, len, ASN1_ITEM_rptr(ASN1_INTEGER));
}
int i2d_ASN1_INTEGER(ASN1_INTEGER *a, unsigned char **out)
{
return ASN1_item_i2d((ASN1_VALUE *)a, out, ASN1_ITEM_rptr(ASN1_INTEGER));
}
可以看到,展开形式所实现的助记函数实际上是对ASN1内部几个函数的封装调用,对提供上层数据类型与内部类型的转换,其中ASN1_VALUE是类型上下转换的纽带,它是个万能指针:
typedef struct ASN1_VALUE_st ASN1_VALUE;
在asn1.h中,对基本数据类型声明了助记函数:
DECLARE_ASN1_FUNCTIONS(ASN1_INTEGER)
DECLARE_ASN1_FUNCTIONS(ASN1_ENUMERATED)
DECLARE_ASN1_FUNCTIONS(ASN1_BIT_STRING)
DECLARE_ASN1_FUNCTIONS(ASN1_OCTET_STRING)
DECLARE_ASN1_FUNCTIONS(ASN1_NULL)
DECLARE_ASN1_FUNCTIONS(ASN1_UTCTIME)
DECLARE_ASN1_FUNCTIONS(ASN1_GENERALIZEDTIME)
DECLARE_ASN1_FUNCTIONS(ASN1_TIME)
DECLARE_ASN1_FUNCTIONS(ASN1_UTF8STRING)
可是我暂时还没有找到对基本数据类型助记函数的宏实现。
自定义结构体支持:
OpenSSL的ASN1编码处理依赖ASN1_ITEM对象,对基本类型来说,OpenSSL本身已经做了声明和初使化,所以可以直接以相关函数中使用。然而对于自定义结构体,需要开发者来定义ASN1_ITEM对象和规则,有一些宏可以提供帮助,摘录部分如下:
# define ASN1_SEQUENCE(tname) \
static const ASN1_TEMPLATE tname##_seq_tt[]
# define ASN1_SEQUENCE_END(stname) ASN1_SEQUENCE_END_name(stname, stname)
# define ASN1_SEQUENCE_END_name(stname, tname) \
;\
ASN1_ITEM_start(tname) \
ASN1_ITYPE_SEQUENCE,\
V_ASN1_SEQUENCE,\
tname##_seq_tt,\
sizeof(tname##_seq_tt) / sizeof(ASN1_TEMPLATE),\
NULL,\
sizeof(stname),\
#tname \
ASN1_ITEM_end(tname)
# define ASN1_ITEM_start(itname) \
const ASN1_ITEM itname##_it = {
# define ASN1_ITEM_end(itname) \
};
# define ASN1_SIMPLE(stname, field, type) ASN1_EX_TYPE(0,0, stname, field, type)
# define ASN1_EX_TYPE(flags, tag, stname, field, type) { \
(flags), (tag), offsetof(stname, field),\
#field, ASN1_ITEM_ref(type) }
在ASN1_ITEM结构中,ASN1_TEMPLATE的结构定义如下:
struct ASN1_TEMPLATE_st {
unsigned long flags; /* Various flags */
long tag; /* tag, not used if no tagging */
unsigned long offset; /* Offset of this field in structure */
const char *field_name; /* Field name */
ASN1_ITEM_EXP *item; /* Relevant ASN1_ITEM or ASN1_ADB */
};
typedef struct ASN1_TEMPLATE_st ASN1_TEMPLATE;
主要字段含义:
offset —— 字段在父结构中的偏移。
field_name —— 字段名称。
item —— 指向字段对应结构类型的ASN1_ITEM对象。
举例定义如下的自定义结构:
typedef struct X509V4_VALID_st
{
ASN1_GENERALIZEDTIME *notBefore;
ASN1_GENERALIZEDTIME *notAfter;
} X509V4_VALID;
DECLARE_ASN1_FUNCTIONS(X509V4_VALID)
ASN1_SEQUENCE(X509V4_VALID) =
{
ASN1_SIMPLE(X509V4_VALID, notBefore, ASN1_GENERALIZEDTIME),
ASN1_SIMPLE(X509V4_VALID, notAfter, ASN1_GENERALIZEDTIME)
}
ASN1_SEQUENCE_END(X509V4_VALID)
IMPLEMENT_ASN1_FUNCTIONS(X509V4_VALID)
将宏展开,为:
static const ASN1_TEMPLATE X509V4_VALID_seq_tt[] =
{
{ 0, 0, offsetof(X509V4_VALID, notBefore), notBefore, ASN1_ITEM_ref(ASN1_GENERALIZEDTIME) },
{ 0, 0, offsetof(X509V4_VALID, notAfter), notAfter, ASN1_ITEM_ref(ASN1_GENERALIZEDTIME) }
};
const ASN1_ITEM X509V4_VALID_it = {
ASN1_ITYPE_SEQUENCE,\
V_ASN1_SEQUENCE,\
X509V4_VALID_seq_tt,\
sizeof(X509V4_VALID_seq_tt) / sizeof(ASN1_TEMPLATE),\
NULL,\
sizeof(X509V4_VALID,),\
X509V4_VALID \
};
常用函数:
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,将内部分配内存,但必须由外部使用者释放。
其内部实现为:
{
ASN1_TLC c;
ASN1_VALUE *ptmpval = NULL;
if (!pval)
pval = &ptmpval;
asn1_tlc_clear_nc(&c);
if (ASN1_item_ex_d2i(pval, in, len, it, -1, 0, 0, &c) > 0)
return *pval;
return NULL;
}
可以看到内部做了为val传入NULL的兼容性处理。
int ASN1_item_i2d(ASN1_VALUE *val, unsigned char **out, const ASN1_ITEM *it);
根据ITEM对象将二进制结构转换为DER编码。
成功返回DER编码长度,失败返回负数。
如果*out为NULL,将内部分配内存,但内存必须由外部使用者释放。若**out为NULL,将仅返回长度,这用于试探长度的场景。
其内部实现为:
{
if (out && !*out) {
unsigned char *p, *buf;
int len;
len = ASN1_item_ex_i2d(&val, NULL, it, -1, flags);
if (len <= 0)
return len;
if ((buf = OPENSSL_malloc(len)) == NULL) {
ASN1err(ASN1_F_ASN1_ITEM_FLAGS_I2D, ERR_R_MALLOC_FAILURE);
return -1;
}
p = buf;
ASN1_item_ex_i2d(&val, &p, it, -1, flags);
*out = buf;
return len;
}
return ASN1_item_ex_i2d(&val, out, it, -1, flags);
}
可以看到内部做了为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()的封装调用。
其内部实现为:
{
const unsigned char *p;
void *ret = NULL;
int len;
BUF_MEM *b = NULL;
len = asn1_d2i_read_bio(in, &b);
if (len < 0)
goto err;
p = (const unsigned char *)b->data;
ret = ASN1_item_d2i(x, &p, len, it);
err:
BUF_MEM_free(b);
return ret;
}
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 i, j = 0, n, ret = 1;
unsigned char *b = NULL;
n = ASN1_item_i2d(x, &b, it);
if (b == NULL) {
ASN1err(ASN1_F_ASN1_ITEM_I2D_BIO, ERR_R_MALLOC_FAILURE);
return 0;
}
for (;;) {
i = BIO_write(out, &(b[j]), n);
if (i == n)
break;
if (i <= 0) {
ret = 0;
break;
}
j += i;
n -= i;
}
OPENSSL_free(b);
return ret;
}
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。
其内部实现为:
{
BUF_MEM *b = NULL;
int len = asn1_d2i_read_bio(in, &b);
if (len < 0)
goto err;
const unsigned char *p = (unsigned char *)b->data;
void *ret = d2i(x, &p, len);
err:
BUF_MEM_free(b);
return ret;
}
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 ret = 1;
int n = i2d(x, NULL);
if (n <= 0)
return 0;
char* b = OPENSSL_malloc(n);
unsigned char *p = (unsigned char *)b;
i2d(x, &p);
int i, j = 0;
for (;;) {
i = BIO_write(out, &(b[j]), n);
if (i == n)
break;
if (i <= 0) {
ret = 0;
break;
}
j += i;
n -= i;
}
OPENSSL_free(b);
return ret;
}
int ASN1_i2d_fp(i2d_of_void *i2d, FILE *out, void *x);
ASN1_i2d_bio() 的FILE版本。
上面d2i_of_void 和 i2d_of_void 函数的签名为:
# define D2I_OF(type) type *(*)(type **,const unsigned char **,long)
# define I2D_OF(type) int (*)(type *,unsigned char **)
通常为数据类型的转换助记函数。
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字符串长度。
使用举例:
下面这个例子测试了很多函数的用法,请自行放开注释并测试。
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <openssl/asn1.h>
#include <openssl/asn1t.h>
namespace dakuang {}
void printHex(const char* pBuf, int nLen)
{
for (int i = 0; i < nLen; ++i)
{
printf("%02x", (unsigned char)pBuf[i]);
}
printf("\n");
}
int main(int argc, char* argv[])
{
/*
int len = a2d_ASN1_OBJECT(NULL, 0, "1.2.840.10040.4.1", -1);
printf("len:%d \n", len);
char* pBuf = (char*)malloc(len);
int ret = a2d_ASN1_OBJECT((unsigned char *)pBuf, len, "1.2.840.10040.4.1", -1);
printf("ret:%d \n", ret);
printf("buf => ");
printHex(pBuf, len);
free(pBuf);
*/
/*
BIO* bp = BIO_new(BIO_s_mem());
BIO_write(bp, "0FAB08BBDDEECC", 14);
ASN1_INTEGER* i = ASN1_INTEGER_new();
char sBuf[50] = {0};
int ret = a2i_ASN1_INTEGER(bp, i, sBuf, 50);
printf("ret:%d \n", ret);
long l = ASN1_INTEGER_get(i);
printf("l:[%ld] \n", l);
ASN1_INTEGER_free(i);
BIO_free(bp);
*/
/*
BIO* bp = BIO_new(BIO_s_mem());
BIO_write(bp, "B2E2CAD4", 8);
ASN1_STRING* str = ASN1_STRING_new();
char sBuf[50] = {0};
int ret = a2i_ASN1_STRING(bp, str, sBuf, 50);
printf("ret:%d \n", ret);
int len = ASN1_STRING_length(str);
printf("len:%d \n", len);
printf("buf => ");
printHex(sBuf, len);
unsigned char* p = ASN1_STRING_data(str);
printf("str:[%s] \n", p);
ASN1_STRING_free(str);
BIO_free(bp);
*/
/*
ASN1_INTEGER* i = ASN1_INTEGER_new();
int ret = ASN1_INTEGER_set(i, 123);
printf("ret:%d \n", ret);
long l = ASN1_INTEGER_get(i);
printf("l:[%ld] \n", l);
ASN1_INTEGER_free(i);
*/
/*
ASN1_STRING* str = ASN1_STRING_new();
int ret = ASN1_STRING_set(str, "abc", 3);
printf("ret:%d \n", ret);
unsigned char* p = ASN1_STRING_data(str);
printf("str:[%s] \n", p);
ASN1_STRING_free(str);
*/
/*
ASN1_STRING* str = ASN1_STRING_new();
int ret = ASN1_STRING_set(str, "abc", 3);
printf("ret:%d \n", ret);
BIO* bp = BIO_new(BIO_s_file());
BIO_set_fp(bp, stdout, BIO_NOCLOSE);
ret = i2a_ASN1_STRING(bp, str, 0);
printf("ret:%d \n", ret);
BIO_free(bp);
ASN1_STRING_free(str);
*/
/*
BIO* out = BIO_new_file("int.cer","w");
ASN1_INTEGER* a = ASN1_INTEGER_new();
ASN1_INTEGER_set(a, 100);
int ret = ASN1_i2d_bio((i2d_of_void*)i2d_ASN1_INTEGER, out, (unsigned char*)a);
printf("ret:%d \n", ret);
BIO_free(out);
*/
/*
BIO* in = BIO_new_file("int.cer","r");
ASN1_INTEGER* a = ASN1_INTEGER_new();
ASN1_d2i_bio(NULL, (d2i_of_void *)d2i_ASN1_INTEGER, in, (void**)&a);
long l = ASN1_INTEGER_get(a);
printf("l:[%ld] \n", l);
ASN1_INTEGER_free(a);
BIO_free(in);
*/
/*
char buf[50] = {0};
int ret = a2d_ASN1_OBJECT((unsigned char*)buf, 50, "1.2.3.4", -1);
printf("ret:%d \n", ret);
printf("buf => ");
printHex(buf, ret);
ASN1_OBJECT* o = ASN1_OBJECT_new();
printf("o:[%p] \n", o);
d2i_ASN1_OBJECT(&o, (const unsigned char**)&buf, ret);
ret = i2d_ASN1_OBJECT(o, (unsigned char**)&buf);
printf("ret:%d \n", ret);
BIO* bp = BIO_new(BIO_s_file());
BIO_set_fp(bp, stdout, BIO_NOCLOSE);
ret = i2a_ASN1_OBJECT(bp, o);
printf("ret:%d \n", ret);
BIO_free(bp);
ASN1_OBJECT_free(o);
*/
return 0;
}