星期六, 十月 07, 2006

OpenSSL 公开加密算法中如何添加新算法

赵治国 zhaozg(at)gmail(dot)com http://zhaozg.googlepages.com/

2002-07-06: Word文档写完

2005-04-07: 发布于个人Blogger







OpenSSL 中公开密钥算法中,我仅仅讨论RSA算法模块!




与RSA相关的数据结构包括。

typedef struct rsa_st RSA;


typedef struct rsa_meth_st

{

const char *name;

int (*rsa_pub_enc)(int flen,unsigned char *from,unsigned char *to,

RSA *rsa,int padding);

int (*rsa_pub_dec)(int flen,unsigned char *from,unsigned char *to,

RSA *rsa,int padding);

int (*rsa_priv_enc)(int flen,unsigned char *from,unsigned char *to,

RSA *rsa,int padding);

int (*rsa_priv_dec)(int flen,unsigned char *from,unsigned char *to,

RSA *rsa,int padding);

int (*rsa_mod_exp)(BIGNUM *r0,BIGNUM *I,RSA *rsa); /* Can be null */

int (*bn_mod_exp)(BIGNUM *r, BIGNUM *a, const BIGNUM *p,

const BIGNUM *m, BN_CTX *ctx,

BN_MONT_CTX *m_ctx); /* Can be null */

int (*init)(RSA *rsa); /* called at new */

int (*finish)(RSA *rsa); /* called at free */

int flags; /* RSA_METHOD_FLAG_* things */

char *app_data; /* may be needed! */

/* New sign and verify functions: some libraries don't allow arbitrary data

* to be signed/verified: this allows them to be used. Note: for this to work

* the RSA_public_decrypt() and RSA_private_encrypt() should *NOT* be used

* RSA_sign(), RSA_verify() should be used instead. Note: for backwards

* compatibility this functionality is only enabled if the RSA_FLAG_SIGN_VER

* option is set in 'flags'.

*/

int (*rsa_sign)(int type, unsigned char *m, unsigned int m_len,

unsigned char *sigret, unsigned int *siglen, RSA *rsa);

int (*rsa_verify)(int dtype, unsigned char *m, unsigned int m_len,

unsigned char *sigbuf, unsigned int siglen, RSA *rsa);


} RSA_METHOD;


/* 对RSA算法的添加一般式提供可供OPENSSL模块使用的加速卡和加密卡接口,将操作接口采用上面的结构进行封装,并不是上面所有的接口都需要实现,但四个加解密函数以及初始化和结束函数应该是必须提供的*/


struct rsa_st

{

/* The first parameter is used to pickup errors where

* this is passed instead of aEVP_PKEY, it is set to 0 */

int pad;

int version;

#if 0

RSA_METHOD *meth;

#else

struct engine_st *engine;

#endif

BIGNUM *n; /* n=pq */

BIGNUM *e; /* e和n,为公开密钥,e与(p-1)(q-1)互为素数 */

BIGNUM *d; /* 私人密钥,ed=1mod(p-1)(q-1) */

BIGNUM *p; /* 大素数 p ,p q 互为素数,且长度一样 */

BIGNUM *q; /* 大素数 q */

BIGNUM *dmp1;

BIGNUM *dmq1;

BIGNUM *iqmp;

/* be careful using this if the RSA structure is shared */

CRYPTO_EX_DATA ex_data;

int references;

int flags;


/* Used to cache montgomery values */

BN_MONT_CTX *_method_mod_n;

BN_MONT_CTX *_method_mod_p;

BN_MONT_CTX *_method_mod_q;


/* all BIGNUM values are actually in the following data, if it is not

* NULL */

char *bignum_data;

BN_BLINDING *blinding;

};

/* 加密过程 c=me mod n 解密过程 m=cd mod n */

/* 根据加解密算法的需要,以及使用的是公钥还是私钥,上面的结构中的字段需要有选择地实现,在任何时候,对n和e,以及flags的初始化都是不可少的*/

/* 如果是为硬件密码引擎提供接口,可以参考crypt/engine/hw_*.c的各个模块*/


/* 关于大数的描述将在另外的文档中说明 */
/* Type needs to be a bit field

* Sub-type needs to be for variations on the method, as in, can it do

* arbitrary encryption.... */

typedef struct evp_pkey_st

{

int type;

int save_type;

int references;

union {

char *ptr;

#ifndef NO_RSA

struct rsa_st *rsa; /* RSA */

#endif

#ifndef NO_DSA

struct dsa_st *dsa; /* DSA */

#endif

#ifndef NO_DH

struct dh_st *dh; /* DH */

#endif

} pkey;

int save_parameters;

STACK_OF(X509_ATTRIBUTE) *attributes; /* [ 0 ] */

} EVP_PKEY;


#define EVP_PKEY_MO_SIGN 0x0001

#define EVP_PKEY_MO_VERIFY 0x0002

