您好,欢迎来到九壹网。
搜索
您的当前位置:首页DES算法实验报告

DES算法实验报告

来源:九壹网


实验4 DES密码算法

一、基于OBE模式的实验目的和要求

1、了解分组密码的起源与涵义。 2、掌握DES密码的加解密原理。 3、编程设计DES密码算法。

二、实验仪器和设备 Visual C/C++ 三、实验过程

1、DES是美国联邦信息处理标准(FIPS)于1977年公开的分组密码算法,它的设计基于Feistel对称网络以及精心设计的S盒,在提出前已经进行了大量的密码分析,足以保证在当时计算条件下的安全性。不过,随着计算能力的飞速发展,现如今DES已经能用密钥穷举方式破解。虽然现在主流的分组密码是AES,但DES的设计原理仍有重要参考价值。在本实验中,为简便起见,就限定DES密码的明文、密文、密钥均为bit,具体描述如下:

明文m是bit序列。

初始密钥K是 bit序列(含8个奇偶校验bit)。 子密钥K1, K2…K16均是48 bit序列。

轮变换函数f (A,J):输入A(32 bit序列), J(48 bit序列),输出32 bit序列。 密文c是 bit序列。 1) 子密钥生成:

输入初始密钥,生成16轮子密钥K1, K2…K16。

初始密钥(bit)经过置换PC-1,去掉了8个奇偶校验位,留下56 bit,接着分成两个28 bit的分组C0与D0,再分别经过一个循环左移函数LS1,得到C1与D1,连成56 bit数据,然后经过置换PC-2,输出子密钥K1,以此类推产生K2至K16。

注意:置换PC-1、PC-2在下文中会具体给出; 函数LSi为向左循环移动 1个位置(i=1,2,9,16)

2个位置(i=3,4,5,6,7,8,10,11,12,13,14,15)。详见下图:

初始密钥(bit) 置换PC-1 C0(28bit) LS1 C1 LS2 D0(28bit) LS1 D1 LS2 置换PC-2 K1 … LS1C16 LS16 D16 置换PC-2 K16

2) 轮变换函数f:

f是DES加解密中每一轮的核心运算,输入A(32 bit), J(48 bit),输出32 bit。 将A做一个扩展运算E,变成48 bit,记为E(A)。计算B=E(A)⊕J,将B分为8组B1…B8,每组Bi为6 bit,通过相应的S盒Si,输出Ci为4 bit,将所有Ci连成C(32 bit),再通过置换P,得到最后的输出f(A,J),为32 bit。在加密或解密的第i轮,A = Ri-1,J = Ki。

注意:每个S盒Si是4×16的矩阵,输入b0b1b2b3b4b5 (6 bit),令L是b0b5对应的十进制数,n是b1b2b3b4对应的十进制数,输出矩阵中第L行n列所对应数的二进制表示。 详见下图:

A (32bit) E J (48bit) E(A) (48bit) B1 B2 B3 B4 B5 B6 B7 B8 6 bit 4 bit C1 C2 C3 C4 C5 C6 C7 C8 P f(A,J) (32bit) S1 S2 S3 S4 S5 S6 S7 S8

3) 加/解密:

DES的加密和解密几乎一样,不同之处在于加密时输入是明文,子密钥使 用顺序为K1K2…K16;解密时输入是密文,子密钥使用顺序为K16K15…K1。 以加密为例,输入明文分组 bit,先进行一次初始置换IP,对置换后的数据X0分成左右两半L0与R0,根据第一个子密钥K1对R0实行轮变换f(R0, K1),将结果与L0作逐位异或运算,得到的结果成为下一轮的R1,R0则成为下一轮的L1。如此循环16次,最后得到L16与R16。可用下列公式简洁地表示:

RiLi1f(Ri1,Ki)LiRi1,i1,2,,16

最后对 bit数据R16, L16实行IP的逆置换IP-1,即得密文分组。

注意:第16轮变换后并未交换L16与R16,而直接将R16, L16作为IP-1的输入。 详见下图:

输入 初始置换IP L0 f R0 K1 L1=R0 f R1=L0⊕f(R0,K1) Ki L15=R14 f R16=L15⊕f(R15,K16) R15=L14⊕f(R14,K15) K16 L16=R15 逆初始置换IP-1 输出

3、编写程序,实现DES密码。 主要步骤:

1) 使用模块化编程,将算法实现的功能写成子函数后供上一层函数调用。 2) 所使用参数的初始定义 #include

static bool SubKey[16][48]; //定义子密钥,使得所有函数都能调用 static const unsigned char IP_Table[] = { //定义IP置换表

58,50,42,34,26,18,10,2,60,52,44,36,28,20,12,4, 62,54,46,38,30,22,14,6,,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 const unsigned char IPInv_Table[] = { //定义IP逆置换表 40,8,48,16,56,24,,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 const unsigned char E_Table[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 const unsigned char P_Table[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 const unsigned char PC1_Table[56] = { 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, 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 const unsigned char PC2_Table[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 const unsigned char LS_Table[16] = { 1,1,2,2,2,2,2,2,1,2,2,2,2,2,2,1};

static unsigned char S_Box[8][4][16] = { //定义E扩展表 //定义P置换表 //定义PC1置换表 //定义PC2置换表 //定义左移位数表

//S盒

//S1

14,4,13,1,2,15,11,8,3,10,6,12,5,9,0,7, 0,15,7,4,14,2,13,1,10,6,12,11,9,5,3,8, 4,1,14,8,13,6,2,11,15,12,9,7,3,10,5,0, 15,12,8,2,4,9,1,7,5,11,3,14,10,0,6,13, //S2

15,1,8,14,6,11,3,4,9,7,2,13,12,0,5,10, 3,13,4,7,15,2,8,14,12,0,1,10,6,9,11,5, 0,14,7,11,10,4,13,1,5,8,12,6,9,3,2,15, 13,8,10,1,3,15,4,2,11,6,7,12,0,5,14,9, //S3

10,0,9,14,6,3,15,5,1,13,12,7,11,4,2,8, 13,7,0,9,3,4,6,10,2,8,5,14,12,11,15,1, 13,6,4,9,8,15,3,0,11,1,2,12,5,10,14,7, 1,10,13,0,6,9,8,7,4,15,14,3,11,5,2,12, //S4

7,13,14,3,0,6,9,10,1,2,8,5,11,12,4,15, 13,8,11,5,6,15,0,3,4,7,2,12,1,10,14,9, 10,6,9,0,12,11,7,13,15,1,3,14,5,2,8,4, 3,15,0,6,10,1,13,8,9,4,5,11,12,7,2,14, //S5

2,12,4,1,7,10,11,6,8,5,3,15,13,0,14,9, 14,11,2,12,4,7,13,1,5,0,15,10,3,9,8,6, 4,2,1,11,10,13,7,8,15,9,12,5,6,3,0,14, 11,8,12,7,1,14,2,13,6,15,0,9,10,4,5,3, //S6

12,1,10,15,9,2,6,8,0,13,3,4,14,7,5,11, 10,15,4,2,7,12,9,5,6,1,13,14,0,11,3,8, 9,14,15,5,2,8,12,3,7,0,4,10,1,13,11,6, 4,3,2,12,9,5,15,10,11,14,1,7,6,0,8,13,

//S7

4,11,2,14,15,0,8,13,3,12,9,7,5,10,6,1, 13,0,11,7,4,9,1,10,14,3,5,12,2,15,8,6, 1,4,11,13,12,3,7,14,10,15,6,8,0,5,9,2, 6,11,13,8,1,4,10,7,9,5,0,15,14,2,3,12, //S8

13,2,8,4,6,15,11,1,10,9,3,14,5,0,12,7, 1,15,13,8,10,3,7,4,12,5,6,11,0,14,9,2, 7,11,4,1,9,12,14,2,0,6,10,13,15,3,5,8, 2,1,14,7,4,10,8,13,15,12,9,0,3,5,6,11};

3) 编写各个模块函数,例如:

void ByteToBit(bool* Out, const unsigned char* In, int bits);

//将字符数组In转化为bool数组Out,bits控制转换bit数

void BitToByte(unsigned char* Out, const bool* In, int bits);

//将bool数组In转化为字符数组Out

void Transform(bool* Out, bool* In, const unsigned char* Table, int len);

//利用置换表Table将bool数组In转换成长度为len的bool数组Out void RotateL(bool* In, int len, int loop);

//对长度为len的bool数组In循环左移loop位 void Des_SetSubKey(unsigned char Key[8]);

//利用初始密钥Key生成16轮子密钥SubKey, 注意前文已定义static bool SubKey[16][48]; void Xor(bool* InA, bool* InB, int len);

//将bool数组InB与InA逐位做异或,结果存入InA void S_Func(bool Out[32], bool In[48]);

//利用S盒S_Box将bool数组In转换为bool数组Out void f_Func(bool In[32], bool Ki[48]);

//利用子密钥Ki对bool数组In做轮变换函数,结果仍存入In void Des_Run(unsigned char Out[8], unsigned char In[8], bool flag);

//Des加解密:输入字符数组In,输出字符数组Out,

flag=1时代表加密,flag=0时代表解密。

4) 编写主函数,主要步骤有 输入明文字符序列(8个字符); 输入密钥字符序列(8个字符); 生成16轮子密钥; 加密并输出密文; 解密并输出解密后的明文。

提示点:

1) 编写函数时多输出中间变量查看结果。

2) 函数memcpy(void *dest, const void *src, size_t n) 用于将源地址src处n 个单位数据拷贝至目的地址dest处, 适用于bool数组的拷贝。

3) 部分函数参考代码

void Transform(bool* Out, bool* In, const unsigned char* Table, int len) // 置换,输入为置换前的位,输出为置换后的位,具体的置换由参数Table控制,置换后的长度由参数len输入

{

}

void ByteToBit(bool* Out, const char* In, int bits) //字符转换为比特,输入为字符In, 输出的二进制位存放在Out { }

for (int i = 0; i < bits; i++)

Out[i] = (In[i / 8] >> (i % 8)) & 1;

bool Temp[256];

//临时数组长度定义成足够大的即可

for(int i=0;iTemp[i] = In[Table[i]-1];

memcpy(Out, Temp, len);

//In[i]的第N位右移N位并和0x01按位与运算

void BitToByte(char* Out, const bool* In, int bits) //比特转换为字符,输入二进制为In, 输出的字符存放在Out { }

void Xor(bool *InA,bool *InB, int len) //异或 {

for(int i = 0; iInA[i] ^= InB[i]; } }

void RotateL(bool *In, int len, int loop) //循环左移 {

static int Tmp[256]; memcpy(Tmp, In, loop); memcpy(In, In+loop, len-loop); memcpy(In+len-loop, Tmp, loop); }

void S_func(bool Out[32],bool In[48]) // S 盒,输入6位,输出4位,将第一六位对应的十进制数作为行,第二三四五为的对应十进制数作为列 {

for(char i=0,j,k; i<8; i++,In+=6,Out+=4)

int i;

memset(Out, 0, (bits + 7) / 8); for (i = 0; i < bits; i++)

Out[i / 8] |= In[i] << (i % 8);

{

j = (In[0]<<1) + In[5];

k = (In[1]<<3) + (In[2]<<2) + (In[3]<<1) + In[4]; ByteToBit(Out, &S_Box[i][j][k], 4); } }

void F_func(bool In[32],bool Ki[48]) // f 轮函数 {

static bool MR[48];

Transform(MR, In, E_Table, 48); // 置换 Xor(MR, Ki, 48); // 异或 S_func(In, MR); // S 盒

Transform(In, In, P_Table, 32); // 置换 }

测试示例:利用DES加密算法,实现“Hi, this is chapter 2!”字符串的加密,并同时解密,例如,加密密钥取为:12345678(字符形式,也可自选)。

4、程序代码:

#include #include #include using namespace std;

const static char IP[] =// 初始置换 {

58, 50, 42, 34, 26, 18, 10, 2, 60, 52, 44, 36, 28, 20, 12, 4, 62, 54, 46, 38, 30, 22, 14, 6, , 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 };

const static char EP1[56] =// 密钥置换(原 位去掉奇偶校验位后) {

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, 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 };

const static char LOOP[16] =// 左移 {

1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1 };

const static char EP2[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 const char EC[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 };

const static char SBox[8][4][16] =//8 个 S 盒 { { // S1

{ 14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7 }, { 0, 15, 7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8 }, { 4, 1, 14, 8, 13, 6, 2, 11, 15, 12, 9, 7, 3, 10, 5, 0 }, { 15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13 } }, { // S2

{ 15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10 }, { 3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5 }, { 0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15 }, { 13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9 } }, { // S3

{ 10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8 }, { 13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1 }, { 13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7 }, { 1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12 } }, { // S4

{ 7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15 }, { 13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9 }, { 10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4 }, { 3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14 } }, { // S5

{ 2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9 }, { 14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6 },

{ 4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12, 5, 6, 3, 0, 14 }, { 11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3 } }, { // S6

{ 12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11 }, { 10, 15, 4, 2, 7, 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8 }, { 9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6 }, { 4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13 } }, { // S7

{ 4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1 }, { 13, 0, 11, 7, 4, 9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6 }, { 1, 4, 11, 13, 12, 3, 7, 14, 10, 15, 6, 8, 0, 5, 9, 2 }, { 6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12 } }, { // S8

{ 13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7 }, { 1, 15, 13, 8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2 }, { 7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, 15, 3, 5, 8 }, { 2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11 } } };

const static char PP[32] =//P 盒置换 {

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, };

const static char LP[] =// 末置换 {

40, 8, 48, 16, 56, 24, , 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 bool M[], tmp[32], *Li = &M[0], *Ri = &M[32]; static bool SubKey[16][48];//16 个子密钥 class CDES// 定义 DES 类 { public:

//void Mode();// 模式

void Encryption(char out[8],char In[8]);// 加密函数 void Decryption(char out[8],char In[8]);// 解密函数 };

void ByteToBit(bool *Out, const char *In, int bits)// 字符转换成字节 { int i;

for(i=0;i// In[i] 的第 N 位右移 N 位并和 0x01 按位 \" 与 \" 运算 (N=1~8) Out[i]=(In[i/8]>>(i%8))&1; } }

void BitToByte(char *Out,const bool *In,int bits)// 字节转换成字符 { int i;

memset(Out,0,(bits+7)/8); for(i=0;iOut[i/8]|=In[i]<<(i%8); } }

void RotateL(bool*In,int len,int loop)// 循环左移 {

static bool tmp[256]; memcpy(tmp,In,loop);

memcpy(In,In+loop,len-loop); memcpy(In+len-loop,tmp,loop); }

void Xor(bool*InA,const bool*InB,int len)// 异或 { int i;

for(i=0;iInA[i]^=InB[i]; } }

void Transform(bool*Out,bool*In,const char*Table,int len)// 各个置换转换 { int i;

static bool tmp[256]; for(i=0;itmp[i]=In[Table[i]-1]; }

memcpy(Out,tmp,len); }

void S_func(bool Out[32],const bool In[48])// 将 48 位转换成 32 位 {

int j,m,n;

// 膨胀后的比特串分为 8 组,每组 6 比特。 for(j=0;j<8;j++,In+=6,Out+=4) {

m = (In[0]*2)+In[5];

n = (In[1]*8)+(In[2]*4)+(In[3]*2)+In[4]; ByteToBit(Out,&SBox[j][m][n],4); } }

void F_func(bool In[32],const bool Ki[48]) {

static bool MR[48]; Transform(MR,In,EC,48); Xor(MR, Ki, 48);

// 膨胀后的比特串分为 8 组,每组 6 比特。各组经过各自的 S 盒后,又变为 4 比特,合并后又成为 32 比特。 S_func(In, MR);

// 该 32 比特经过 P 变换后,输出的比特串才是 32 比特的 f(Ri-1,Ki) 。 Transform(In, In, PP, 32); }

void SetKey(char key[8])// 生成子密钥 { int i;

static bool K[], *KL = &K[0], *KR = &K[28]; ByteToBit(K,key,); // 转换为二进制

Transform(K,K,EP1,56); // 比特的密钥 K ,经过 EP1 后,生成 56 比特的串。 // 生成 16 个子密钥 for(i=0;i<16;i++) {

// 循环左移,合并 RotateL(KL,28,LOOP[i]); RotateL(KR,28,LOOP[i]); Transform(SubKey[i],K,EP2,48); } }

void CDES::Encryption(char out[8],char In[8])// 加密函数 {

ByteToBit(M,In,); // 转换为二进制 Transform(M,M,IP,); for(int i=0;i<16;i++) {

memcpy(tmp,Ri,32); F_func(Ri,SubKey[i]);

Xor(Ri,Li,32);// 将所得结果与明文的左 32 位进行异或 memcpy(Li,tmp,32);// 将明文的左右 32 位交换 }

Transform(M, M, LP, );

BitToByte(out, M, ); // return(out); }

void CDES::Decryption(char out[8],char In[8])// 解密函数 ( 加密的逆过程 )

{

ByteToBit(M,In,); // 转换为二进制 Transform(M,M,IP,); for(int i=15;i>=0;i--) {

memcpy(tmp,Li,32); F_func(Li,SubKey[i]); Xor(Li,Ri,32); memcpy(Ri,tmp,32); }

Transform(M, M, LP, ); BitToByte(out, M, ); // return(out); }

int main() {

char key[10]; char str[10]; char str1[10];

cout<<\" 请输入要加密的字符 ( 只限字符类 ):\"; cin>>str;

cout<<\" 请设置密码 ( 不多于 8 位 ):\"; cin>>key; SetKey(key);

memset(str1,0,sizeof(str1)); CDES des;

des.Encryption(str1,str); cout<<\" 加密后为: \"<>n; if(n==1) {

while(1) {

cout<<\" 请输入密码: \"; char sec[10];

cin>>sec;

if(strcmp(sec,key)==0) {

memset(str,0,sizeof(str)); des.Decryption(str,str1); cout<<\" 解密后为: \"<cout<<\" 密码错误 ! \"<cout<<\" 过程结束 ! \"<四、实验结果与分析

五、基于OBE模式的学生自我评价与体会

这次的实验跟我们以前做的实验不同,因为我觉得这次我是真真正正的自己亲自去完成。所以是我觉得这次实验最宝贵,最深刻的。就是实验的过程全是我们学生自己动手来完成的,这样,我们就必须要弄懂实验的原理。在这里我深深体会到哲学上理论对实践的指导作用:弄懂实验原理,而且体会到了实验的操作能力是靠自己亲自动手,亲自开动脑筋,亲自去请教别人才能得到提高的。

因篇幅问题不能全部显示,请点此查看更多更全内容

Copyright © 2019- 91gzw.com 版权所有 湘ICP备2023023988号-2

违法及侵权请联系:TEL:199 18 7713 E-MAIL:2724546146@qq.com

本站由北京市万商天勤律师事务所王兴未律师提供法律服务