RSA_NC不互素

1.题目

from Crypto.Util.number import *
import gmpy2
from flag import m

p = getPrime(1024)
q = getPrime(1024)
n = p * q
print('n =', n)
e = 0x10001
M = m * e * 1 * 2022 * p
c = pow(M, e, n)
print('c =', c)

#n = 16266043783454053154037197753138388613864200794483663334493856481522764684650995230938142916968470804276539967429581472897698022852787399956166067156691430593337430691851251036378709799238876668312530223697905925939542713491015517460139150765778057817475571231361809654951289718071760502692960235551663466242938669673675870151921605230499603814070711617511206013584605131901906195136038060653121164252894949526861390984185085201067988694831398388037080993820517447099157891181179389949333832439004857436617834100885739716577641892686620423154860716308518151628754780994043553863224363539879909831811888663875989774849
#c = 12716190507848578560760116589677996073721225715245215495257947887969923319693501568134141757778665747980229898129090929698368855086594836111461700857934476682700625486249555753323344759513528101651108919161794915999809784961533946922607642974500946026677116418317599095703217004064379100607278317877894742815660315660254853364776654303066021672567442581774299847661025422994141801987588151758971034155714424052693627277202951522779716696303237915400201362585413354036973117149974017434406560929491956957193491445847385625481870256240443170803497196783872213746269940877814806857222191433079944785910813364137603874411

2.思路

题目给的M = m * e * 1 * 2022 * p值得我们注意,说明p是M的因子,那么我们可以从这里入手

进行推论如下:

假设c=(m*p)^e%n
=> c=(m*p)^e+kn
=> c=m^e*p^e+kn
=> c=k1p+kn
=> c=k1p+kpq
=> c=kp
又因为M = m * e * 1 * 2022 * p
p=gcd(n,c)
利用这个结论求出p,之后的问题就迎刃而解了

3.解题代码

import gmpy2
import libnum

n = 16266043783454053154037197753138388613864200794483663334493856481522764684650995230938142916968470804276539967429581472897698022852787399956166067156691430593337430691851251036378709799238876668312530223697905925939542713491015517460139150765778057817475571231361809654951289718071760502692960235551663466242938669673675870151921605230499603814070711617511206013584605131901906195136038060653121164252894949526861390984185085201067988694831398388037080993820517447099157891181179389949333832439004857436617834100885739716577641892686620423154860716308518151628754780994043553863224363539879909831811888663875989774849
c = 12716190507848578560760116589677996073721225715245215495257947887969923319693501568134141757778665747980229898129090929698368855086594836111461700857934476682700625486249555753323344759513528101651108919161794915999809784961533946922607642974500946026677116418317599095703217004064379100607278317877894742815660315660254853364776654303066021672567442581774299847661025422994141801987588151758971034155714424052693627277202951522779716696303237915400201362585413354036973117149974017434406560929491956957193491445847385625481870256240443170803497196783872213746269940877814806857222191433079944785910813364137603874411
e = 65537

# 分解n(通过gcd(c, n)直接得到p)
p = gmpy2.gcd(c, n)
q = n // p
assert p * q == n  # 验证分解正确性

# 计算私钥d
phi = (p - 1) * (q - 1)
d = gmpy2.invert(e, phi)

# 解密得到M = m * e * 2022 * p
M = pow(c, d, n)

# 恢复明文m
m = M // (e * 2022 * p)

# 将m转为字节(flag)
flag = libnum.n2s(int(m))
print(flag.decode())

结果如下:

<think>我们有一个RSA加密和解密的接口集合,我们需要实现一个程序,该程序可以对指定的文件进行加密或解密操作。 程序需要支持以下功能: 1. 生成RSA密钥对并保存到文件(可选,如果用户需要生成密钥) 2. 使用公钥加密文件 3. 使用私钥解密文件 由于接口中包含了密钥生成、保存、加载,以及加密解密函数,我们可以设计如下: 程序流程: 1. 解析命令行参数,确定操作模式(生成密钥、加密、解密)以及相应的参数(如密钥文件、密码、输入文件、输出文件等)。 2. 根据模式执行相应操作。 操作模式: - 生成密钥:指定密钥位数、私钥文件、公钥文件、密码(可选) - 加密:指定公钥文件、输入文件(明文)、输出文件(密文) - 解密:指定私钥文件、输入文件(密文)、输出文件(明文)、密码(如果私钥有加密的话) 注意:RSA加密有长度限制,所以对于大文件,我们能一次性加密。通常的做法是: - 生成一个对称密钥(比如AES密钥) - 用对称密钥加密文件 - 用RSA公钥加密对称密钥 - 将加密后的对称密钥和加密的文件内容一起存储 但是,根据题目给出的接口,我们只有RSA的加密解密函数,而且接口是直接对一段数据进行加密解密。 由于RSA加密的数据长度受密钥长度限制,例如1024位密钥最多只能加密117字节(128-11,因为使用PKCS#1 v1.5 padding)。 因此,对于大文件,我们需要分块加密。但是分块加密也有问题:效率低,且每块加密后的长度会固定为密钥长度(例如128字节)。 过,题目要求使用给定的接口,所以我们按照分块加密的方式来实现。 步骤: 1. 初始化:调用rsa_init()(如果该函数用于初始化库,比如OpenSSL的初始化) 2. 加载密钥:根据模式加载公钥(加密)或私钥(解密) 3. 打开输入文件和输出文件 4. 分块读取输入文件,然后调用加密或解密函数处理每一块,将结果写入输出文件。 5. 清理:关闭文件,释放密钥,调用rsa_cleanup() 注意:分块的大小需要根据密钥长度和填充方式确定。我们假设使用PKCS#1 v1.5填充(这是RSA加密的常见填充方式)。 加密时,每次读取的明文块长度应小于等于RSA_size(pub_key) - 11(因为填充需要11字节)。 解密时,每次读取的密文块长度应为RSA_size(priv_key)(即密钥的模长,以字节为单位)。 但是,给定的接口中,加密函数为: int rsa_encrypt(RSA* pub_key, const unsigned char* plaintext, int pt_len, unsigned char* ciphertext); 解密函数为: int rsa_decrypt(RSA* priv_key, const unsigned char* ciphertext, int ct_len, unsigned char* plaintext); 根据常规,rsa_encrypt函数返回加密后的密文长度,应该等于RSA密钥的模长(字节数)。同样,解密后的明文长度应该等于我们传入的明文块长度(因为是对称的)。 因此,我们可以这样分块: 加密: 块大小(读入):chunk_size = RSA_size(pub_key) - 11 密文块大小:rsa_size = RSA_size(pub_key) 每次读取chunk_size字节,加密后得到rsa_size字节,写入输出文件。 解密: 块大小(读入):rsa_size = RSA_size(priv_key) (因为密文块是固定大小的) 每次读取rsa_size字节,解密后得到chunk_size(即加密时使用的块大小)字节,写入输出文件。 但是,注意:在解密时,我们知道加密时使用的块大小(即chunk_size),但我们可以知道解密后的数据块长度,因为rsa_decrypt函数会返回实际解密出的明文长度。 因此,在解密时,我们读取固定大小的块(rsa_size),然后解密,得到实际长度,然后写入该长度的数据。 但是,这里有一个问题:文件大小可能是块的整数倍。在加密时,最后一块可能足chunk_size,我们仍然可以加密,因为函数允许传入任意长度(只要超过chunk_size)。解密时,最后一块也是完整的rsa_size,所以没有问题。 但是,如果文件很大,我们需要考虑内存分配。我们可以为每个块动态分配内存,但更高效的方式是使用栈上的数组(如果块大小大,比如1024位密钥对应128字节,那么我们可以分配128字节的栈数组)。但是,如果密钥很大(比如4096位),那么块大小会达到512字节,仍然可以接受。 然而,为了灵活性,我们使用动态分配:为每个块分配内存(加密时:输入块chunk_size,输出块rsa_size;解密时:输入块rsa_size,输出块chunk_size)。但是,由于chunk_size=rsa_size-11,所以解密后的块最大为rsa_size-11,我们可以分配一个同样大小的缓冲区。 但是,注意:解密时,我们知道加密时使用的块大小,但我们可以知道解密后的数据长度,所以我们可以按实际长度写入。 具体步骤(加密): 1. 打开输入文件(明文)和输出文件(密文)。 2. 获取RSA_size(pub_key),记为rsa_size,计算明文块大小chunk_size = rsa_size - 11。 3. 分配内存:plain_chunk = malloc(chunk_size); cipher_chunk = malloc(rsa_size); 4. 循环读取:每次读取chunk_size字节到plain_chunk,实际读取到的长度len。 如果len>0,则调用rsa_encrypt加密这块数据,得到密文长度cipher_len(应该等于rsa_size),然后将cipher_chunk写入输出文件。 如果len==0,则退出循环。 5. 注意最后一块可能足chunk_size,但我们仍然可以加密,因为函数允许传入小于chunk_size的长度(但要超过)。 解密步骤: 1. 打开输入文件(密文)和输出文件(明文)。 2. 获取RSA_size(priv_key),记为rsa_size。 3. 分配内存:cipher_chunk = malloc(rsa_size); plain_chunk = malloc(rsa_size); // 因为解密后最大长度是rsa_size-11,但为了简单,分配rsa_size也够 4. 循环读取:每次读取rsa_size字节到cipher_chunk,实际读取到len。 如果len != rsa_size,则可能出错(因为密文应该是rsa_size的整数倍),除非最后一块?但加密时我们写的是完整块,所以文件大小应该是rsa_size的整数倍。所以如果读取足,且是最后一块(即len>0且足),则报错。 调用rsa_decrypt,传入cipher_chunk和len(注意:len应该等于rsa_size,否则解密函数可能失败),得到明文长度plain_len,然后将plain_chunk的前plain_len字节写入输出文件。 5. 注意:如果最后一块读取时,文件结束,那么读取到的长度可能小于rsa_size,这应该视为错误,因为加密后的文件长度必须是rsa_size的整数倍。 但是,加密时,我们每次写入一个完整的密文块(rsa_size字节),所以密文文件大小一定是rsa_size的整数倍。因此,解密时,如果读取到完整的块,就报错。 另外,我们还需要考虑错误处理:加密或解密函数返回错误。 根据以上思路,我们编写程序。 首先,我们定义程序的使用方法: 生成密钥: ./rsa_tool -g -b <bits> -p <private key file> -u <public key file> [-pass <password>] 加密: ./rsa_tool -e -i <input file> -o <output file> -u <public key file> 解密: ./rsa_tool -d -i <input file> -o <output file> -p <private key file> [-pass <password>] 然后,我们实现主函数,解析参数。 由于我们使用C语言,并且是在Linux平台,我们可以使用getopt来解析参数。 注意:在调用rsa_init()时,我们可能需要进行一些初始化工作(比如OpenSSL的初始化)。同样,在结束时调用rsa_cleanup()。 我们假设这些接口是基于OpenSSL实现的,但题目没有给出具体实现,我们只使用这些接口。 下面我们开始编写代码。 由于代码较长,我们将分步骤编写,并添加必要的注释。 注意:为了安全,密码在解密时可能需要从命令行或环境变量获取,这里我们简单地从命令行参数获取(-pass参数)。 但是,在保存私钥时,如果提供了密码,则私钥文件会被加密。在加载私钥时,需要提供相同的密码。 我们开始编写代码框架: 步骤: 1. 包含必要的头文件:stdio.h, stdlib.h, string.h, unistd.h (for getopt) 2. 定义全局变量(需要) 3. 编写主函数,解析参数 4. 根据同的模式执行操作 由于我们知道rsa_init等函数的声明,我们假设有一个头文件包含了这些声明。这里我们假设这些函数已经存在,我们将调用它们。 为了简化,我们假设密码最大长度为256。 另外,由于RSA加密解密的分块处理,我们按照上述逻辑实现。 代码实现如下:</think>下面是一个使用提供的RSA接口实现文件加密/解密的C语言程序。该程序支持生成密钥对、加密文件和解密文件操作,并处理大文件的分块加密/解密。 ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> // 假设这些函数声明在rsa_utils.h中 int rsa_init(); void rsa_cleanup(); RSA* rsa_generate_keypair(int bits); int rsa_save_keys(RSA* rsa, const char* private_file, const char* public_file, const char* password); RSA* rsa_load_private_key(const char* filename, const char* password); RSA* rsa_load_public_key(const char* filename); int rsa_encrypt(RSA* pub_key, const unsigned char* plaintext, int pt_len, unsigned char* ciphertext); int rsa_decrypt(RSA* priv_key, const unsigned char* ciphertext, int ct_len, unsigned char* plaintext); void rsa_free_key(RSA* rsa); #define MAX_PASS_LEN 256 #define CHUNK_SIZE 245 // 2048-bit密钥最大加密块大小 (256-11 bytes) // 打印用法信息 void print_usage(const char* prog_name) { fprintf(stderr, "Usage:\n"); fprintf(stderr, " Generate keys: %s -g -b <bits> -p <private_file> -u <public_file> [-pass <password>]\n", prog_name); fprintf(stderr, " Encrypt file: %s -e -i <input> -o <output> -u <public_key>\n", prog_name); fprintf(stderr, " Decrypt file: %s -d -i <input> -o <output> -p <private_key> [-pass <password>]\n", prog_name); exit(EXIT_FAILURE); } // 生成密钥对 void generate_keys(int bits, const char* priv_file, const char* pub_file, const char* password) { RSA *rsa = rsa_generate_keypair(bits); if (!rsa) { fprintf(stderr, "Error generating RSA keypair\n"); exit(EXIT_FAILURE); } if (rsa_save_keys(rsa, priv_file, pub_file, password) != 0) { fprintf(stderr, "Error saving keys to files\n"); rsa_free_key(rsa); exit(EXIT_FAILURE); } rsa_free_key(rsa); printf("Keys generated successfully:\n Private: %s\n Public: %s\n", priv_file, pub_file); } // 加密文件 void encrypt_file(const char* input_file, const char* output_file, const char* pub_key_file) { RSA *pub_key = rsa_load_public_key(pub_key_file); if (!pub_key) { fprintf(stderr, "Error loading public key\n"); exit(EXIT_FAILURE); } int rsa_size = RSA_size(pub_key); if (rsa_size < CHUNK_SIZE + 11) { fprintf(stderr, "Key size too small for encryption chunks\n"); rsa_free_key(pub_key); exit(EXIT_FAILURE); } FILE *in = fopen(input_file, "rb"); FILE *out = fopen(output_file, "wb"); if (!in || !out) { perror("File open error"); rsa_free_key(pub_key); exit(EXIT_FAILURE); } unsigned char in_buf[CHUNK_SIZE]; unsigned char *out_buf = malloc(rsa_size); if (!out_buf) { perror("Memory allocation error"); fclose(in); fclose(out); rsa_free_key(pub_key); exit(EXIT_FAILURE); } size_t bytes_read; while ((bytes_read = fread(in_buf, 1, CHUNK_SIZE, in)) > 0) { int enc_len = rsa_encrypt(pub_key, in_buf, bytes_read, out_buf); if (enc_len <= 0) { fprintf(stderr, "Encryption failed\n"); break; } fwrite(out_buf, 1, enc_len, out); } free(out_buf); fclose(in); fclose(out); rsa_free_key(pub_key); printf("File encrypted successfully: %s -> %s\n", input_file, output_file); } // 解密文件 void decrypt_file(const char* input_file, const char* output_file, const char* priv_key_file, const char* password) { RSA *priv_key = rsa_load_private_key(priv_key_file, password); if (!priv_key) { fprintf(stderr, "Error loading private key (wrong password?)\n"); exit(EXIT_FAILURE); } int rsa_size = RSA_size(priv_key); FILE *in = fopen(input_file, "rb"); FILE *out = fopen(output_file, "wb"); if (!in || !out) { perror("File open error"); rsa_free_key(priv_key); exit(EXIT_FAILURE); } unsigned char *in_buf = malloc(rsa_size); unsigned char *out_buf = malloc(rsa_size); if (!in_buf || !out_buf) { perror("Memory allocation error"); fclose(in); fclose(out); free(in_buf); free(out_buf); rsa_free_key(priv_key); exit(EXIT_FAILURE); } size_t bytes_read; while ((bytes_read = fread(in_buf, 1, rsa_size, in)) > 0) { // 只处理完整块 if (bytes_read != (size_t)rsa_size) { fprintf(stderr, "Warning: Incomplete block at end of file\n"); break; } int dec_len = rsa_decrypt(priv_key, in_buf, bytes_read, out_buf); if (dec_len < 0) { fprintf(stderr, "Decryption failed\n"); break; } fwrite(out_buf, 1, dec_len, out); } free(in_buf); free(out_buf); fclose(in); fclose(out); rsa_free_key(priv_key); printf("File decrypted successfully: %s -> %s\n", input_file, output_file); } int main(int argc, char *argv[]) { if (rsa_init() != 0) { fprintf(stderr, "RSA initialization failed\n"); return EXIT_FAILURE; } int opt; char *priv_file = NULL, *pub_file = NULL; char *input_file = NULL, *output_file = NULL; char *password = NULL; int bits = 0; int gen_mode = 0, enc_mode = 0, dec_mode = 0; // 解析命令行参数 while ((opt = getopt(argc, argv, "gdei:o:p:u:b:pass:")) != -1) { switch (opt) { case 'g': gen_mode = 1; break; case 'e': enc_mode = 1; break; case 'd': dec_mode = 1; break; case 'i': input_file = optarg; break; case 'o': output_file = optarg; break; case 'p': priv_file = optarg; break; case 'u': pub_file = optarg; break; case 'b': bits = atoi(optarg); break; case 'pass': password = optarg; break; default: print_usage(argv[0]); } } // 验证参数组合 if (gen_mode + enc_mode + dec_mode != 1) { fprintf(stderr, "Must specify exactly one mode (-g/-e/-d)\n"); print_usage(argv[0]); } if (gen_mode) { if (!bits || !priv_file || !pub_file) { fprintf(stderr, "Missing required arguments for key generation\n"); print_usage(argv[0]); } generate_keys(bits, priv_file, pub_file, password); } else if (enc_mode) { if (!input_file || !output_file || !pub_file) { fprintf(stderr, "Missing required arguments for encryption\n"); print_usage(argv[0]); } encrypt_file(input_file, output_file, pub_file); } else if (dec_mode) { if (!input_file || !output_file || !priv_file) { fprintf(stderr, "Missing required arguments for decryption\n"); print_usage(argv[0]); } decrypt_file(input_file, output_file, priv_file, password); } rsa_cleanup(); return EXIT_SUCCESS; } ``` ### 代码解释 1. **程序结构**: - `main()`:解析命令行参数并调用相应功能 - `generate_keys()`:生成并保存RSA密钥对 - `encrypt_file()`:使用公钥加密文件 - `decrypt_file()`:使用私钥解密文件 - `print_usage()`:显示帮助信息 2. **密钥生成**: - 使用`rsa_generate_keypair()`生成密钥对 - 通过`rsa_save_keys()`保存到文件 - 支持密码保护私钥(可选) 3. **文件加密**: - 加载公钥文件 - 分块读取输入文件(每块245字节) - 每块使用`rsa_encrypt()`加密 - 将加密块写入输出文件 4. **文件解密**: - 加载私钥文件(可能需要密码) - 分块读取加密文件(每块256字节) - 每块使用`rsa_decrypt()`解密 - 将解密数据写入输出文件 5. **重要常量**: - `CHUNK_SIZE=245`:2048位RSA最大加密块大小 - `MAX_PASS_LEN=256`:密码最大长度 6. **错误处理**: - 检查所有关键操作返回值 - 处理文件I/O错误 - 内存分配检查 - 参数验证 ### 使用示例 1. 生成2048位密钥对: ```bash ./rsa_tool -g -b 2048 -p private.pem -u public.pem -pass mypassword ``` 2. 加密文件: ```bash ./rsa_tool -e -i plain.txt -o encrypted.bin -u public.pem ``` 3. 解密文件: ```bash ./rsa_tool -d -i encrypted.bin -o decrypted.txt -p private.pem -pass mypassword ``` ### 注意事项 1. 大文件处理: - 程序自动分块处理文件,支持任意大小文件 - 加密块大小固定(245字节输入→256字节输出) - 解密块大小固定(256字节输入→≤245字节输出) 2. 安全性考虑: - 密码通过命令行传递(生产环境应考虑更安全的方式) - 敏感数据在内存中应及时清除 - 私钥文件应设置适当权限 3. 性能优化: - 使用固定大小缓冲区减少内存分配 - 顺序读写优化I/O性能 - 错误发生时立即清理资源 4. 限制: - 加密文件大小会增加约4.5%(256/245≈1.045) - 支持流式加密(需要完整文件)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值