RSA加解密

1.基本介绍    

       提到加密技术,就不得不提起加密技术的分类,加密技术通常分为两大类,对称式加密和非对称式加密。

       对称式加密指的是加密和解密使用同样规则(简称"密钥")的一种加密方法,这种加密方法由于要使用相同的密匙,传递密匙的安全性成为一个问题。所以就出现了非对称式加密:

       非对称式加密就是加密和解密所使用的不是同一个密钥,通常有两个密钥,称为“公钥”和“私钥”,它们两个必需配对使用,否则不能打开加密文件。这里的“公钥”是指可以对外公布的,“私钥”则不能,只能由持有人一个人知道。它的优越性就在这里,因为对称式的加密方法如果是在网络上传输加密文件就很难不把密钥告诉对方,不管用什么方法都有可能被别窃听到。而非对称式的加密方法有两个密钥,且其中的“公钥”是可以公开的,也就不怕别人知道,收件人解密时只要用自己的私钥即可以,这样就很好地避免了密钥的传输安全性问题。

       我们这里所用的RSA加密就是一种非对称式加密,RSA的实际没有什么含义,命名为RSA是因为这个算法是由三位数学家Rivest、Shamir 和 Adleman 所设计的,所以取三个人名字的首字母来命名。


2.RSA密钥产生过程:

1. 随机选择两个不相等的质数p和q;
2. 计算p和q的乘积n,n = pq;
3. 计算n的欧拉函数φ(n);
4. 随机选择一个整数e,条件是1 < e < φ(n),且e与φ(n) 互质;
5. 计算e对于φ(n)的模反元素d,使得de≡1 mod φ(n),即:(de)modφ(n) = 1;
6. 产生公钥(e, n),私钥(d, n)。

备注:

互质数:

公约数只有1的两个数,叫做互质数。
1.两个质数一定是互质数;
2.一个数是质数,另一个数只要不是前者的倍数,两者就构成互质关系,比如5和12
3.如果两个数之中,较大的那个数是质数,则两者构成互质关系,比如97和88
4.1和任意一个自然数是都是互质关系,比如1和100
5.相邻的两个自然数是互质数,如 15与 16
6.相邻的两个奇数是互质数 ,如 49与 51

欧拉函数:

欧拉函数是小于x的正整数中与x互质的数的数目。

欧拉函数是积性函数,若m,n互质,则:φ(mn)=φ(m)φ(n);

若m为质数,则有φ(m)=m-1;同理若n为质数,则有φ(n)=n-1;

由此可得,当m,n都是质数,则有φ(mn)=(m-1)(n-1)。

 

举个简单的例子:

1. 选择p = 3, q = 11;
2. n = pq = 33;
3. φ(n) = (p - 1)(q - 1) = 20;
4. 选择e = 3, 此时e与φ(n) 互质;
5. (de)modφ(n) =(d*3)mod20=1,求出d=7;
6. 公钥(3,33),私钥(7,33);
7. 例如对明文数字“5”加密,根据加密公式,密文=(5^3)mod(33) = 125 % 33 = 26;
8. 把上述密文解密,明文=26^7 % 33 = 8031810176 % 33 = 5。


3.代码分析

这个项目是在vs2017编译环境下完成的,包含一个头文件RSA.h,两个源文件RSA.cpp和test.cpp。

RSA.h头文件:结构体的定义,和功能函数的声明。

RSA.cpp源文件:功能函数的定义和实现。

test.cpp源文件:主函数及测试函数。

1.RSA.h

#pragma once
#include <string>
#include <vector>
struct Key
{
	long pkey;
	//私钥(ekey, pkey): (e,n)
	long ekey;
	//公钥(dkey, pkey): (d, n)
	long dkey;
};

class RSA
{
public:
	RSA();

	Key getKey()
	{
		return _key;
	}

	void ecrept(const char* plain_file_in, const char* ecrept_file_out,
		long ekey, long pkey);//加密
	void decrept(const char* ecrept_file_in, const char* plain_file_out,
		long dkey, long pkey);//解密

	std::vector<long> ecrept(std::string& str_in, long ekey, long pkey);//字符串加密
	std::string decrept(std::vector<long>& ecrept_str, long dkey, long pkey);//解密为字符串

