第十三章 NSA1库 / 13.7 ASN1常用函数

       ASN1的基本的数据类型一般都有如下函数:newfreei2dd2ii2aa2iprintsetgetcmpdup。其中newfreei2dd2i函数通过宏定义实现。new函数用于分配空间,生成ASN1数据结构;free用于释放空间;i2d函数将ASN1数据结构转换为DER编码;d2iDER编码转换为ASN1数据结构,i2a将内部结构转换为ASCII码,a2iASCII码转换为内部数据结构。set函数用于设置ASN1类型的值,get函数用于获取ASN1类型值;printASN1类型打印;cmp用于比较ASN1数据结构;dup函数进行数据结构的拷贝。

       常用的函数有:

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

       计算OIDDER编码,比如将2.99999.3形式转换为内存形式。示例:

      #include <openssl/asn1.h>

       void main()

       {

              const             char        oid[]={"2.99999.3"};

              int                                       i;

              unsigned  char        *buf;

      

              i=a2d_ASN1_OBJECT(NULL,0,oid,-1);

              if (i <= 0)

                     return;

              buf=(unsigned char *)malloc(sizeof(unsigned char)*i);

              i=a2d_ASN1_OBJECT(buf,i,oid,-1);

              free(buf);

              return;

       }

   输出结果:buf内存值为:86 8D 6F 03

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

bp中的ASC码转换为ASN1_INTEGER,buf存放BIO中的ASC码。示例如下:

#include <openssl/asn1.h>

int    main()

{

              BIO                      *bp;

              ASN1_INTEGER   *i;

              unsigned charbuf[50];

              int                         size,len;

 

              bp=BIO_new(BIO_s_mem());

              len=BIO_write(bp,"0FAB08BBDDEECC",14);

              size=50;

              i=ASN1_INTEGER_new();

              a2i_ASN1_INTEGER(bp,i,buf,size);

              BIO_free(bp);

              ASN1_INTEGER_free(i);

              return 0;

}

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

ASCII码转换为ASN1_STRING,示例:

#include <openssl/asn1.h>

int    main()

{

              BIO                      *bp;

              ASN1_STRING            *str;

              unsigned charbuf[50];

              int                         size,len;

 

              bp=BIO_new(BIO_s_mem());

              len=BIO_write(bp,"B2E2CAD4",8);

              size=50;

              str=ASN1_STRING_new();

              a2i_ASN1_STRING(bp,str,buf,size);

              BIO_free(bp);

              ASN1_STRING_free(str);

              return 0;

}

转换后str->data的前四个字节即变成"测试"

4unsigned char *asc2uni(const char *asc, int asclen, unsigned char **uni, int *unilen)

ASCII码转换为unicode,示例:

#include <stdio.h>

#include <openssl/crypto.h>

int    main()

{

              unsigned charasc[50]={"B2E2CAD4"};

              unsigned charuni[50],*p,*q;

              int                         ascLen,unlen;

 

              ascLen=strlen(asc);

              q=asc2uni(asc,ascLen,NULL,&unlen);

              OPENSSL_free(q);

              return 0;

}

5int ASN1_BIT_STRING_get_bit(ASN1_BIT_STRING *a, int n)

本函数根据n获取其比特位上的值,示例:

#include <openssl/asn1.h>

int     main()

{

        int     ret,i,n;

        ASN1_BIT_STRING *a;

 

        a=ASN1_BIT_STRING_new();

        ASN1_BIT_STRING_set(a,"ab",2);

        for(i=0;i<2*8;i++)

        {

                ret=ASN1_BIT_STRING_get_bit(a,i);

                printf("%d",ret);

        }

              ASN1_BIT_STRING_free(a);

        return 0;

}

程序输出:0110000101100010

说明:a”ab”的二进制既是0110000101100010

6ASN1_BIT_STRING_set

设置ASN1_BIT_STRING的值,它调用了ASN1_STRING_set函数;

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

