RSA非对称加密使用方式与原理浅析

一、非对称加密的简述:

非对称加密,顾名思义加解密用的不是同一个密钥(如此我们也能很通俗的对照理解对称加密,显然它后者是加解密为同一个密钥),那么非对称加密就得用俩个密钥,一个叫公钥,任何人都能够去获取,一个叫私钥,不会四处乱传输,保留在一个认定安全的区域,公钥和私钥任意一方加密,只能由另一方解密,自己也是无法解密的,目前全球的数据安全测试中密钥大于1024的密钥还没有人宣称能够破解,因此是安全级别很高的加密算法,关于非对称加密的历史由来可以百度,有很详细的介绍,是美国很厉害的计算机大师和数学家设计出来的。
下面我们来简单的了解下非对称加密的数学方面是怎么推导的(参考阮一峰大神,加上自己的理解):

1、互质关系

如果俩个正整数,除了1以外没有其他公因子,我们称这俩数叫互质关系。7和12,没有公因子,则为互质关系,从这里能看出,不是质数也能和其他书组成互质关系。
因此我们能推出来下列一些结论:

1、任何俩个质数都能组成互质关系,例如5、11;
2、一个数是质数,另一个数只要不是其倍数,两者就是互质关系,例如:7、12
3、如果两个数中较大的数是质数,他们就是互质关系,其实这一条和2是差不多的意思,大家发现没有,较大数是质数,那铁定不是较小数的倍数;
4、1和任意数都是互质关系,这是质数的定义和1这个数的特性决定的
5、p是大于1的整数,则p和p-1构成互质关系,比如15和16
6、p是大于1的奇数,则p和p-2构成互质关系。例如27和25

2、欧拉函数

看个问题:

任意给定正整数n,请问在小于等于n的正整数之中,有多少个与n组成互质关系?

计算这个结果的公式就是欧拉函数,用φ(n)表示,例如给定正整数6,在小于等于6的正整数中,是1、5与6组成互质关系,因此φ(n) = 2;欧拉函数用来证明欧拉定理:**如果两个正整数a和n互质,a的φ(n)次方被n除的余数为1,公式:φ(n)=(p-1)(q-1) **

或者说,a的φ(n)次方减去1,可以被n整除。比如,3和7互质,而7的欧拉函数φ(7)等于6,所以3的6次方(729)减去1,可以被7整除(728/7=104)。我们不做证明只看结论,反正现代数学用了这么多年,一旦出问题是全方位的数据天塌地陷,不在乎我们这点程序bug。

这个欧拉定理可以大大简化某些运算,比如9和11互质,11的欧拉函数φ(11)等于10,9的10次方(3486784401)个位数肯定是1,我们可以套用这个公式,比如9的任意次方,个位数是多少我们就能笔算出来(只要再找一个数和9组成互质关系,来套用欧拉定理)

3、模反元素

如果两个正整数a和n互质,那么一定可以找到整数b,使得ab-1被n整除,或者说ab被n除的余数是1,这事b就叫a的模反元素

比如:5和7互质,那么5的模反元素就是3,因为:5*3 / 7 =2 …1,很显然一个数的模反元素是很多的,a的φ(n)-1 次方,就是a的模反元素。

二、RSA非对称加密原理

有了上述的数论基础,我们就能阐述RSA算法的基本概念了:

1、RSA加密有哪些元素:

  1. 明文
  2. 公钥
  3. 私钥
  4. 加密算法
  5. 密文

2、生成加密要素

下面来看公钥和私钥的由来,要使用上述的欧拉定理和模反元素;

2.1:随机选两个质数p和q,31和53(真实加密时,选择越大的质数越难以暴力破解,看下边流程很容易明白)
2.2:计算p和q的乘积n:31乘以53 = 1643
2.3:n就是密钥的长度,1643用二进制表达:11001101011,一共11位,因此这个密钥就是11位,实际应用中,RSA密钥一般是1024位,2048位和4096位。
2.4:计算n欧拉函数φ(n):根据公式φ(n) = (p-1)(q-1),φ(n) = 30*52 = 1560
2.5:随机选择一个整数e,条件是1< e < φ(n),且e与φ(n) 互质,随机选择e = 17
2.6:计算e对于φ(n)的模反元素d,"模反元素"就是指有一个整数d,可以使得ed被φ(n)除的余数为1,因此可以用这个公式表达:ed - 1 = kφ(n),已知e = 17,φ(n) = 1560;接下来就是求这个二元一次方程,可以计算出d的值

