网络对抗期末(一)
加密实验室
实验一:RSA公钥加密和签名实验室
编写一个程序来实现RSA算法,并使用它来进行加密,解密,签名生成和验证。
RSA简介
RSA(RIVEST-Shamir-Adleman)是第一个公钥密码系统之一,广泛用于安全通信。 RSA算法将生成两个大的随机素数,然后使用它们生成公钥和私钥对,该对可用于进行加密,解密,数字签名生成和数字签名验证。 RSA算法建立在数字理论上,并且可以通过库的支持很容易地实现。
RSA算法涉及大量计算。这些计算无法使用程序中的简单算术运算符直接控制,因为这些操作员只能在原始数据类型上运行,例如32位整数和64位长整数类型。 RSA算法涉及的数字通常大于512位。例如,到多个两个32位整数A和B,我们只需要在我们的程序中使用* b。但是,如果他们是大数字,我们就不能再这样做了;相反,我们需要使用算法(即,函数)来计算其产品。
有几个库可以在任意大小的整数上执行算术运算。在此实验室中,我们将使用OpenSSL提供的大数字库。要使用此库,我们将将每个大数字作为Bignum类型,然后使用库提供的API用于各种操作,例如添加,乘法,指数,模块化操作等。
算法流程图:
openssl大数库
openssl/bn.h是OpenSSL库中的头文件,其中定义了用于大整数运算和密码学中的大整数算法实现的数据类型和函数。其中,bn.h中定义的BN结构体用于存储任意长度的大整数数据,并提供了用于生成、操作、释放BN结构体的函数,如BN_new、BN_copy、BN_add等。可以使用bn.h中提供的函数对BN结构体进行大整数的加减乘除、幂运算、模运算、比较、转换等各种操作。同时,由于大多数密码学算法都涉及到大整数运算,因此bn.h也提供了各种密码学算法所需的函数,如RSA加密、签名等。使用openssl/bn.h头文件可以方便地实现大整数操作,便于进行密码学算法的设计和开发。
示例
在这个例子中,我们初始化了三个BIGNUM变量,a,b和n;然后我们计算a * b和(ab mod n)。
/ *bn_sample.c * / #include <stdio.h> #include <openssl/bn.h> #define NBITS 256 void printBN(char *msg, BIGNUM *a) { /* Use BN_bn2hex(a) for hex string * Use BN_bn2dec(a) for decimal string */ char *number_str = BN_bn2hex(a); printf("%s %s\n", msg, number_str); OPENSSL_free(number_str); } int main() { BN_CTX *ctx = BN_CTX_new(); BIGNUM *a = BN_new(); BIGNUM *b = BN_new(); BIGNUM *n = BN_new(); BIGNUM *res = BN_new(); // Initialize a, b, n BN_generate_prime_ex(a, NBITS, 1, NULL, NULL, NULL); BN_dec2bn(&b, "273489463796838501848592769467194369268"); BN_rand(n, NBITS, 0, 0); // res = a *b BN_mul(res, a, b, ctx); printBN("a * b = ", res); // res = aˆb mod n BN_mod_exp(res, a, b, n, ctx); printBN("aˆc mod n = ", res); return 0; }
任务1:派生私钥
设 p、q 和 e 为三个素数。设 n=p×q。我们将使用(e,n)作为公钥。请计算私钥d。下面列出了 p、q 和 e 的十六进制值。应该注意的是,尽管此任务中使用的p和q是相当大的数字,但它们不够大而不安全。为了简单起见,我们故意使它们变小。在实践中,这些数字的长度应至少为 512 位(此处使用的数字仅为 128 位)。
p = F7E75FDC469067FFDC4E847C51F452DF q = E85CED54AF57E53E092113E62F436F4F e = 0D88C3
#include <stdio.h> #include <openssl/bn.h> #define NBITS 128 void printBN(char *msg, BIGNUM *a) { /* Use BN_bn2hex(a) for hex string * Use BN_bn2dec(a) for decimal string */ char *number_str = BN_bn2hex(a); printf("%s %s\n", msg, number_str); OPENSSL_free(number_str); } int main() { BN_CTX *ctx = BN_CTX_new(); BIGNUM *p = BN_new(); BIGNUM *q = BN_new(); BIGNUM *fai_n = BN_new(); BIGNUM *n = BN_new(); BIGNUM *e = BN_new(); BIGNUM *d = BN_new(); BIGNUM *p_1 = BN_new(); BIGNUM *q_1 = BN_new(); BN_hex2bn(&p, "F7E75FDC469067FFDC4E847C51F452DF"); BN_hex2bn(&q, "E85CED54AF57E53E092113E62F436F4F"); BN_hex2bn(&e, "0D88C3"); BN_sub(p_1, p, BN_value_one()); BN_sub(q_1, q, BN_value_one()); BN_mul(n, p, q, ctx); BN_mul(fai_n, p_1, q_1, ctx); //printBN("fai_n=", fai_n); BN_mod_inverse(d, e, fai_n, ctx); printBN("public key e=\t", e); printBN("public key n=\t", n); printBN("private key d=\t", d); return 0; }
求得:
public key e=0D88C3 public key n=E103ABD94892E3E74AFD724BF28E78366D9676BCCC70118BD0AA1968DBB143D1 private key d=3587A24598E5F2A21DB007D89D18CC50ABA5075BA19A33890FE7C28A9B496AEB
任务2:加密消息
设 (e, n) 为公钥。请加密消息“A top secret!”(引号不包括在内)。我们需要将此 ASCII 字符串转换为十六进制字符串,然后使用 hex-to-bn API BN hex2bn() 将十六进制字符串转换为 BIGNUM。以下 python 命令可用于将普通 ASCII 字符串转换为十六进制字符串(或者使用od命令)。
$ python -c ' print("A top secret!".encode("hex")) '4120746f702073656372657421
#include <stdio.h> #include <openssl/bn.h> #define NBITS 128 void printBN(char *msg, BIGNUM *a) { /* Use BN_bn2hex(a) for hex string * Use BN_bn2dec(a) for decimal string */ char *number_str = BN_bn2hex(a); printf("%s %s\n", msg, number_str); OPENSSL_free(number_str); } int main() { BN_CTX *ctx = BN_CTX_new(); BIGNUM *n = BN_new(); BIGNUM *e = BN_new(); BIGNUM *d = BN_new(); BIGNUM *m = BN_new(); //massage //BIGNUM* p = BN_new(); //plaintxt BIGNUM *c = BN_new(); //cyphertxt BN_hex2bn(&n, "DCBFFE3E51F62E09CE7032E2677A78946A849DC4CDDE3A4D0CB81629242FB1A5"); BN_hex2bn(&e, "010001"); BN_hex2bn(&d, "74D806F9F3A62BAE331FFE3F0A68AFE35B3D2E4794148AACBC26AA381CD7D30D"); BN_hex2bn(&m, "4120746f702073656372657421"); BN_mod_exp(c, m, e, n, ctx); //BN_mod_exp(p, c, d, n, ctx); printBN("cyphertxt:", c); //printBN("plaintxt:", p); return 0; }
加密结果:
6FB078DA550B2650832661E14F4F8D2CFAEF475A0DF3A75CACDC5DE5CFC5FADC
任务3:解密消息
解密上面的加密结果
解密结果为:
4120746F702073656372657421
使用python将其转换成ASCII字符串:
python3 -c 'print(bytes.fromhex("4120746F702073656372657421").decode("utf-8"))'
任务4:签名
通过签署数字文件提供真实性证明
在M使用私钥对M应用私钥操作,并获得一个数字,每个人都可以使用我们的公钥来从s回来对于需要签名的消息:
数字签名=md mod n
在实践中,消息可能很长,从而产生长签名和更多的计算时间,因此,我们从原始信息中生成加密哈希值,并且只签署哈希值
此任务中使用的公钥/私钥与task 2中使用的公钥/私钥相同。请为以下消息生成签名(请直接签名此消息,而不是签名其哈希值):
M = I owe you $2000.
对M消息做一点小小的修改,比如把2000成3000,并在修改后的消息上签名。比较两个签名并描述你所观察到的。
M = I owe you $3000.
转化为十六进制字符串
# python3 python -c 'print("I owe you $2000".encode("hex"))' python -c 'print("I owe you $3000".encode("hex"))'
得到:
49206f776520796f75202432303030
49206f776520796f75202433303030
可见,虽然信息只做了稍微的改动,签名结果也会发生很大的变化。
任务5:验证签名
Bob收到来自Alice的消息M = "Launch a missile.",其签名为s。我们知道Alice的公钥是(e, n),请验证该签名是否确实是Alice的。公钥和签名(十六进制)如下所示:
M = Launch a missile. S = 643D6F34902D9C7EC90CB0B2BCA36C47FA37165C0005CAB026C0542CBDB6802F e = 010001 (this hex value equals to decimal 65537) n = AE1CD4DC432798D933779FBD46C6E1247F0CF1233595113AA51B450F18116115
假设in的签名已损坏,签名的最后一个字节从2F更改为3F,即只有一个比特的更改。请重复这个任务,并描述验证过程会发生什么。
#include <stdio.h> #include <openssl/bn.h> #define NBITS 128 void printBN(char *msg, BIGNUM *a) { /* Use BN_bn2hex(a) for hex string * Use BN_bn2dec(a) for decimal string */ char *number_str = BN_bn2hex(a); printf("%s %s\n", msg, number_str); OPENSSL_free(number_str); } int main() { BN_CTX *ctx = BN_CTX_new(); BIGNUM *n = BN_new(); BIGNUM *e = BN_new(); BIGNUM *M = BN_new(); BIGNUM *m1 = BN_new(); BIGNUM *m2 = BN_new(); BIGNUM *sig1 = BN_new(); BIGNUM *sig2 = BN_new(); BN_hex2bn(&M, "4c61756e63682061206d697373696c652e"); BN_hex2bn(&n, "AE1CD4DC432798D933779FBD46C6E1247F0CF1233595113AA51B450F18116115"); BN_hex2bn(&e, "010001"); BN_hex2bn(&sig1, "643D6F34902D9C7EC90CB0B2BCA36C47FA37165C0005CAB026C0542CBDB6802F"); BN_hex2bn(&sig2, "643D6F34902D9C7EC90CB0B2BCA36C47FA37165C0005CAB026C0542CBDB6803F"); BN_mod_exp(m1, sig1, e, n, ctx); BN_mod_exp(m2, sig2, e, n, ctx); printf("verifying of signature1:"); if (BN_cmp(m1, M) == 0) { printf("valid!\n"); } else { printf("invalid!\n"); } printf("verifying of signature2:"); if (BN_cmp(m2, M) == 0) { printf("valid!\n"); } else { printf("invalid!\n"); } return 0; }
当签名没有损坏时,可以验证成功。即使签名只变动了一位,也会使得验证失败。