bio的数据DER解码,xnew无意义,d2iDER解码函数,inbio数据,x为数据类型,返回值为解码后的结果。如果x分配了内存,x所指向的地址与返回值一致。示例如下:

#include <stdio.h>

#include <openssl/asn1.h>

#include <openssl/x509v3.h>

#include <openssl/bio.h>

int    main()

{

              BIO        *in;

              X509      **out=NULL,*x;

 

              in=BIO_new_file("a.cer","r");

              out=(X509 **)malloc(sizeof(X509 *));

              *out=NULL;

              x=ASN1_d2i_bio(NULL,(d2i_of_void *)d2i_X509,in,out);

              X509_free(x);

              free(out);

              return 0;

}

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

in指向的文件进行DER解码,其内部调用了ASN1_d2i_bi函数,用法与ASN1_d2i_bi类似。

9int ASN1_digest(i2d_of_void *i2d, const EVP_MD *type,

char *data,unsigned char *md, unsigned int *len)

ASN1数据类型签名。将data指针指向的ASN1数据类型用i2d函数进行DER编码,然后用type指定的摘要方法进行计算,结果存放在md中,结果的长度由len表示。

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

ASN1数据结构DER编码,并将结果写入bio。示例如下:

#include <openssl/asn1.h>

#include <openssl/bio.h>

int     main()

{

           int             ret;

        BIO             *out;

        ASN1_INTEGER    *a;

 

        out=BIO_new_file("int.cer","w");

        a=ASN1_INTEGER_new();

        ASN1_INTEGER_set(a,(long)100);

        ret=ASN1_i2d_bio(i2d_ASN1_INTEGER,out,a);

        BIO_free(out);

        return 0;

}

本程序将ASN1_INTEGER类型装换为DER编码并写入文件。int.cer的内容如下:

02 01 64  (十六进制)。

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

ASN1数据结构DER编码并写入FILE,此函数调用了ASN1_i2d_bio

12void *ASN1_dup(i2d_of_void *i2d, d2i_of_void *d2i, char *x)

ASN1数据复制。xASN1内部数据结构,本函数先将x通过i2d将它变成DER编码,然后用d2iDER解码,并返回解码结果。

13)  ASN1_ENUMERATED_set

设置ASN1_ENUMERATED的值。

14)  ASN1_ENUMERATED_get

获取ASN1_ENUMERATED的值;示例如下:

clude <openssl/asn1.h>

int    main()

{

              long                      ret;

              ASN1_ENUMERATED         *a;

 

              a=ASN1_ENUMERATED_new();

              ASN1_ENUMERATED_set(a,(long)155);

              ret=ASN1_ENUMERATED_get(a);

              printf("%ld\n",ret);

              return 0;

}

15BIGNUM *ASN1_ENUMERATED_to_BN(ASN1_ENUMERATED *ai, BIGNUM *bn)

ASN1_ENUMERATED类型转换为BN大数类型。此函数调用BN_bin2bn函数获取bn,如果ai->type表明它是负数,再调用BN_set_negative设置bn成负数。示例如下:

#include <openssl/asn1.h>

int    main()

{

              long                      ret;

              ASN1_ENUMERATED         *a;

              BIGNUM                      *bn;

 

              a=ASN1_ENUMERATED_new();

              ASN1_ENUMERATED_set(a,(long)155);

              ret=ASN1_ENUMERATED_get(a);

              bn=BN_new();

              bn=ASN1_ENUMERATED_to_BN(a,bn);

              BN_free(bn);

              ASN1_ENUMERATED_free(a);

              return 0;

}

如果ASN1_ENUMERATED_to_BN的第二个参数为NULL,bn将在内部分配空间。

16int ASN1_GENERALIZEDTIME_check(ASN1_GENERALIZEDTIME *a)

检查输入参数是不是合法的ASN1_GENERALIZEDTIME类型。

17int ASN1_parse_dump(BIO *bp, const unsigned char *pp, long len, int indent, int dump)

