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中提供了不少加密卡的引擎代码,可以参考实现之。
没有评论:
发表评论