#define EVP_PKEY_MO_ENCRYPT 0x0004

#define EVP_PKEY_MO_DECRYPT 0x0008


用于封装公开密钥算法中,各种公开密钥和私人密钥的结构。


OpenSSL 中硬件引擎所要完成的工作本质上就是实现RSA_METHOD结构中的函数指针。


/* 软件算法RSA算法如何生成公私密钥对 */

RSA *RSA_generate_key(int bits, unsigned long e_value,

void (*callback)(int,int,void *), void *cb_arg)

{

/*

输入

bits, 密钥长度

e_value, e值,也就是公开密钥

callback 回调函数

cb_args 回调函数的参数

*/

RSA *rsa=NULL;

BIGNUM *r0=NULL,*r1=NULL,*r2=NULL,*r3=NULL,*tmp;

int bitsp,bitsq,ok= -1,n=0,i;

BN_CTX *ctx=NULL,*ctx2=NULL;


ctx=BN_CTX_new();

if (ctx == NULL) goto err;

ctx2=BN_CTX_new();

if (ctx2 == NULL) goto err;

BN_CTX_start(ctx);

r0 = BN_CTX_get(ctx);

r1 = BN_CTX_get(ctx);

r2 = BN_CTX_get(ctx);

r3 = BN_CTX_get(ctx);

if (r3 == NULL) goto err;


/* 大质数p,q的位数 bitsp,bitsq

如果RSA采用了bits位加密强度

则:大质数p的长度为,bitsp = (bits+1)/2

大质数q的长度为,bitsq =bits-bitsp,

*/

bitsp=(bits+1)/2;

bitsq=bits-bitsp;

rsa=RSA_new();

if (rsa == NULL) goto err;


/* set e */

rsa->e=BN_new();

if (rsa->e == NULL) goto err;


#if 1

/* The problem is when building with 8, 16, or 32 BN_ULONG,

* unsigned long can be larger */

for (i=0; i
{

if (e_value & (1UL<
BN_set_bit(rsa->e,i);

}

#else

if (!BN_set_word(rsa->e,e_value)) goto err;

#endif


/* generate p and q */

for (;;)

{

rsa->p=BN_generate_prime(NULL,bitsp,0,NULL,NULL,callback,cb_arg);

if (rsa->p == NULL) goto err;

if (!BN_sub(r2,rsa->p,BN_value_one())) goto err;

if (!BN_gcd(r1,r2,rsa->e,ctx)) goto err;

if (BN_is_one(r1)) break;

if (callback != NULL) callback(2,n++,cb_arg);

BN_free(rsa->p);

}

if (callback != NULL) callback(3,0,cb_arg);

for (;;)

{

rsa->q=BN_generate_prime(NULL,bitsq,0,NULL,NULL,callback,cb_arg);

if (rsa->q == NULL) goto err;

if (!BN_sub(r2,rsa->q,BN_value_one())) goto err;

if (!BN_gcd(r1,r2,rsa->e,ctx)) goto err;

if (BN_is_one(r1) && (BN_cmp(rsa->p,rsa->q) != 0))

break;

if (callback != NULL) callback(2,n++,cb_arg);

BN_free(rsa->q);

}

if (callback != NULL) callback(3,1,cb_arg);

if (BN_cmp(rsa->p,rsa->q) <>
{

tmp=rsa->p;

rsa->p=rsa->q;

rsa->q=tmp;

}


/* calculate n */

rsa->n=BN_new();

if (rsa->n == NULL) goto err;

if (!BN_mul(rsa->n,rsa->p,rsa->q,ctx)) goto err;


/* calculate d */

if (!BN_sub(r1,rsa->p,BN_value_one())) goto err; /* p-1 */

if (!BN_sub(r2,rsa->q,BN_value_one())) goto err; /* q-1 */

if (!BN_mul(r0,r1,r2,ctx)) goto err; /* (p-1)(q-1) */


/* should not be needed, since gcd(p-1,e) == 1 and gcd(q-1,e) == 1 */

/* for (;;)

{

if (!BN_gcd(r3,r0,rsa->e,ctx)) goto err;

if (BN_is_one(r3)) break;


if (1)

{

if (!BN_add_word(rsa->e,2L)) goto err;

continue;

}

RSAerr(RSA_F_RSA_GENERATE_KEY,RSA_R_BAD_E_VALUE);

goto err;

}

*/

/* 生成d,私人密钥 */

rsa->d=BN_mod_inverse(NULL,rsa->e,r0,ctx2); /* d */

if (rsa->d == NULL) goto err;


/* 生成中间计算结果 */

/* calculate d mod (p-1) */

rsa->dmp1=BN_new();

if (rsa->dmp1 == NULL) goto err;

if (!BN_mod(rsa->dmp1,rsa->d,r1,ctx)) goto err;


/* calculate d mod (q-1) */

rsa->dmq1=BN_new();

if (rsa->dmq1 == NULL) goto err;

if (!BN_mod(rsa->dmq1,rsa->d,r2,ctx)) goto err;


/* calculate inverse of q mod p */

rsa->iqmp=BN_mod_inverse(NULL,rsa->q,rsa->p,ctx2);

if (rsa->iqmp == NULL) goto err;


ok=1;

err:

if (ok == -1)

{

RSAerr(RSA_F_RSA_GENERATE_KEY,ERR_LIB_BN);

ok=0;

}

BN_CTX_end(ctx);

BN_CTX_free(ctx);

BN_CTX_free(ctx2);


if (!ok)

{

if (rsa != NULL) RSA_free(rsa);

return(NULL);

}

else

return(rsa);

}



/* This is a structure for storing implementations of various crypto

* algorithms and functions. */

typedef struct engine_st

{

const char *id;

const char *name;

RSA_METHOD *rsa_meth;

DSA_METHOD *dsa_meth;

DH_METHOD *dh_meth;

RAND_METHOD *rand_meth;

BN_MOD_EXP bn_mod_exp;

BN_MOD_EXP_CRT bn_mod_exp_crt;

int (*init)(void);

int (*finish)(void);

int (*ctrl)(int cmd, long i, void *p, void (*f)());

EVP_PKEY *(*load_privkey)(const char *key_id, const char *passphrase);

EVP_PKEY *(*load_pubkey)(const char *key_id, const char *passphrase);

int flags;

/* reference count on the structure itself */

int struct_ref;

/* reference count on usability of the engine type. NB: This

* controls the loading and initialisation of any functionlity

* required by this engine, whereas the previous count is

* simply to cope with (de)allocation of this structure. Hence,

* running_ref <= struct_ref at all times. */

int funct_ref;

/* Used to maintain the linked-list of engines. */

struct engine_st *prev;

struct engine_st *next;

} ENGINE;


/* 描述硬件引擎的数据结构,需要实现的字段包括,id,name,init,finish,如果引擎提供rsa算法,则必须实现rsa_method */


/* 提供返回ENGINE的接口,并在crypt/engine/engine_int.h申明。*/

ENGINE *ENGINE_HardCard();

如果想把HardCard加到OPENSSL的缺省引擎列表中,需要在在crypto/engine/engine_list.c 的engine_internal_check()中添加实现代码。

static int engine_internal_check(void)

{

if(engine_list_flag)

return 1;

/* This is our first time up, we need to populate the list

* with our statically compiled-in engines. */

if(!engine_list_add(ENGINE_openssl()))

return 0;

#ifndef NO_HW

#ifndef NO_HW_CSWIFT

if(!engine_list_add(ENGINE_cswift()))

return 0;

#endif /* !NO_HW_CSWIFT */

#ifndef NO_HW_NCIPHER

if(!engine_list_add(ENGINE_ncipher()))

return 0;

#endif /* !NO_HW_NCIPHER */

#ifndef NO_HW_ATALLA

if(!engine_list_add(ENGINE_atalla()))

return 0;

#endif /* !NO_HW_ATALLA */

#endif /* !NO_HW */

engine_list_flag = 1;

return 1;

}

否则,你在使用引擎之前必须手动完成engine_list_add这个过程。



在init函数中实现引擎的初始化,在ENGINE_set_default(ENGINE *e,long flag)还函数中调用,finish释放Engine所占用的资源。


如果要采用引擎实现的算法,可以通过下面的程序来实现。

ENGINE *e=NULL;

if((e = ENGINE_by_id("pkicard")) == NULL)

{

printf("invalid engine pkicard");

}

printf("engine pkicard set\n");

if(!ENGINE_set_default(e, ENGINE_METHOD_RSA))

{/* 引擎提供的加密算法被制定为缺省的RSA算法 */

printf("PKICARD RSA\n");

goto end;

}


在实现公钥和私钥加解密的时候会涉及到padding的问题,padding的处理方式必须与标准RSA算法相同。


RSA_pub_dec()采用RSA_padding_check_PKCS1_type_1去调明文数据的padding.

RSA_pub_enc()采用RSA_padding_add_PKCS1_type_2去调明文数据的padding.

RSA_priv_dec()采用RSA_padding_check_PKCS1_type_2去调明文数据的padding.

RSA_priv_enc()采用RSA_padding_add_PKCS1_type_1去调明文数据的padding.

以上是PKCS1的padding处理方式,更为详细的资料请来查看源代码

crypt/rsa/rsa_pk1.c


在crypt/rsa/rsa.h还能找到其他的padding方式。具体的应用请查看相关代码。


在接口实现的过程中,各种加密卡的字节序和位序可能各不相同,需要作提要的调整以满足OpenSSL的接口。


另外:engine中提供了不少加密卡的引擎代码,可以参考实现之。



































 

没有评论: