欢迎访问:常州市武进区嘉泽中心小学网站 !今天是:
栏目列表
您现在的位置是:首页>>教师>>计算机技术>>程序设计>>一般性编程问题>>加解密>>文章内容
DES算法的介绍和实现(下)
发布时间:2008-11-20   点击:   来源:本站原创   录入者:佚名
13951次 ]

DES算法的介绍和实现(下)
作者:西安 吴真

三.文件加密解密工具

在《DES算法的介绍和实现(上)》一文中,介绍了DES算法的原理,在本文中将给出一个文本文件加密工具的具体实现代码。

3.1 实现的介绍


利用算法核心代码封装的接口函数笔者编写了一个针对文本文件的加密解密工具.下文叙述了在实践时的一些心得体会,希望能对读者有所裨益.
笔者选择把密文以16进制的形式写入文件的方法.当然也可以直接写入文件.
例:
密文为:12345678
在内存中显示为:
31 32 33 34 35 36 37 38
那么就把以3132333435363738的形式写入文件.
为了解密的方便,密文中的每个字节用两个字节表示,也即在内存中显示为0x9A的内容,就以9A的形式写入文件中.当内存中显示的内容为0x0?(?代表0~F)形式时,需要以0?的形式写入文件.
这样可以避开前面提及的问题,只是在解密时先按照两两组合的原则,顺序把从文件中读取的数据转换成待解的密文.
例:
读出的数据是:
3132333435363738
那么复原的过程:
31->1
32->2
33->3
….
38->8
最终得真正的密文12345678,这样就可以调用DES算法解密函数从密文得到明文.
DES算法是对固定大小(64位)的数据块进行加密解密操作的,对于那些不够64位的数据块需要采用填充机制补位到64位长,为了方便使用,数据位的填充是对用户而言是透明的,利用该工具进行加密解密操作时,用户只需输入操作的类型、读取数据的文件名、写入操作结果的文件名、密钥等信息.
操作思路:
#define READFILESIZE 512
步骤:
1.从文件中读取READFILESIZE个字节的数据
2.,如果从文件中读出的数据少于READFILESIZE个,以0补足,然后根据用户指定的类型对这READFILESIZE个字节的数据进行操作.
3.判断文件是否结束,没有则执行步骤1
4.把加密后的文件实际长度添加到密文的末尾
5.结束
采用一次只从文件读取READFILESIZE个字节是在为了防止由于需要加密或解密的文件太大导致内存不够的情况出现.

3.2 注意事项

