最近在新作一个东西,需要用到RSA算法,不想用OpenSSL的算法库了,决定使用稍微轻量级一些的PolarSSL的算法库。现在的需求是需要使用PolarSSL的RSA算法产生RSA密钥对,需要提取出公钥数据和私钥数据,并且可以把公钥和私钥提供给其他支持RSA的产品使用,比如USBKEY,加密狗、或者其他的RSA算法库。也需要把第三方产生的RSA密钥对可以拿来进行公钥运算或者私钥运算。按理说,只要可以提取出公钥模数N,和私钥因子D(或者DP,DQ)就可以了,PolarSSL的算法库应该没什么问题,产生的RSA对象里面这些因子都是现成的,研究一下数据对象的读写就应该能搞定了,但是同事说公钥填充N和E=0x10001没问题,私钥只写D或者P和Q就是没搞定,好久没有看这个了,决定拿来研究一下。
RSA的加密算法不多说了,网上可以找到各种专业的介绍。先看PolarSSL的RSA算法库中rsa_context的定义吧。
/**
* \brief RSA context structure
*/
typedef struct
{
int ver; /*!< always 0 */
size_t len; /*!< size(N) in chars */
mpi N; /*!< public modulus */
mpi E; /*!< public exponent */
mpi D; /*!< private exponent */
mpi P; /*!< 1st prime factor */
mpi Q; /*!< 2nd prime factor */
mpi DP; /*!< D % (P - 1) */
mpi DQ; /*!< D % (Q - 1) */
mpi QP; /*!< 1 / (Q % P) */
mpi RN; /*!< cached R^2 mod N */
mpi RP; /*!< cached R^2 mod P */
mpi RQ; /*!< cached R^2 mod Q */
int padding; /*!< RSA_PKCS_V15 for 1.5 padding and
RSA_PKCS_v21 for OAEP/PSS */
int hash_id; /*!< Hash identifier of md_type_t as
specified in the md.h header file
for the EME-OAEP and EMSA-PSS
encoding */
}
rsa_context;
可以看到rsa_context中有公钥模数N和公钥指数E,私钥D和P,Q,DP,DQ,QP等,如果只用N、D、E或者P和Q的话,是应该可以进行私钥运算的,但是PolarSSL的算法有个rsa_check_pubkey/rsa_check_prikey函数来检查公私钥格式是否正确,公钥给N和E赋值可以检查通过,私钥单独给N、D、E或者P、Q赋值,检查私钥格式是失败的,查看一下rsa_check_prikey的内容看下格式错误在什么地方。
/*
* Check a private RSA key
*/
int rsa_check_privkey( const rsa_context *ctx )
{
int ret;
mpi PQ, DE, P1, Q1, H, I, G, G2, L1, L2;
if( ( ret = rsa_check_pubkey( ctx ) ) != 0 )
return( ret );
if( !ctx->P.p || !ctx->Q.p || !ctx->D.p )
return( POLARSSL_ERR_RSA_KEY_CHECK_FAILED );
mpi_init( &PQ ); mpi_init( &DE ); mpi_init( &P1 ); mpi_init( &Q1 );
mpi_init( &H ); mpi_init( &I ); mpi_init( &G ); mpi_init( &G2 );
mpi_init( &L1 ); mpi_init( &L2 );
MPI_CHK( mpi_mul_mpi( &PQ, &ctx->P, &ctx->Q ) );
MPI_CHK( mpi_mul_mpi( &DE, &ctx->D, &ctx->E ) );
MPI_CHK( mpi_sub_int( &P1, &ctx->P, 1 ) );
MPI_CHK( mpi_sub_int( &Q1, &ctx->Q, 1 ) );
MPI_CHK( mpi_mul_mpi( &H, &P1, &Q1 ) );
MPI_CHK( mpi_gcd( &G, &ctx->E, &H ) );
MPI_CHK( mpi_gcd( &G2, &P1, &Q1 ) );
MPI_CHK( mpi_div_mpi( &L1, &L2, &H, &G2 ) );
MPI_CHK( mpi_mod_mpi( &I, &DE, &L1 ) );
/*
* Check for a valid PKCS1v2 private key
*/
if( mpi_cmp_mpi( &PQ, &ctx->N ) != 0 ||
mpi_cmp_int( &L2, 0 ) != 0 ||
mpi_cmp_int( &I, 1 ) != 0 ||
mpi_cmp_int( &G, 1 ) != 0 )
{
ret = POLARSSL_ERR_RSA_KEY_CHECK_FAILED;
}
cleanup:
mpi_free( &PQ ); mpi_free( &DE ); mpi_free( &P1 ); mpi_free( &Q1 );
mpi_free( &H ); mpi_free( &I ); mpi_free( &G ); mpi_free( &G2 );
mpi_free( &L1 ); mpi_free( &L2 );
if( ret == POLARSSL_ERR_RSA_KEY_CHECK_FAILED )
return( ret );
if( ret != 0 )
return( POLARSSL_ERR_RSA_KEY_CHECK_FAILED + ret );
return( 0 );
}
检查函数是要查看N、E是否有效,还要用D、P、Q进行验证,所以需要自己使用私钥P和Q计算出剩余的元素,然后填充的rsa_context中去。
首先定义了自己的中国余数定理的RSA私钥结构和RSA公钥结构,如下:
/*
/brief RSA CRT format privatekey
P,Q
bit = 1024/2048
*/
typedef struct
{
int bit;
unsigned char P[128];
unsigned char Q[128];
}rsa_crt_prikey;
/*
/brief RSA public key
bit = 1024/2048
N
E=0x10001;//default
*/
typedef struct
{
int bit;
unsigned N[256];
}rsa_pubkey;
公钥指数E固定使用0x10001,就不在结构里单独写了。然后封装了一个函数从rsa_context中提取出公钥和私钥数据,两个函数是使用公钥或者私钥数据填充rsa_context结构。
/*
把CRT格式的RSA私钥填充到rsa_context中去
*/
int rsa_fill_crt_privatekey(rsa_context *ctx, rsa_crt_prikey *prikey)
{
int ret;
mpi P1,Q1,H;
mpi_init(&P1); mpi_init(&Q1); mpi_init(&H);
//
rsa_init(ctx,RSA_PKCS_V15,RSA_PKCS_V21);
//
mpi_read_binary(&ctx->P, prikey->P, prikey->bit/16);//从CRT格式的私钥中读取P
mpi_read_binary(&ctx->Q, prikey->Q, prikey->bit/16);//从CRT格式的私钥中读取Q
mpi_read_binary(&ctx->E, (unsigned char*)"\x00\x01\x00\x01", 4);//填充固定的指数E=0x10001
//
MPI_CHK(mpi_mul_mpi( &ctx->N, &ctx->P, &ctx->Q ));//计算N,N=P*Q
//
MPI_CHK(mpi_sub_int( &P1, &ctx->P, 1 ));
MPI_CHK(mpi_sub_int( &Q1, &ctx->Q, 1 ));
MPI_CHK(mpi_mul_mpi( &H, &P1, &Q1 ));
MPI_CHK(mpi_inv_mod( &ctx->D , &ctx->E, &H ));//计算出D,D=(P-1)*(Q-1) MOD E
MPI_CHK(mpi_mod_mpi( &ctx->DP, &ctx->D, &P1 ));//计算DP
MPI_CHK(mpi_mod_mpi( &ctx->DQ, &ctx->D, &Q1 ));//计算DP</span>
MPI_CHK(mpi_inv_mod( &ctx->QP, &ctx->Q, &ctx->P ));//计算QP
//
ctx->len = ( mpi_msb( &ctx->N ) + 7 ) >> 3;//填充长度
//
ret = rsa_check_privkey(ctx);
cleanup:
mpi_free(&P1); mpi_free(&Q1); mpi_free(&H);
return ret;
}
/*
把公钥模N填充到rsa_context中去
*/
int rsa_fill_publickey(rsa_context *ctx, rsa_pubkey *pubkey)
{
int ret;
//
rsa_init(ctx,RSA_PKCS_V15,RSA_PKCS_V21);
//
mpi_read_binary(&ctx->N, pubkey->N, pubkey->bit/8);
mpi_read_binary(&ctx->E, (unsigned char*)"\x00\x01\x00\x01", 4);
//
ctx->len = ( mpi_msb( &ctx->N ) + 7 ) >> 3;
//
ret = rsa_check_pubkey(ctx);
//
return ret;
}
/*
* \brief get crt private key and public key from rsa_context
*/
int rsa_getkeypairs(rsa_context* ctx, rsa_crt_prikey *prikey, rsa_pubkey *pubkey)
{
int ret;
//
if (NULL != prikey)
{
prikey->bit = ctx->len * 8;
ret = mpi_write_binary(&ctx->P, prikey->P, ctx->len/2);
if (0 != ret)
{
return ret;
}
ret = mpi_write_binary(&ctx->Q, prikey->Q, ctx->len/2);
if (0 != ret)
{
return ret;
}
}
//
if (NULL != pubkey)
{
pubkey->bit = ctx->len * 8;
ret = mpi_write_binary(&ctx->N,pubkey->N, ctx->len);
if (0 != ret)
{
return ret;
}
}
//
return 0;
}
//
这样使用这个rsa算法产生密钥对后可以使用上面的rsa_getkeypairs提取出公钥和私钥数据给其他产品或者算法运算,也可以把其他产品或者算法产生的公钥或者私钥数据传给PolarSSL的RSA算法进行运算了。