一、非对称加密的简述:
非对称加密,顾名思义加解密用的不是同一个密钥(如此我们也能很通俗的对照理解对称加密,显然它后者是加解密为同一个密钥),那么非对称加密就得用俩个密钥,一个叫公钥,任何人都能够去获取,一个叫私钥,不会四处乱传输,保留在一个认定安全的区域,公钥和私钥任意一方加密,只能由另一方解密,自己也是无法解密的,目前全球的数据安全测试中密钥大于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加密有哪些元素:
- 明文
- 公钥
- 私钥
- 加密算法
- 密文
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");