本函数用于将pplen指明的DER编码值写在BIO中,其中indentdump用于设置打印的格式。indent用来设置打印出来当列之间空格个数,ident越小,打印内容越紧凑。dump表明当asn1单元为BIT STRINGOCTET STRING时,打印内容的字节数。示例如下:

#include <openssl/bio.h>

#include <openssl/asn1.h>

int    main()

{

              int           ret,len,indent,dump;

              BIO        *bp;

              char*pp,buf[5000];

              FILE       *fp;

              bp=BIO_new(BIO_s_file());

              BIO_set_fp(bp,stdout,BIO_NOCLOSE);

              fp=fopen("der.cer","rb");

              len=fread(buf,1,5000,fp);

              fclose(fp);

              pp=buf;

              indent=7;

              dump=11;

              ret=ASN1_parse_dump(bp,pp,len,indent,dump);

              BIO_free(bp);

              return 0;

}

其中der.cer为一个DER编码的文件,比如一个数字证书。

18int ASN1_sign(i2d_of_void *i2d, X509_ALGOR *algor1, X509_ALGOR *algor2,          ASN1_BIT_STRING *signature, char *data, EVP_PKEY *pkey, const EVP_MD *type)

ASN1数据类型签名。i2dASN1数据的DER方法,signature用于存放签名结果,dataASN1数据指针,pkey指明签名密钥,type为摘要算法,algor1algor2无用,可全为NULL。签名时,先将ASN1数据DER编码,然后摘要,最后签名运算。

x509.h中有很多ASN1数据类型的签名都通过此函数来定义,有X509_signX509_REQ_signX509_CRL_signNETSCAPE_SPKI_sign等。示例如下:

#include <openssl/asn1.h>

#include <openssl/rsa.h>

#include <openssl/evp.h>

int    main()

{

              int                         ret;

              ASN1_INTEGER   *a;

              EVP_MD               *md;

              EVP_PKEY            *pkey;

              char               *data;

              ASN1_BIT_STRING     *signature=NULL;

              RSA                      *r;

              int                         i,bits=1024;

              unsigned long  e=RSA_3;

              BIGNUM               *bne;

      

              bne=BN_new();

              ret=BN_set_word(bne,e);

              r=RSA_new();

              ret=RSA_generate_key_ex(r,bits,bne,NULL);

              if(ret!=1)

              {

                     printf("RSA_generate_key_ex err!\n");

                     return -1;

              }

              pkey=EVP_PKEY_new();

              EVP_PKEY_assign_RSA(pkey,r);

              a=ASN1_INTEGER_new();

              ASN1_INTEGER_set(a,100);

              md=EVP_md5();

              data=(char *)a;

              signature=ASN1_BIT_STRING_new();

              ret=ASN1_sign(i2d_ASN1_INTEGER,NULL,NULL,signature,data,pkey,md);

              printf("signature len : %d\n",ret);

              EVP_PKEY_free(pkey);

              ASN1_INTEGER_free(a);

              free(signature);

              return 0;

}本例将ASN1_INTEGER整数签名。

19ASN1_STRING *ASN1_STRING_dup(ASN1_STRING *str)

ASN1_STRING类型拷贝。内部申请空间,需要用户调用ASN1_STRING_free释放该空间。

20int ASN1_STRING_cmp(ASN1_STRING *a, ASN1_STRING *b)

ASN1_STRING比较。ossl_typ.h中绝大多数ASN1基本类型都定义为ASN1_STRING,所以,此函数比较通用。示例如下:

#include <openssl/asn1.h>

int    main()

{

              int    ret;

              ASN1_STRING     *a,*b,*c;

              a=ASN1_STRING_new();

              b=ASN1_STRING_new();

              ASN1_STRING_set(a,"abc",3);

              ASN1_STRING_set(b,"def",3);

              ret=ASN1_STRING_cmp(a,b);

              printf("%d\n",ret);

              c=ASN1_STRING_dup(a);

              ret=ASN1_STRING_cmp(a,c);

              printf("%d\n",ret);

              ASN1_STRING_free(a);

              ASN1_STRING_free(b);

              ASN1_STRING_free(c);

              return 0;

}