	void printInfo(std::vector<long>& ecrept_str);//打印信息
private:
	long ecrept(long msg, long key, long pkey);//加密单个信息
	long produce_prime();//产生素数
	bool is_prime(long prime);//是否为素数
	void produce_keys();
	long produce_pkey(long prime1, long prime2);//计算pq乘积n
	long produce_orla(long prime1, long prime2);//计算欧拉函数φ(n)
	long produce_ekey(long orla);//产生e
	long produce_gcd(long ekey, long orla);//产生最大公约数
	long produce_dkey(long ekey, long orla);//产生d

private:
	Key _key;
};

        这里我们首先定义了结构体Key,里面有组成的私钥和公钥的三个数据,n,e和d。再定义一个RSA类,定义一个结构体Key类型的成员变量_key,用来获取Key中的数据。

        我们主要是两种加密方式,一种是输入一段字符串,对字符串进行加密,另一种是选择一个文件,对文件进行加密。为了验证,我们又将加密后的信息进行解密。我们在公有访问中只声明这两种方式的加密解密接口就行了,其余在加密解密过程中的函数接口都是私有访问。

2.RSA.cpp

#include"RSA.h"
#include<time.h>
#include<math.h>
#include<iostream>
#include<fstream>

RSA::RSA()
{
	produce_keys();
}

void RSA::ecrept(const char* plain_file_in, const char* ecrept_file_out,
    long ekey, long pkey)//加密
{
	std::ifstream fin(plain_file_in);
	std::ofstream fout(ecrept_file_out, std::ofstream::app);
	if (!fin.is_open())
	{
		std::cout << "open file failed" << std::endl;
		return;
	}
	const int NUM = 256;
	char buffer[NUM];
	long buffer_out[NUM];
	int curNum;
	while (!fin.eof())
	{
		fin.read(buffer, NUM);
		curNum = fin.gcount();
		for (int i = 0; i < curNum; ++i)
		{
			buffer_out[i] = ecrept(buffer[i], ekey, pkey);
		}
		fout.write((char *)buffer_out, curNum * sizeof(long));
	}
	fin.close();
	fout.close();
}
void RSA::decrept(const char* ecrept_file_in, const char* plain_file_out,
	long dkey, long pkey)//解密
{
	std::ifstream fin(ecrept_file_in);
	std::ofstream fout(plain_file_out, std::ofstream::app);
	if (!fin.is_open())
	{
		std::cout << "open file failed" << std::endl;
		return;
	}
	const int NUM = 256;
	long buffer[NUM];
	char buffer_out[NUM];
	int curNum;
	while (!fin.eof())
	{
		fin.read((char *)buffer, NUM * sizeof(long));
		curNum = fin.gcount() / sizeof(long);
		for (int i = 0; i < curNum; ++i)
		{
			buffer_out[i] = ecrept(buffer[i], dkey, pkey);
		}
		fout.write(buffer_out, curNum);
	}
	fin.close();
	fout.close();
}

std::vector<long> RSA::ecrept(std::string& str_in, long ekey, long pkey)//字符串加密
{
	std::vector<long> vecout;
	size_t sz = str_in.size();
	for (const auto& e : str_in)
	{
		vecout.push_back(ecrept(e, ekey, pkey));
	}
	return vecout;
}
std::string RSA::decrept(std::vector<long>& ecrept_str, long dkey, long pkey)//解密为字符串
{
	std::string strout;
	for (const auto& e : ecrept_str)
	{
		strout.push_back(ecrept(e, dkey, pkey));
	}
	return strout;
}

void RSA::printInfo(std::vector<long>& ecrept_str)//打印信息
{
	for (const auto & e : ecrept_str)
	{
		std::cout << e << ' ';
	}
	std::cout << std::endl;
}

//加密解密算法
//模幂运算(a^b)%c   
long RSA::ecrept(long msg, long key, long pkey)
{
	long msg_out = 1;
	//A0:a^(2^0) % c = a^1 = a%c
	long a = msg;
	long b = key;
	int c = pkey;
	while (b)
	{
		if (b & 1)
			//msg_out = (A0 * A1 ...Ai ... An) % c
			msg_out = (msg_out * a) % c;
		b >>= 1;
		//Ai = (A(i - 1) * A(i - 1)) % c
		a = (a * a) % c;
	}
	return msg_out;
}

long RSA::produce_prime()
{
	srand(time(nullptr));
	long prime = 0;
	while (1)
	{
		prime = rand() % 50 + 2;
		if (is_prime(prime))
			break;
	}
	return prime;
}

bool RSA::is_prime(long prime)
{
	if (prime < 2)
		return false;
	for (int i = 2; i < sqrt(prime); ++i)
	{
		if (prime % i == 0)
			return false;
	}
	return true;
}

void RSA::produce_keys()
{
	long prime1 = produce_prime();
	long prime2 = produce_prime();
	while (prime1 == prime2)
		prime2 = produce_prime();
	_key.pkey = produce_pkey(prime1, prime2);
	long orla = produce_orla(prime1, prime2);
	_key.ekey = produce_ekey(orla);
	_key.dkey = produce_dkey(_key.ekey, orla);
}

long RSA::produce_pkey(long prime1, long prime2)
{
	return prime1 * prime2;
}

long RSA::produce_orla(long prime1, long prime2)
{
	return (prime1 - 1) * (prime2 - 1);
}

//随机选择一个整数e,条件是1 < e < φ(n),且e与φ(n) 互质
long RSA::produce_ekey(long orla)
{
	long ekey;
	srand(time(nullptr));
	while (1)
	{
		ekey = rand() % orla;
		if (ekey > 1 && produce_gcd(ekey, orla) == 1)
			break;
	}
	return ekey;
}

//求最大公约数
long RSA::produce_gcd(long ekey, long orla)
{
	//gcd(a, b)------gcd(b ,a%b)
	long ret;
	while (ret = ekey % orla)
	{
		ekey = orla;
		orla = ret;
	}
	return orla;
}

long RSA::produce_dkey(long ekey, long orla)
{
	//(dkey * ekey) % orla == 1

	long dkey = orla / ekey;//dkey最小为orla / ekey,才能保证dkey * ekey与orla相等
							//只有dkey * ekey比orla大才能有余数为1
	while (1)
	{
		if ((dkey * ekey) % orla == 1)
			break;
		++dkey;
	}
	return dkey;
}

        构造函数RSA(),用来获取私有成员变量_key;

        想要实现对字符串或文件的加密解密,首先要得到加密解密的算法(也就是完成long RSA::ecrept(long msg, long key, long pkey)的定义),这里RSA算法加密解密方法相同,只是用的密钥不同,所以只需要传入不同的参数即可实现;

        这里最重要的一点就是加密解密算法,加密算法大概可以表示为:(要加密/解密的信息)^  key % pkey,其中key是加密ekey或解密dkey。这里我们就要用到模幂运算了,下面是我找的模幂运算的过程:

   

a^b%c = (A0 * A1 * ... * Ai  *... * An) % c  = (A0 * A0^2 * ... * (A0^2)^i*... * (A0^2)^n) % c

A0 = a^2^0 % c = a % c

       最终的模幂运算也就是连乘b的二进制位为1对应的位,每次查看b的最后一位(通过&1查看最后一位,右移一位实现变化查看二进制位的每一位),如果为1,则连乘运算,否则进行下一位Ai+1的运算(也就是从A0到An的变化,也就是代码里的a = (a * a) % c)。

如此往复,当b的所有二进制位为1的部分全部运算过,也就是此时的b=0,表示模幂运算完成,也就完成了加密。

        当然,若要解密,则需要得到私钥(ekey, pkey),加密则需要得到公钥(dkey, pkey),那么就必须获取到pkey,ekey和dkey,在long RSA::produce_prime()中实现获取功能。

        获取key首先要产生两个素数,调用produce_prime()来产生素数,然后通过is_prime(long prime)来判断是否为素数,从而获取到两个素数。因为素数判断时使用for循环,所以这里产生的素数只是在2到51之间(不能是大数,否则效率太低,后面会对此处进行优化)。素数得到了,则pkey和orla就很容易得到了,就不过多解释了。只对ekey和dkey的获取稍微解释一下:

        要得到ekey,上面第2点RSA加密过程中已经说过了ekey的条件,即随机选择一个整数e,条件是1 < e < φ(n),且e与φ(n) 互质。(这里的e就是我所说的ekey,同d也是dkey)那就先随机产生一个数,满足的条件就是1 < e < φ(n),且e与φ(n) 互质(互质也就是最大公约数是1),判断这两个条件,满足则得到ekey,不满足一直产生,知道满足即可。

        dkey满足的条件在RSA加密过程中也说明了,即 (de)modφ(n) = 1;也就是使得(dkey * ekey) % orla == 1,满足即可。