到这里,所有要用的元素都齐备,公钥(n,e);私钥(n,d)

梳理一下:用俩个质数 p、q 计算出 n,用n计算出φ(n),随机选择一个e,计算出d,则公钥私钥产生;而n在公钥私钥中都会出现,因此至关重要,n是p和q相乘得出来,拿到n能否反推出来p和q就是RSA算法是否安全的核心(因为公私钥就三个数值,n、e、d,暴露公钥n、e能否推算d就是安全性高与低的核心),显然1560反推(p-1)(q-1)这俩质数不太难,但是特别大的整数,反推就非常困难,维基百科是这么说的:

    “对极大整数做因数分解的难度决定了RSA算法的可靠性。换言之,对一极大整数做因数分解愈困难,RSA算法愈可靠。
    假如有人找到一种快速因数分解的算法,那么RSA的可靠性就会极度下降。但找到这样的算法的可能性是非常小的。今天只有短的RSA密钥才可能被暴力破解。到2008年为止,世界上还没有任何可靠的攻击RSA算法的方式。
    只要密钥长度足够长,用RSA加密的信息实际上是不能被解破的。”

目前已知被暴力破解的最大整数(232个十进制位,768个二进制位),再大的至少没人宣称破解出来,因此上面说,选择越大的质数越难以暴力破解,

3、RSA算法有一个很重要的特性:

明文M必须是整数,(字符串可以取ascll码或者Unicode码)且m必须小于n(密钥的长度)
而现行的RSA密钥一般是三类:1024位、2048位、4096位

填充方式输入输出
RSA_PKCS1_PADDING必须比RSA钥模长至少短11位以上与RSA钥模长一样长
RSA_PKCS1_OAEP_PADDING必须比RSA钥模长至少短41位以上与RSA钥模长一样长
RSA_NO_PADDING可以和RSA钥模长一样长与RSA钥模长一样长

注:这里我最初开发时有个疑惑,明明RSA加密是公钥+私钥,为什么很多资料里会冒出个密钥,到底公钥和私钥哪个才算是密钥,或者说在RSA算法中,密钥是个啥玩意?
其实是这样的:RSA加解密在使用时(代码中),是只有公钥和私钥的,你任意选择一方来加密,另一方做解密,那你在加密(或者解密)的时候,是不是只会用其中之一来做?我猜测是处于习惯,就把这时候的公钥(或者私钥)叫做密钥,其实密钥是通用的俗称,不是真正的学名,真正代码开发的时候,在RSA中出现的学名只有公钥、私钥

三、RSA加解密代码:

大体上原理了解的差不多,上代码

1、这是私钥加密

//私钥加密
std::string encryptByPrikeyString(const uint8_t* message,int messageLen, const char* m_keyData) 
{
  std::string strPublicKey(m_keyData);
  int k = 0;
  int nPublicKeyLen = strPublicKey.length();
  ALOGD("nPublicKeyLen = %d,value = %s", nPublicKeyLen,strPublicKey.data());
  #if 0
  for( int i = 64; i < nPublicKeyLen+k; i+=64 )
  {
    strPublicKey.insert(i, "\n");
    ++k;
    ++i;
  }

  strPublicKey.insert(0, "-----BEGIN RSA PRIVATE KEY-----\n");
  strPublicKey.append("\n-----END RSA PRIVATE KEY-----\n");
#endif

  BIO* in = BIO_new_mem_buf((void*)strPublicKey.c_str(), strlen(strPublicKey.c_str()));
  if (in == NULL) 
  {
    ERR_load_crypto_strings();
    char errBuf[512];
    ERR_error_string_n(ERR_get_error(), errBuf, sizeof(errBuf));
    ALOGE("BIO_new_mem_buf failed [%s]", errBuf);
    return "";
  }

  RSA* rsa = PEM_read_bio_RSAPrivateKey(in, NULL, NULL, NULL);
  //RSA* rsa = PEM_read_bio_RSAPublicKey(in, NULL, NULL, NULL);

  if (rsa == NULL) 
  {
    ERR_load_crypto_strings();
    char errBuf[512];
    ERR_error_string_n(ERR_get_error(), errBuf, sizeof(errBuf));
    ALOGE("PEM_read_bio_RSAPrivateKeyrsa [%s]", errBuf);

    BIO_free_all(in);
    RSA_free(rsa);
    //清除管理CRYPTO_EX_DATA的全局hash表中的数据,避免内存泄漏
    CRYPTO_cleanup_all_ex_data();
    return "";
  }

  int size = RSA_size(rsa);
  std::vector<char> encrypt_data;
  encrypt_data.resize(size);
  int ret = RSA_private_encrypt(messageLen, (unsigned char*)message, (unsigned char*)encrypt_data.data(), rsa, RSA_PKCS1_PADDING);

  if (ret < 0) 
  {
    ALOGE("RSA_private_encrypt failed");
    BIO_free_all(in);
    RSA_free(rsa);
    //清除管理CRYPTO_EX_DATA的全局hash表中的数据,避免内存泄漏
    CRYPTO_cleanup_all_ex_data();
    return "";
  }

  BIO_free_all(in);
  RSA_free(rsa);
  //清除管理CRYPTO_EX_DATA的全局hash表中的数据,避免内存泄漏
  CRYPTO_cleanup_all_ex_data();
  std::string encryptStr(encrypt_data.begin(), encrypt_data.end());//密文数据

  ALOGD("encryptStr:%s", encryptStr.c_str());
  //转换为base64数据
  //std::string result = base64_encode(encryptStr);
  //ALOGD("result:%s", result.c_str());

  return encryptStr;
}

2、这是公钥解密:

//公钥解密
std::string decryptByPubkeyString(const uint8_t* message,int messageLen, const char* pubkey) 
{
  std::string strPublicKey(pubkey);
  int k = 0;
  int nPublicKeyLen = strPublicKey.length();
  ALOGD("decryptByPubkeyString nPublicKeyLen = %d,value = %s", nPublicKeyLen,strPublicKey.data());
  #if 0
  for( int i = 64; i < nPublicKeyLen+k; i+=64 )
  {
    strPublicKey.insert(i, "\n");
    ++k;
    ++i;
  }

  strPublicKey.insert(0, "-----BEGIN RSA PRIVATE KEY-----\n");
  strPublicKey.append("\n-----END RSA PRIVATE KEY-----\n");
#endif

  BIO* in = BIO_new_mem_buf((void*)strPublicKey.c_str(), strlen(strPublicKey.c_str()));
  if (in == NULL) 
  {
    ERR_load_crypto_strings();
    char errBuf[512];
    ERR_error_string_n(ERR_get_error(), errBuf, sizeof(errBuf));
    ALOGE("BIO_new_mem_buf failed [%s]", errBuf);
    return "";
  }

  RSA* rsa = PEM_read_bio_RSAPublicKey(in, NULL, NULL, NULL);

  if (rsa == NULL) 
  {
    ERR_load_crypto_strings();
    char errBuf[512];
    ERR_error_string_n(ERR_get_error(), errBuf, sizeof(errBuf));
    ALOGE("PEM_read_bio_RSAPrivateKeyrsa [%s]", errBuf);

    BIO_free_all(in);
    RSA_free(rsa);
    //清除管理CRYPTO_EX_DATA的全局hash表中的数据,避免内存泄漏
    CRYPTO_cleanup_all_ex_data();
    return "";
  }

  int size = RSA_size(rsa);
  std::vector<char> encrypt_data;
  encrypt_data.resize(size);
  int ret = RSA_public_decrypt(messageLen, (unsigned char*)message, (unsigned char*)encrypt_data.data(), rsa, RSA_PKCS1_PADDING);

  if (ret < 0) 
  {
    ALOGE("RSA_public_decrypt failed");
    BIO_free_all(in);
    RSA_free(rsa);
    //清除管理CRYPTO_EX_DATA的全局hash表中的数据,避免内存泄漏
    CRYPTO_cleanup_all_ex_data();
    return "";
  }

  BIO_free_all(in);
  RSA_free(rsa);
  //清除管理CRYPTO_EX_DATA的全局hash表中的数据,避免内存泄漏
  CRYPTO_cleanup_all_ex_data();
  std::string decryptStr(encrypt_data.begin(), encrypt_data.end());//明文数据

  ALOGD("decryptStr:%s", decryptStr.c_str());
  //转换为base64数据
  //std::string result = base64_encode(decryptStr);
  //ALOGD("result:%s", result.c_str());

  return decryptStr;
}

3、拆分明文:

假如密钥长度是1024位,也就是128字节,如果明文长度大于它就得拆分成一段一段数据才能加解密
这是加密的拆分

        struct timeval tv; 
		gettimeofday(&tv, NULL);
		int64_t start = tv.tv_sec * 1000 + tv.tv_usec / 1000;
		ALOGE("11111aaaaa:RSA Start,data.len = %d",data.len);


		std::string result;
		result.clear();
		for(int i = 0 ; i < msg_info.msg_len/117; i++)
    	{
    		uint8_t * msgsss = (uint8_t *)malloc(117);
			if (!msgsss)
			{
				break;
			}
			memset(msgsss,0,117);
			memcpy(msgsss, &data.msg[i*117], i*117 + 117);
            result = result + encryptByPrikeyString(msgsss,117, m_prikeyData.c_str());
			if (!msgsss)
			{
				free(msgsss);
				msgsss = NULL;
			}
    	}
   		if( msg_info.msg_len%117 != 0)
    	{
	        int tem1 = msg_info.msg_len/117*117;
	        int tem2 = msg_info.msg_len - tem1;
    		uint8_t * msgccc = (uint8_t *)malloc(tem2);
			if (!msgccc)
			{
				break;
			}
			memset(msgccc,0,tem2);
			memcpy(msgccc, &msg_info.msg[tem1], tem2);
	        result = result + encryptByPrikeyString(msgccc,tem2, m_prikeyData.c_str());
			if (!msgccc)
			{
				free(msgccc);
				msgccc = NULL;
			}
    	}
		ALOGE("11111bbb:RSA_encryptData,RSAEnDataLen = %d",result.length());
		struct timeval tv1; 
		gettimeofday(&tv1, NULL);
		int64_t end = tv1.tv_sec * 1000 + tv1.tv_usec / 1000;
		ALOGE("11111ddddd:RSA_encryptDataTime: %d",end - start);

4、拆分密文:

std::string deResult;
        std::string input;
		deResult.clear();
		for(int i = 0 ; i< result.length()/128; i++)
    	{
	        input.clear();
	        input.assign(result.begin() + i*128, result.begin() + i*128 + 128);
			
    		uint8_t * dedata = (uint8_t *)malloc(128);
			if (!dedata)
			{
				break;
			}
			memset(dedata,0,128);
			memcpy(dedata, deResult.c_str()+i*128, 128);
        	deResult = deResult + decryptByPubkeyString(dedata,128, m_pubkeyData.c_str());
			if (!dedata)
			{
				free(dedata);
				dedata = NULL;
			}
    	}
    	if(result.length()%128 != 0)
    	{
	        int tem1 = result.length()/128 * 128;
	        int tem2 = result.length() - tem1;
	        input.clear();
	        input.assign(result.begin()+ tem1, result.end());
    		uint8_t * dedata = (uint8_t *)malloc(tem2);
			if (!dedata)
			{
				break;
			}
			memset(dedata,0,tem2);
			memcpy(dedata, deResult.c_str()+tem1, tem2);
	        deResult = deResult + decryptByPubkeyString(dedata,tem2, m_pubkeyData.c_str());
			if (!dedata)
			{
				free(dedata);
				dedata = NULL;
			}
    }
		ALOGE("11111ccc:RSA_decryptData");
  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在 Nuxt.js 中使用 RSA 非对称加密可以通过引入 Node.js 的 crypto 模块来实现。具体的实现步骤如下: 1. 在 Nuxt.js 项目中安装 crypto 模块,可以使用 npm 或 yarn 命令进行安装: ``` npm install crypto ``` 或 ``` yarn add crypto ``` 2. 在需要使用 RSA 非对称加密的地方引入 crypto 模块: ```js const crypto = require('crypto'); ``` 3. 生成 RSA 密钥对,可以使用 crypto 模块中的 `generateKeyPairSync()` 方法: ```js const { publicKey, privateKey } = crypto.generateKeyPairSync('rsa', { modulusLength: 2048, publicKeyEncoding: { type: 'spki', format: 'pem' }, privateKeyEncoding: { type: 'pkcs8', format: 'pem' } }); ``` 此时,我们就可以得到一个包含公钥和私钥的密钥对了。 4. 使用公钥进行加密,可以使用 crypto 模块中的 `publicEncrypt()` 方法: ```js const data = 'hello world'; const encrypted = crypto.publicEncrypt(publicKey, Buffer.from(data)).toString('base64'); console.log(encrypted); // 输出加密后的数据 ``` 5. 使用私钥进行解密,可以使用 crypto 模块中的 `privateDecrypt()` 方法: ```js const decrypted = crypto.privateDecrypt(privateKey, Buffer.from(encrypted, 'base64')).toString(); console.log(decrypted); // 输出解密后的数据 ``` 以上就是在 Nuxt.js 中使用 RSA 非对称加密的基本步骤。需要注意的是,加密和解密的过程都需要保护密钥的安全,避免密钥泄露导致加密数据的安全性受到威胁。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值