21unsigned char * ASN1_STRING_data(ASN1_STRING *x)

获取ASN1_STRING数据存放地址,即ASN1_STRING数据结构中data地址。本函数由宏实现。

22int ASN1_STRING_set(ASN1_STRING *str, const void *_data, int len)

设置ASN1字符串类型的值。strASN1_STRING地址,_data为设置值的首地址,len为被设置值的长度。示例如下:

ASN1_STRING     *str=NULL;

str=ASN1_STRING_new();

ASN1_STRING_set(str,”abc”,3);

此示例生成的ASN1_STRING类型为OCTET_STRING。其他的ASN1_STRING类型也能用此函数设置,如下:

ASN1_PRINTABLESTRING*str=NULL;

str=ASN1_PRINTABLESTRING_new();

ASN1_STRING_set(str,”abc”,3);

23ASN1_STRING_TABLE *ASN1_STRING_TABLE_get(int nid)

根据nid来查找ASN1_STRING_TABLE表。此函数先查找标准表tbl_standard,再查找扩展表stableASN1_STRING_TABLE数据结构在asn1.h中定义,它用于约束ASN1_STRING_set_by_NID函数生成的ASN1_STRING类型。

typedef struct asn1_string_table_st {

              int nid;

              long minsize;

              long maxsize;

              unsigned long mask;

              unsigned long flags;

} ASN1_STRING_TABLE;

其中nid表示对象idminsize表示此nid值的最小长度,maxsize表示此nid值的最大长度,mask为此nid可以采用的ASN1_STRING类型:B_ASN1_BMPSTRINGB_ASN1_UTF8STRINGB_ASN1_T61STRINGB_ASN1_UTF8STRINGflags用于标记是否为扩展或是否已有mask

24ASN1_STRING *ASN1_STRING_set_by_NID(ASN1_STRING **out, const unsigned char *in,  int inlen, int inform, int nid)

根据nid和输入值获取对应的ASN1_STIRNG类型。out为输出,in为输入数据,inlen为其长度,inform为输入数据的类型,可以的值有:MBSTRING_BMPMBSTRING_UNIVMBSTRING_UTF8MBSTRING_ASCnid为数字证书中常用的nid,在a_strnid.c中由全局变量tbl_standard定义,可以的值有:NID_commonNameNID_countryNameNID_localityNameNID_stateOrProvinceNameNID_organizationNameNID_organizationalUnitNameNID_pkcs9_emailAddressNID_pkcs9_unstructuredNameNID_pkcs9_challengePasswordNID_pkcs9_unstructuredAddressNID_givenNameNID_surnameNID_initialsNID_serialNumberNID_friendlyNameNID_nameNID_dnQualifierNID_domainComponentNID_ms_csp_name。生成的ASN1_STRING类型可以为:ASN1_T61STRINGASN1_IA5STRINGASN1_PRINTABLESTRINGASN1_BMPSTRINGASN1_UNIVERSALSTRINGASN1_UTF8STRING

示例1

#include <stdio.h>

#include <openssl/asn1.h>

#include <openssl/obj_mac.h>

int    main()