3.test.cpp

#include"RSA.h"
#include<iostream>

void teststring()
{
	RSA rsa;
	Key key = rsa.getKey();
	std::string strin;
	while(1)
	{
		std::cout << "输入要加密的信息:" << std::endl;
		std::cin >> strin;
		std::vector<long>strecrept = rsa.ecrept(strin, key.ekey, key.pkey);
		std::string strout = rsa.decrept(strecrept, key.dkey, key.pkey);
		std::cout << "加密后的信息:" << std::endl;
		rsa.printInfo(strecrept);
		std::cout << "解密后的信息:" << std::endl;
		std::cout << strout << std::endl;
		std::cout << std::endl;

	}
}

void testFile()
{
	RSA rsa;
	Key key = rsa.getKey();
	std::string filename;
	std::cout << "输入文件名:" << std::endl;
	std::cin >> filename;
	rsa.ecrept(filename.c_str(), (filename + ".ecrept.txt").c_str(), key.ekey, key.pkey);
	rsa.decrept((filename + ".ecrept.txt").c_str(), (filename + ".decrept.txt").c_str(), key.dkey, key.pkey);
	//IO标准库使用C风格字符串而不是C++ string类型字符串作为文件名,所以需要用c_str获取C风格字符串
	//c风格的字符串是可以隐式转换成string的,但string无法隐式转换成c风格的字符串
	//为了把string转换成ifstream可以接受的类型,必须把string转换为c风格的字符串。
}

int main()
{
	//teststring();
	testFile();
	system("pause");
	return 0;
}

        这是一个测试程序,参考注释,这里不进行讲解

        上面用到了欧几里得算法,我自己证明了一下这个算法,有兴趣可以看看:               https://blog.csdn.net/Watery________1314/article/details/99670583

        完成上面的程序,RSA加密已经可以运行了,但是还需要进行优化,之前我们素数产生范围只在2-51之间,并不能产生大数,安全性不高,只有数越大才越难进行因式分解,所以我将对此进行一些优化,让产生的素数为大数,见另一篇博客:https://blog.csdn.net/Watery________1314/article/details/96970308

 

 

 

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在C#中,可以使用BouncyCastle库来实现RSA签名。下面是将Python代码转换为C#代码的示例: ``` using System; using System.IO; using System.Security.Cryptography; using System.Text; using Org.BouncyCastle.Crypto; using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.Security; public static string Sign(string content) { var rsa = RSA.Create(); var privateKeyText = File.ReadAllText("private_key.pem"); var privateKeyBytes = Convert.FromBase64String(privateKeyText); var keyPair = (AsymmetricCipherKeyPair)new PemReader(new StringReader(privateKeyText)).ReadObject(); var privateKey = (RsaPrivateCrtKeyParameters)keyPair.Private; rsa.ImportParameters(new RSAParameters { Modulus = privateKey.Modulus.ToByteArrayUnsigned(), Exponent = privateKey.PublicExponent.ToByteArrayUnsigned(), D = privateKey.Exponent.ToByteArrayUnsigned(), P = privateKey.P.ToByteArrayUnsigned(), Q = privateKey.Q.ToByteArrayUnsigned(), DP = privateKey.DP.ToByteArrayUnsigned(), DQ = privateKey.DQ.ToByteArrayUnsigned(), InverseQ = privateKey.QInv.ToByteArrayUnsigned() }); var sha = SHA1.Create(); var hash = sha.ComputeHash(Encoding.UTF8.GetBytes(content)); var signature = rsa.SignHash(hash, HashAlgorithmName.SHA1, RSASignaturePadding.Pkcs1); return Convert.ToBase64String(signature); } ``` 注意,由于Python中的签名使用的是SHA-1算法,因此在C#中使用了相同的算法。如果要使用其他算法,则需要相应地更改。此外,还需要添加BouncyCastle库的引用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值