DES算法的加密密钥是根据用户输入的密码生成的,该算法把64位密码中的第8位、第16位、第24位、第32位、第40位、第48位、第56位、第64位作为奇偶校验位,在计算密钥时要忽略这8位.如果输入的密码只是在这8位上有区别的话,那么操作后的结果将是一样的.
例:
输入的密码为wuzhenll,密钥的16进制表示为77 75 7A 68 65 6E 6C 6C
任意改变这64位数据的奇偶校验位,可以得到16个不同的密码,
把8个奇偶检验位全取反后:
w->v
u->t
z->{
h->i
e->d
n->o
l->m
形成新密码:vt{idomm
表面上新密码和原密码迥然不同,但是由于他们仅在奇偶校验位上有区别,所以用这两个密码进行加密解密操作得到的结果是一样的.
笔者建议使用安全系数较高的多密钥加密解密方案.
此外用户输入的密码的长度不受限制,当输入的密码长度为0时,使用缺省64位密码;当输入的密码长度大于8字节时,输入密码的前8个字节为有效密码.
该工具提供6种不同的操作类型:

1:一层加密;
2:一层解密;

3:N层单密钥加密;
4:N层单密钥解密;

5:N层多密钥加密;
6:N层多密钥解密;

这六种操作是对称使用的,例如:加密明文时选择一层加密,解密时对密文使用一层解密.

3.3 实现源码

笔者在此提供自编的DES算法的实现源码,读者可以直接使用接口函数实现加密解密操作,有兴趣自己动手实现的读者也可以把她作为一份参考..如发现问题请E-Mail给我.ok, Let’s go…

/*略去信息头,#include文件和函数申明*/


        ULONG32 g_outkey[16][2] = { 0};/*输出的key*/

        ULONG32 g_bufkey[2] = { 0};/*形成起始密钥*/
/*实现时多采用查表方式,下面是定义的供查找用的表*/

static ULONG8 wz_lefttable[16] = {1,1,2,2,2,2,2,2,1,2,2,2,2,2,2,1};

static ULONG32 wz_leftandtab[3] = {0x0 , 0x80000000 , 0xc0000000 } ;

static ULONG8 wz_keyleft[28] = 

{

       57,49,41,33,25,17,9,1,58,50,42,34,26,18,

       10,2,59,51,43,35,27,19,11,3,60,52,44,36

};

 

static ULONG8 wz_keyright[28] = {

       63,55,47,39,31,23,15,7,62,54,46,38,30,22,

       14,6,61,53,45,37,29,21,13,5,28,20,12,4

};

 

static ULONG8 wz_keychoose[48] ={

       14,17,11,24,1,5,3,28,15,6,21,10,

       23,19,12,4,26,8,16,7,27,20,13,2,

       41,52,31,37,47,55,30,40,51,45,33,48,

       44,49,39,56,34,53,46,42,50,36,29,32

};

 

static ULONG8 wz_pc4[64] = { /*最后一次调整*/

       40,8,48,16,56,24,64,32, 39,7,47,15,55,23,63,31,

       38,6,46,14,54,22,62,30, 37,5,45,13,53,21,61,29,

       36,4,44,12,52,20,60,28, 35,3,43,11,51,19,59,27,

       34,2,42,10,50,18,58,26, 33,1,41,9,49,17,57,25

};

static ULONG8  wz_pc1[64] = {/*第一次转换时用*/

                 58,50,42,34,26,18,10,2,60,52,44,36,28,20,12,4,

                 62,54,46,38,30,22,14,6,64,56,48,40,32,24,16,8,

                 57,49,41,33,25,17,9,1,59,51,43,35,27,19,11,3,

                 61,53,45,37,29,21,13,5,63,55,47,39,31,23,15,7 

} ;

 

static ULONG8 wz_pc3[32] = {

                        16,7,20,21, 29,12,28,17, 1,15,23,26,

                        5,18,31,10, 2,8,24,14, 32,27,3,9,

                        19,13,30,6, 22,11,4,25

} ;



static ULONG32  wz_pc2[64] = { 

        0x80000000L,0x40000000L,0x20000000L,0x10000000L, 0x8000000L, 

	0x4000000L, 0x2000000L, 0x1000000L, 0x800000L, 0x400000L,

        0x200000L, 0x100000L,  0x80000L, 0x40000L, 0x20000L,0x10000L, 

	0x8000L, 0x4000L, 0x2000L, 0x1000L, 0x800L, 0x400L, 0x200L,

	0x100L, 0x80L,0x40L,0x20L, 0x10L, 0x8L, 0x4L, 0x2L, 0x1L,

	0x80000000L,0x40000000L,0x20000000L,0x10000000L, 0x8000000L,

	0x4000000L, 0x2000000L, 0x1000000L, 0x800000L, 0x400000L,

        0x200000L, 0x100000L,  0x80000L, 0x40000L, 0x20000L, 0x10000L, 

	0x8000L, 0x4000L, 0x2000L, 0x1000L, 0x800L, 0x400L, 0x200L, 

	0x100L, 0x80L, 0x40L,0x20L, 0x10L, 0x8L,  0x4L, 0x2L, 0x1L,     

}; 

   

static ULONG8 exptab3[48] = {

                              32,1,2,3,4,5,4,5,6,7,8,9,8,9,10,11,12,13,

                              12,13,14,15,16,17,16,17,18,19,20,21,

                              20,21,22,23,24,25,24,25,26,27,28,29,

                              28,29,30,31,32,1 

};

                       

static ULONG8 SP[8][64] = 

{

       {

              0xe,0x0,0x4,0xf,0xd,0x7,0x1,0x4,0x2,0xe,0xf,0x2,0xb,

              0xd,0x8,0x1,0x3,0xa,0xa,0x6,0x6,0xc,0xc,0xb,0x5,0x9,

              0x9,0x5,0x0,0x3,0x7,0x8,0x4,0xf,0x1,0xc,0xe,0x8,0x8,

              0x2,0xd,0x4,0x6,0x9,0x2,0x1,0xb,0x7,0xf,0x5,0xc,0xb,

              0x9,0x3,0x7,0xe,0x3,0xa,0xa,0x0,0x5,0x6,0x0,0xd  

       },



       { 

              0xf,0x3,0x1,0xd,0x8,0x4,0xe,0x7,0x6,0xf,0xb,0x2,0x3,

              0x8,0x4,0xf,0x9,0xc,0x7,0x0,0x2,0x1,0xd,0xa,0xc,0x6,

              0x0,0x9,0x5,0xb,0xa,0x5,0x0,0xd,0xe,0x8,0x7,0xa,0xb,

              0x1,0xa,0x3,0x4,0xf,0xd,0x4,0x1,0x2,0x5,0xb,0x8,0x6,

              0xc,0x7,0x6,0xc,0x9,0x0,0x3,0x5,0x2,0xe,0xf,0x9

       },

       { 

              0xa,0xd,0x0,0x7,0x9,0x0,0xe,0x9,0x6,0x3,0x3,0x4,0xf,

              0x6,0x5,0xa,0x1,0x2,0xd,0x8,0xc,0x5,0x7,0xe,0xb,0xc,

              0x4,0xb,0x2,0xf,0x8,0x1,0xd,0x1,0x6,0xa,0x4,0xd,0x9,

              0x0,0x8,0x6,0xf,0x9,0x3,0x8,0x0,0x7,0xb,0x4,0x1,0xf,

              0x2,0xe,0xc,0x3,0x5,0xb,0xa,0x5,0xe,0x2,0x7,0xc                                          

       },

       { 

              0x7,0xd,0xd,0x8,0xe,0xb,0x3,0x5,0x0,0x6,0x6,0xf,0x9,

              0x0,0xa,0x3,0x1,0x4,0x2,0x7,0x8,0x2,0x5,0xc,0xb,0x1,

              0xc,0xa,0x4,0xe,0xf,0x9,0xa,0x3,0x6,0xf,0x9,0x0,0x0,

              0x6,0xc,0xa,0xb,0xa,0x7,0xd,0xd,0x8,0xf,0x9,0x1,0x4,

              0x3,0x5,0xe,0xb,0x5,0xc,0x2,0x7,0x8,0x2,0x4,0xe                         

       },

       { 

              0x2,0xe,0xc,0xb,0x4,0x2,0x1,0xc,0x7,0x4,0xa,0x7,0xb,

              0xd,0x6,0x1,0x8,0x5,0x5,0x0,0x3,0xf,0xf,0xa,0xd,0x3,

              0x0,0x9,0xe,0x8,0x9,0x6,0x4,0xb,0x2,0x8,0x1,0xc,0xb,

              0x7,0xa,0x1,0xd,0xe,0x7,0x2,0x8,0xd,0xf,0x6,0x9,0xf,

              0xc,0x0,0x5,0x9,0x6,0xa,0x3,0x4,0x0,0x5,0xe,0x3

       },

       { 

              0xc,0xa,0x1,0xf,0xa,0x4,0xf,0x2,0x9,0x7,0x2,0xc,0x6,

              0x9,0x8,0x5,0x0,0x6,0xd,0x1,0x3,0xd,0x4,0xe,0xe,0x0,

              0x7,0xb,0x5,0x3,0xb,0x8,0x9,0x4,0xe,0x3,0xf,0x2,0x5,

              0xc,0x2,0x9,0x8,0x5,0xc,0xf,0x3,0xa,0x7,0xb,0x0,0xe,

              0x4,0x1,0xa,0x7,0x1,0x6,0xd,0x0,0xb,0x8,0x6,0xd

       },

       { 

              0x4,0xd,0xb,0x0,0x2,0xb,0xe,0x7,0xf,0x4,0x0,0x9,0x8,

              0x1,0xd,0xa,0x3,0xe,0xc,0x3,0x9,0x5,0x7,0xc,0x5,0x2,

              0xa,0xf,0x6,0x8,0x1,0x6,0x1,0x6,0x4,0xb,0xb,0xd,0xd,

              0x8,0xc,0x1,0x3,0x4,0x7,0xa,0xe,0x7,0xa,0x9,0xf,0x5,

              0x6,0x0,0x8,0xf,0x0,0xe,0x5,0x2,0x9,0x3,0x2,0xc

       },

       { 

              0xd,0x1,0x2,0xf,0x8,0xd,0x4,0x8,0x6,0xa,0xf,0x3,0xb,

              0x7,0x1,0x4,0xa,0xc,0x9,0x5,0x3,0x6,0xe,0xb,0x5,0x0,

              0x0,0xe,0xc,0x9,0x7,0x2,0x7,0x2,0xb,0x1,0x4,0xe,0x1,

              0x7,0x9,0x4,0xc,0xa,0xe,0x8,0x2,0xd,0x0,0xf,0x6,0xc,

              0xa,0x9,0xd,0x0,0xf,0x3,0x3,0x5,0x5,0x6,0x8,0xb

       } 

};
/*函数实现*/

INT32 DdesN(ULONG8 *data, ULONG8 **key, ULONG32 n_key,ULONG32 readlen)

{

       INT32 i ;

       for ( i = n_key ; i > 0 ; i--)

       {

              Ddes(data,key[i-1],readlen);

       }

       return SUCCESS;

}



INT32 desN(ULONG8 *data, ULONG8 **key, ULONG32 n_key,ULONG32 readlen)

{

       ULONG32 i = 0;

       for ( i = 0 ; i < n_key ; i++)

       {

              des(data,key[i],readlen);

       }

       return SUCCESS;

}



INT32 des3(ULONG8 *data, ULONG8 *key,ULONG32 n ,ULONG32 readlen)

{

       ULONG32 i = 0 , j = 0 ;

       makefirstkey((ULONG32*)key) ;  /*产生密钥*/

       for ( i = 0 ; i < n ; i++)

       {

              for ( j = 0 ; j < readlen ; j += 8)

              {

                     handle_data( (ULONG32*)&data[j], DESENCRY );

              }

       }

       return SUCCESS;

}



INT32 Ddes3(ULONG8 *data,ULONG8 *key,ULONG32 n ,ULONG32 readlen)

{

       ULONG32 i = 0 , j = 0 ;

       makefirstkey((ULONG32*)key) ;  /*产生密钥*/

       for ( i = 0 ; i < n ; i++)

       {

              for ( j = 0 ; j < readlen ; j += 8)

              {

                     handle_data( (ULONG32*)&data[j], DESDECRY );

              }

       }

       return SUCCESS;

}



INT32 des(ULONG8 *data, ULONG8 *key,INT32 readlen)

{

       INT32 i = 0;   

       makefirstkey((ULONG32*)key) ;  /*产生密钥*/

       for ( i = 0 ; i < readlen ; i += 8)

       {

              handle_data( (ULONG32*)&data[i], DESENCRY );

       }

       return SUCCESS;

}



INT32 Ddes(ULONG8 *data,ULONG8 *key,INT32 readlen)

{

       INT32 i = 0;

       makefirstkey((ULONG32*)key) ;  

       for ( i = 0 ; i < readlen ; i += 8)

       {

              handle_data( (ULONG32*)&data[i] ,DESDECRY);

       }

       return SUCCESS;

}



INT32 handle_data(ULONG32 *left , ULONG8 choice)

{

       INT32  number = 0 ,j = 0;   

       ULONG32 *right = &left[1] ;

       ULONG32 tmp = 0;       

       ULONG32 tmpbuf[2] = { 0 };             



       /*第一次调整wz_pc1[64]*/

       for ( j = 0 ; j < 64 ; j++)

       {

              if (j < 32 ) 

              {

                     if ( wz_pc1[j] > 32)/*属于right*/

                     {

                            if ( *right&wz_pc2[wz_pc1[j]-1] )

                            {

                                   tmpbuf[0] |= wz_pc2[j] ;

                            }

                     }

                     else

                     {

                            if ( *left&wz_pc2[wz_pc1[j]-1] )

                            {

                                   tmpbuf[0] |= wz_pc2[j] ;

                            }

                     }

              }

              else

              {

                     if ( wz_pc1[j] > 32)/*属于right*/

                     {

                            if ( *right&wz_pc2[wz_pc1[j]-1] )

                            {

                                   tmpbuf[1] |= wz_pc2[j] ;

                            }

                     }

                     else

                     {

                            if ( *left&wz_pc2[wz_pc1[j]-1] )

                            {

                                   tmpbuf[1] |= wz_pc2[j] ;

                            }

                     }

              }

       }

       *left  = tmpbuf[0] ;

       *right = tmpbuf[1];

       tmpbuf[0] = 0 ;

       tmpbuf[1] = 0 ;

    switch( choice )

       {

       case DESENCRY:

              for ( number = 0 ; number < 16 ; number++)

              {            

                     makedata( left , right , (ULONG32)number) ;

              }

              break;

       case DESDECRY:

                     for ( number = 15 ; number >= 0 ; number--)

                     {            

                            makedata( left , right ,(ULONG32)number) ;

                     }

              break;

       default:

              break;

       }



       /*最后一轮操作不交换左右值*/



       tmp = *left ;

       *left = *right ;

       *right = tmp ;        



       /*最后一次调整wz_pc4[64]*/



       for ( j = 0 ; j < 64 ; j++)

       {

              if (j < 32 ) 

              {

                     if ( wz_pc4[j] > 32)/*属于right*/

                     {

                            if ( *right&wz_pc2[wz_pc4[j]-1] )

                            {

                                   tmpbuf[0] |= wz_pc2[j] ;

                            }

                     }

                     else

                     {

                            if ( *left&wz_pc2[wz_pc4[j]-1] )

                            {

                                   tmpbuf[0] |= wz_pc2[j] ;

                            }

                     }

              }

              else

              {

                     if ( wz_pc4[j] > 32)/*属于right*/

                     {

                            if ( *right&wz_pc2[wz_pc4[j]-1] )

                            {

                                   tmpbuf[1] |= wz_pc2[j] ;

                            }

                     }

                     else

                     {

                            if ( *left&wz_pc2[wz_pc4[j]-1] )

                            {

                                   tmpbuf[1] |= wz_pc2[j] ;

                            }

                     }

              }

       }

  

       *left =  tmpbuf[0] ;

       *right = tmpbuf[1];



       return SUCCESS;

}





INT32 makedata(ULONG32  *left ,ULONG32  *right ,ULONG32 number) 

{

              INT32 j; 

              ULONG32 oldright = *right;         

              ULONG8 rexpbuf[8] = { 0} ;

              ULONG32 datatmp = 0;        

              ULONG32 exp[2] = { 0} ;                         

       //由32扩充至48位



       for ( j = 0 ; j < 48 ; j++)

       {

              /*两个32位,每个存放24位*/

              if ( j < 24 )

              {

                     if ( *right&wz_pc2[exptab3[j]-1] )

                     {

                            exp[0] |= wz_pc2[j] ;

                     }            

              }            

              else

              {

                     if ( *right&wz_pc2[exptab3[j]-1] )

                     {

                            exp[1] |= wz_pc2[j-24] ;

                     }

              }

       }



       for ( j = 0 ; j < 2 ; j++)

       {            

              exp[j] ^= g_outkey[number][j] ;

       }    



       /*由48->32*/      



       exp[1] >>= 8 ;

       rexpbuf[7] = (ULONG8) (exp[1]&0x0000003fL) ;

       exp[1] >>= 6 ;

       rexpbuf[6] = (ULONG8) (exp[1]&0x0000003fL) ;

       exp[1] >>= 6 ;

       rexpbuf[5] = (ULONG8) (exp[1]&0x0000003fL) ;

       exp[1] >>= 6 ;

       rexpbuf[4] = (ULONG8) (exp[1]&0x0000003fL) ;

       exp[0]  >>=  8 ;

       rexpbuf[3] = (ULONG8) (exp[0]&0x0000003fL) ;     

       exp[0] >>= 6 ;

       rexpbuf[2] = (ULONG8) (exp[0]&0x0000003fL) ;

       exp[0] >>= 6 ;

       rexpbuf[1] = (ULONG8) (exp[0]&0x0000003fL) ;

       exp[0] >>= 6 ;

       rexpbuf[0] = (ULONG8) (exp[0]&0x0000003fL) ;     

       exp[0] = 0 ;

       exp[1] = 0 ;

 

       /*由48-> 32*/



       *right = 0 ;

       for ( j = 0 ; j < 7 ; j++)

       {

              *right |= SP[j][rexpbuf[j]] ;

              *right <<= 4 ;

       }

       *right |= SP[j][rexpbuf[j]] ;



       /*又要换位了*/



       datatmp = 0;

       for ( j = 0 ; j < 32 ; j++)

       {

              if ( *right&wz_pc2[wz_pc3[j]-1] )

              {

                     datatmp |= wz_pc2[j] ;

              }

       }

       *right = datatmp ;



       /*一轮结束收尾操作*/                



       *right ^= *left;       

       *left = oldright;

 

       return SUCCESS;

}



INT32 makefirstkey( ULONG32 *keyP )

{

       ULONG32 key[2] = {0};

       ULONG32 *Pkey ;

       ULONG32 *Pbufkey ;

       INT32 j; 

       Pbufkey = (ULONG32*)g_bufkey ;

       Pkey = (ULONG32*)key;

              

       memset((ULONG8*)g_bufkey,0,sizeof(g_bufkey));    

       memcpy((ULONG8*)&key,(ULONG8*)keyP ,8) ;      

       memset((ULONG8*)g_outkey,0,sizeof(g_outkey));

       for(  j = 0 ; j < 28 ; j++)

       {

              if ( wz_keyleft[j] > 32 ) 

              {

                     if ( Pkey[1]&wz_pc2[wz_keyleft[j]-1] )

                     {

                            Pbufkey[0] |= wz_pc2[j] ;

                     }

              }

              else

              {

                     if ( Pkey[0]&wz_pc2[wz_keyleft[j]-1] )

                     {

                            Pbufkey[0] |= wz_pc2[j] ;

                     }

              }

              

              if ( wz_keyright[j] > 32 ) 

              {

                     if ( Pkey[1]&wz_pc2[wz_keyright[j]-1] )

                     {

                            Pbufkey[1] |= wz_pc2[j];

                     }

              }

              else

              {

                     if ( Pkey[0]&wz_pc2[wz_keyright[j]-1] )

                     {

                            Pbufkey[1] |= wz_pc2[j];

                     }

              }

       }

       for (j = 0 ; j < 16 ; j++)

       {

              makekey(&Pbufkey[0],&Pbufkey[1] , j ) ;

       }

       return SUCCESS;

}





INT32 makekey(  ULONG32 *keyleft,ULONG32 *keyright ,ULONG32 number)/*输入密钥的地址,一个32位的*/

{

       ULONG32 tmpkey[2] ={0};

       ULONG32 *Ptmpkey = (ULONG32*)tmpkey;     

       ULONG32 *Poutkey = (ULONG32*)&g_outkey[number]; 

       INT32 j;        

       memset((ULONG8*)tmpkey,0,sizeof(tmpkey));          

              /*要最高的一位或两位*/

              *Ptmpkey = *keyleft&wz_leftandtab[wz_lefttable[number]] ;           

              Ptmpkey[1] = *keyright&wz_leftandtab[wz_lefttable[number]] ;              

              if ( wz_lefttable[number] == 1)

              {

                     *Ptmpkey >>= 27;

                     Ptmpkey[1] >>= 27;

              }

              else

              {

                     *Ptmpkey >>= 26;

                     Ptmpkey[1] >>= 26;                    

              }

              Ptmpkey[0] &= 0xfffffff0;

              Ptmpkey[1] &= 0xfffffff0;

              /*得到高位的值*/

              *keyleft <<= wz_lefttable[number] ;

              *keyright <<= wz_lefttable[number] ;

              *keyleft |= Ptmpkey[0] ;

              *keyright |= Ptmpkey[1] ;            

              Ptmpkey[0] = 0;

              Ptmpkey[1] = 0;

    

       /*从56位中选出48位,3个16位*/

       for ( j = 0 ; j < 48 ; j++)

       {

              if ( j < 24 )

              {

              

                            if ( *keyleft&wz_pc2[wz_keychoose[j]-1])

                            {

                                   Poutkey[0] |= wz_pc2[j] ;

                            }                   

              }            

              

              else /*j>=24*/

              {                   

                            if ( *keyright&wz_pc2[(wz_keychoose[j]-28)])

                            {

                                   Poutkey[1] | = wz_pc2[j-24] ;

                            }                   

              }

       }

       return SUCCESS;

}

代码在win2000/vc6.0环境下测试通过.

作者信息:
地址:西安沣惠南路8号西安大唐电信数据通信部(邮编 710075)
电话:029-8379381
E-Mail:hitwz@{域名已经过期}


附件:
    关闭窗口
    打印文档
    账号登录
    保持登录 忘记密码?
    账号与武进教师培训平台同步