{

              int           inlen,nid,inform,len;

              charin[100],out[100],*p;

              ASN1_STRING     *a;

              FILE       *fp;

      

              /* 汉字“赵”的UTF8,可以用UltraEdit获取*/

              memset(&in[0],0xEF,1);

              memset(&in[1],0xBB,1);

              memset(&in[2],0xBF,1);

              memset(&in[3],0xE8,1);

              memset(&in[4],0xB5,1);

              memset(&in[5],0xB5,1);

              inlen=6;

              inform=MBSTRING_UTF8;

              nid=NID_commonName;

       /* 如果调用下面两个函数,生成的ASN1_STRING类型将是ASN1_UTF8而不是ASN1_BMPSTRING     */

ASN1_STRING_set_default_mask(B_ASN1_UTF8STRING);

              ret=ASN1_STRING_set_default_mask_asc("utf8only");

              if(ret!=1)

              {

                     printf("ASN1_STRING_set_default_mask_asc err.\n");

                     return 0;

              }

              a=ASN1_STRING_set_by_NID(NULL,in,inlen,inform,nid);

              p=out;

              len=i2d_ASN1_BMPSTRING(a,&p);

              fp=fopen("a.cer","w");

              fwrite(out,1,len,fp);

              fclose(fp);

              ASN1_STRING_free(a);

              return 0;

}

本例根据UTF8编码的汉字获取nidNID_commonNameASN1_STRING类型,其结果是一个ASN1_BMPSTRING类型。

示例2

#include <stdio.h>

#include <openssl/asn1.h>

#include <openssl/obj_mac.h>

int    main()

{

              int           inlen,nid,inform,len;

              charin[100],out[100],*p;

              ASN1_STRING     *a;

              FILE       *fp;

 

              strcpy(in,"ab");

              inlen=2;

              inform=MBSTRING_ASC;

              nid=NID_commonName;

              /* 设置生成的ASN1_STRING类型 */

ASN1_STRING_set_default_mask(B_ASN1_UTF8STRING);

              a=ASN1_STRING_set_by_NID(NULL,in,inlen,inform,nid);

              switch(a->type)

              {

              caseV_ASN1_T61STRING:

                     printf("V_ASN1_T61STRING\n");

                     break;

              caseV_ASN1_IA5STRING:

                     printf("V_ASN1_IA5STRING\n");

                     break;

              caseV_ASN1_PRINTABLESTRING:

                     printf("V_ASN1_PRINTABLESTRING\n");

                     break;

              caseV_ASN1_BMPSTRING:

                     printf("V_ASN1_BMPSTRING\n");

                     break;

              caseV_ASN1_UNIVERSALSTRING:

                     printf("V_ASN1_UNIVERSALSTRING\n");

                     break;

              caseV_ASN1_UTF8STRING:

                     printf("V_ASN1_UTF8STRING\n");

                     break;

              default:

                     printf("err");

                     break;

              }

              p=out;

              len=i2d_ASN1_bytes(a,&p,a->type,V_ASN1_UNIVERSAL);

              fp=fopen("a.cer","w");

              fwrite(out,1,len,fp);

              fclose(fp);

              ASN1_STRING_free(a);

              getchar();

              return 0;

}

25void ASN1_STRING_set_default_mask(unsigned long mask)

设置ASN1_STRING_set_by_NID函数返回的ASN1_STRING类型。mask可以取如下值:B_ASN1_BMPSTRINGB_ASN1_UTF8STRINGB_ASN1_T61STRINGB_ASN1_UTF8STRING

26int ASN1_STRING_set_default_mask_asc(char *p)

设置ASN1_STRING_set_by_NID函数返回的ASN1_STRING类型。字符串p可以的值有:nombstrpkixutf8onlydefault,如果设置为default,则相当于没有调用本函数。

27int ASN1_STRING_TABLE_add(int nid, long minsize, long maxsize, unsigned long mask,                           unsigned long flags)

添加扩展的ASN1_STRING_TABLE项。说明:a_strnid.c中定义了基本的ASN1_STRING_TABLE项,如果用户要添加新的ASN1_STRING_TABLE项,需要调此次函数。Openssl源代码中有好几处都有这种用法,Openssl定义标准的某种表,并且提供扩展函数供用户去扩充。

示例:ASN1_STRING_TABLE_addNID_yourNID,1,100, DIRSTRING_TYPE,0)。

28void ASN1_STRING_TABLE_cleanup(void)

清除用户自建的扩展ASN1_STRING_TABLE表。

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

将整数转换成为ASCII,放在BIO中。示例如下:

#include <openssl/asn1.h>

int    main()

{

              ASN1_INTEGER   *i;

              long               v;

              BIO                      *bp;

 

              printf("输入v的值:\n");

              scanf("%ld",&v);

              i=ASN1_INTEGER_new();

              ASN1_INTEGER_set(i,v);

              bp=BIO_new(BIO_s_file());

              BIO_set_fp(bp,stdout,BIO_NOCLOSE);

              i2a_ASN1_INTEGER(bp,i);

              BIO_free(bp);

              ASN1_INTEGER_free(i);

              printf("\n");

              return 0;

}

输出:

输入v的值:

15

0F

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

type不起作用,将ASN1_STRING转换为ASCII.。示例如下:

#include <openssl/asn1.h>

#include <openssl/asn1t.h>

int    main()

{

              ASN1_STRING            *a;

              BIO                      *bp;

 

              a=ASN1_STRING_new();

              ASN1_STRING_set(a,"测试",4);

              bp=BIO_new(BIO_s_file());

              BIO_set_fp(bp,stdout,BIO_NOCLOSE);

              i2a_ASN1_STRING(bp,a,1);

              BIO_free(bp);

              ASN1_STRING_free(a);

              printf("\n");

              return 0;

}

输出结果:

       B2E2CAD4

31OBJ_bsearch

用于从排序好的数据结构地址数组中用二分法查找数据。示例如下:

#include <openssl/objects.h>

typedef    struct      Student_st

{

              int           age;

}Student;

int    cmp_func(const void *a,const void *b)

{

              Student *x,*y;

              x=*(Student **)a;

              y=*(Student **)b;

              return x->age-y->age;

}

int    main()

{

              int                         ret,num,size;

              ASN1_OBJECT            *obj=NULL;

              char               **addr,*p;

              Student                 a[6],**sort,**x;

      

              a[0].age=3;

              a[1].age=56;

              a[2].age=5;

              a[3].age=1;

              a[4].age=3;

              a[5].age=6;

              sort=(Student **)malloc(6*sizeof(Student *));

              sort[0]=&a[0];

              sort[1]=&a[1];

              sort[2]=&a[2];

              sort[3]=&a[3];

              sort[4]=&a[4];

              sort[5]=&a[5];

      

              qsort(sort,6,sizeof(Student *),cmp_func);

              obj=OBJ_nid2obj(NID_rsa);

              ret=OBJ_add_object(obj);

              if(ret==NID_undef)

              {

                     printf("err");

              }

              else

              {

                     printf("ok\n");

              }

              p=&a[4];

              addr=OBJ_bsearch(&p,(char *)sort,6,sizeof(Student *),cmp_func);

              x=(Student **)addr;

              printf("%d == %d\n",a[4].age,(*x)->age);

              return 0;

}

32OBJ_create

根据oid以及名称信息生成一个内部的object,示例:

nid=OBJ_create("1.2.3.44","testSn","testLn")

33OBJ_NAME_add

OBJ_NAME_cleanup

OBJ_NAME_get

OBJ_NAME_init

OBJ_NAME_remove

OBJ_NAME_new_index

OBJ_NAME_do_all

OBJ_NAME_do_all_sorted

OBJ_NAME函数用于根据名字获取对称算法或者摘要算法,主要涉及到函数有:

int EVP_add_cipher(const EVP_CIPHER *c);

int EVP_add_digest(const EVP_MD *md);

const EVP_CIPHER *EVP_get_cipherbyname(const char *name);

const EVP_MD *EVP_get_digestbyname(const char *name);

void EVP_cleanup(void);

这些函数在evp/names.c中实现,他们调用了OBJ_NAME函数。

EVP_add_cipherEVP_add_digest函数调用OBJ_NAME_initOBJ_NAME_add函数,将EVP_CIPHER