RSA
RSA加密利用了单向函数正向求解很简单,反向求解很复杂的特性
基本概念
一、 什么是“素数”?
素数是这样的整数,它除了能表示为它自己和1的乘积以外,不能表示为任何其它两个整数的乘积。例如,15=3*5,所以15不是素数;又如,12=6*2=4*3,所以12也不是素数。另一方面,13除了等于13*1以外,不能表示为其它任何两个整数的乘积,所以13是一个素数。素数也称为“质数”。
二、什么是“互质数”(或“互素数”)?
小学数学教材对互质数是这样定义的:“公约数只有1的两个数,叫做互质数。”这里所说的“两个数”是指自然数。
判别方法主要有以下几种(不限于此):
(1)两个质数一定是互质数。例如,2与7、13与19。
(2)一个质数如果不能整除另一个合数,这两个数为互质数。例如,3与10、5与 26。
(3)1不是质数也不是合数,它和任何一个自然数在一起都是互质数。如1和9908。
(4)相邻的两个自然数是互质数。如 15与 16。
(5)相邻的两个奇数是互质数。如 49与 51。
(6)大数是质数的两个数是互质数。如97与88。
(7)小数是质数,大数不是小数的倍数的两个数是互质数。如 7和 16。
(8)两个数都是合数(二数差又较大),小数所有的质因数,都不是大数的约数,这两个数是互质数。如357与715,357=3×7×17,而3、7和17都不是715的约数,这两个数为互质数。等等。
三、什么是模指数运算?
指数运算谁都懂,不必说了,先说说模运算。模运算是整数运算,有一个整数m,以n为模做模运算,即m mod n。怎样做呢?让m去被n整除,只取所得的余数作为结果,就叫做模运算。例如,10 mod 3=1;26 mod 6=2;28 mod 2 =0等等。
模指数运算就是先做指数运算,取其结果再做模运算。如(5^3) mod 7 = (125 mod 7) = 6。
RSA加解密
RSA属于公钥加密算法中的一个重要应用。RSA加密算法由五个部分组成:
- 原文(Message):需要加密的信息,可以是数字、文字、视频、音频等,用 M M M 表示。
- 密文(Ciphertext):加密后得到的信息,用 C C C表示。
- 公钥(Public Key)和私钥(Secret Key),用 P K PK PK 和 S K SK SK表示。
- 加密算法(Encryption):若 E ( x ) E(x) E(x) 为加密算法,加密过程可以理解为 C = E ( M ) C = E(M) C=E(M)根据原文和加密算法得到密文。
- 解密算法(Decryption):若 D ( x ) D(x) D(x)为解密算法,解密过程可以理解为 M = D ( C ) M = D(C) M=D(C)据密文和解密算法得到原文。
假设Alice和Bob要在网上进行加密通信,他们要怎么应用RSA来加密和解密信息呢?步骤如下:
- 随机选择两个不相同的素数 p , q p,q p,q 。
- 将 p , q p,q p,q相乘,记为 n = p × q n = p\times q n=p×q 。
- 计算 n n n的欧拉函数 φ ( n ) \varphi(n) φ(n) ,欧拉函数证明,当 p , q p,q p,q为不相同的素数时, φ ( n ) = ( p − 1 ) ( q − 1 ) \varphi(n)=(p-1)(q-1) φ(n)=(p−1)(q−1) 。
- 随机选择一个整数 e e e,满足两个条件: φ ( n ) \varphi(n) φ(n) 与 e e e互质,且 1 < e < φ ( n ) 1<e<\varphi(n) 1<e<φ(n) 。
- 计算 e e e对于 φ ( n ) \varphi(n) φ(n) 的模反元素 d d d,也就是说找到一个 d d d满足 e d = 1 m o d φ ( n ) ed = 1 mod\varphi(n) ed=1modφ(n)。这个式子等价于 e d − 1 = k φ ( n ) ed-1 = k\varphi(n) ed−1=kφ(n),实际上就是对于方程 e d − k φ ( n ) = 1 ed-k\varphi(n) = 1 ed−kφ(n)=1求 ( d , k ) (d,k) (d,k)的整数解。这个方程可以用扩展欧几里得算法求解。
- 最终把 ( e , n ) (e,n) (e,n)封装成公钥, ( d , n ) (d,n) (d,n)封装成私钥。
我们举个实际例子运行一遍这个算法。
RSA算法
Java版本
package com.tencent.blue.utils;
import org.apache.tomcat.util.codec.binary.Base64;
import org.apache.tomcat.util.http.fileupload.IOUtils;
import javax.crypto.Cipher;
import java.io.ByteArrayOutputStream;
import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.Map;
/**
* Created by cuiran on 19/1/9.
*/
public class RSAUtils {
public static final String CHARSET = "UTF-8";
public static final String RSA_ALGORITHM = "RSA";
public static Map<String, String> createKeys(int keySize){
//为RSA算法创建一个KeyPairGenerator对象
KeyPairGenerator kpg;
try{
kpg = KeyPairGenerator.getInstance(RSA_ALGORITHM);
}catch(NoSuchAlgorithmException e){
throw new IllegalArgumentException("No such algorithm-->[" + RSA_ALGORITHM + "]");
}
//初始化KeyPairGenerator对象,密钥长度
kpg.initialize(keySize);
//生成密匙对
KeyPair keyPair = kpg.generateKeyPair();
//得到公钥
Key publicKey = keyPair.getPublic();
String publicKeyStr = Base64.encodeBase64URLSafeString(publicKey.getEncoded());
//得到私钥
Key privateKey = keyPair.getPrivate();
String privateKeyStr = Base64.encodeBase64URLSafeString(privateKey.getEncoded());
Map<String, String> keyPairMap = new HashMap<String, String>();
keyPairMap.put("publicKey", publicKeyStr);
keyPairMap.put("privateKey", privateKeyStr);
return keyPairMap;
}
/**
* 得到公钥
* @param publicKey 密钥字符串(经过base64编码)
* @throws Exception
*/
public static RSAPublicKey getPublicKey(String publicKey) throws NoSuchAlgorithmException, InvalidKeySpecException {
//通过X509编码的Key指令获得公钥对象
KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM);
X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(Base64.decodeBase64(publicKey));
RSAPublicKey key = (RSAPublicKey) keyFactory.generatePublic(x509KeySpec);
return key;
}
/**
* 得到私钥
* @param privateKey 密钥字符串(经过base64编码)
* @throws Exception
*/
public static RSAPrivateKey getPrivateKey(String privateKey) throws NoSuchAlgorithmException, InvalidKeySpecException {
//通过PKCS#8编码的Key指令获得私钥对象
KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM);
PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKey));
RSAPrivateKey key = (RSAPrivateKey) keyFactory.generatePrivate(pkcs8KeySpec);
return key;
}
/**
* 公钥加密
* @param data
* @param publicKey
* @return
*/
public static String publicEncrypt(String data, RSAPublicKey publicKey){
try{
Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
return Base64.encodeBase64URLSafeString(rsaSplitCodec(cipher, Cipher.ENCRYPT_MODE, data.getBytes(CHARSET), publicKey.getModulus().bitLength()));
}catch(Exception e){
throw new RuntimeException("加密字符串[" + data + "]时遇到异常", e);
}
}
/**
* 私钥解密
* @param data
* @param privateKey
* @return
*/
public static String privateDecrypt(String data, RSAPrivateKey privateKey){
try{
Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, privateKey);
return new String(rsaSplitCodec(cipher, Cipher.DECRYPT_MODE, Base64.decodeBase64(data), privateKey.getModulus().bitLength()), CHARSET);
}catch(Exception e){
throw new RuntimeException("解密字符串[" + data + "]时遇到异常", e);
}
}
/**
* 私钥加密
* @param data
* @param privateKey
* @return
*/
public static String privateEncrypt(String data, RSAPrivateKey privateKey){
try{
Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, privateKey);
return Base64.encodeBase64URLSafeString(rsaSplitCodec(cipher, Cipher.ENCRYPT_MODE, data.getBytes(CHARSET), privateKey.getModulus().bitLength()));
}catch(Exception e){
throw new RuntimeException("加密字符串[" + data + "]时遇到异常", e);
}
}
/**
* 公钥解密
* @param data
* @param publicKey
* @return
*/
public static String publicDecrypt(String data, RSAPublicKey publicKey){
try{
Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, publicKey);
return new String(rsaSplitCodec(cipher, Cipher.DECRYPT_MODE, Base64.decodeBase64(data), publicKey.getModulus().bitLength()), CHARSET);
}catch(Exception e){
throw new RuntimeException("解密字符串[" + data + "]时遇到异常", e);
}
}
private static byte[] rsaSplitCodec(Cipher cipher, int opmode, byte[] datas, int keySize){
int maxBlock = 0;
if(opmode == Cipher.DECRYPT_MODE){
maxBlock = keySize / 8;
}else{
maxBlock = keySize / 8 - 11;
}
ByteArrayOutputStream
out = new ByteArrayOutputStream();
int offSet = 0;
byte[] buff;
int i = 0;
try{
while(datas.length > offSet){
if(datas.length-offSet > maxBlock){
buff = cipher.doFinal(datas, offSet, maxBlock);
}else{
buff = cipher.doFinal(datas, offSet, datas.length-offSet);
}
out.write(buff, 0, buff.length);
i++;
offSet = i * maxBlock;
}
}catch(Exception e){
throw new RuntimeException("加解密阀值为["+maxBlock+"]的数据时发生异常", e);
}
byte[] resultDatas = out.toByteArray();
IOUtils.closeQuietly(out);
return resultDatas;
}
public static void main (String[] args) throws Exception {
Map<String, String> keyMap = RSAUtils.createKeys(1024);
String publicKey = keyMap.get("publicKey");
String privateKey = keyMap.get("privateKey");
System.out.println("公钥: \n\r" + publicKey);
System.out.println("私钥: \n\r" + privateKey);
System.out.println("公钥加密——私钥解密");
String str = "code_cayden";
System.out.println("\r明文:\r\n" + str);
System.out.println("\r明文大小:\r\n" + str.getBytes().length);
String encodedData = RSAUtils.publicEncrypt(str, RSAUtils.getPublicKey(publicKey));
System.out.println("密文:\r\n" + encodedData);
String decodedData = RSAUtils.privateDecrypt(encodedData, RSAUtils.getPrivateKey(privateKey));
System.out.println("解密后文字: \r\n" + decodedData);
}
}
C++
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include<string.h>
#include <math.h>
#include<algorithm>
using namespace std;
typedef long long ll;
int e, d, n;
int gcd(int a, int b) //求最大公约数
{
int c = 0;
if(a<b) swap(a,b);
c = b;
do
{
b = c;
c = a%b;
a = b;
}
while (c != 0);
return b;
}
int PrimarityTest(int a, int i) //判断i是否是素数
{
int flag=0;
for(a;a<i;a++)
{
if(i%a==0)
{
flag=1;
break;
}
}
if(flag) return 0;
return 1;
// complete this part
}
int ModularExponention(int a, int b, int n) //求a^bmodn
{
int y;
/*使用二进制平方乘法计算 pow(a,b) % n*/
y=1;
while(b != 0)
{
/*对于b中的每个1,累加y*/
if(b & 1)
y = (y*a) % n;
/*对于b中的每一位,计算a的平方*/
a = (a*a) % n;
/*准备b中的下一位*/
b = b>>1;
}
return y;
// complete this part
}
void extgcd(ll a,ll b,ll& d,ll& x,ll& y) //获取(1/a)modb得结果
{
if(!b)
{
d=a;
x=1;
y=0;
}
else
{
extgcd(b,a%b,d,y,x);
y-=x*(a/b);
}
}
int ModularInverse(int a,int b) //获取(1/a)modb得结果
{
ll d,x,y;
extgcd(a,b,d,x,y);
return d==1?(x+b)%b:-1;
// complete this part
}
void KeyGeneration() //获取公钥密钥
{
int p, q;
int phi_n;
do
{
do
p = rand();
while (p % 2 == 0);
}
while (!PrimarityTest(2, p));
do
{
do
q = rand();
while (q % 2 == 0);
}
while (!PrimarityTest(2, q));
n = p * q;
phi_n = (p - 1) * (q - 1);
do
e = rand() % (phi_n - 2) + 2; // 1 < e < phi_n
while (gcd(e, phi_n) != 1);
d = ModularInverse(e,phi_n);
}
void Encryption(int value, FILE* out) //加密
{
int cipher;
cipher = ModularExponention(value, e, n);
fprintf(out, "%d ", cipher);
}
void Decryption(int value, FILE* out) //解密
{
int decipher;
decipher = ModularExponention(value, d, n);
fprintf(out, "%c", decipher);
}
int main(void)
{
FILE* inp, * out;
char filepath[15], filename[100];
strcpy(filepath, "F:\Desktop\\"); //文件路径
sprintf(filename, "%s%s", filepath, "cipher.txt");
out = fopen(filename, "w+"); //打开文件
fclose(out);
sprintf(filename, "%s%s", filepath, "decipher.txt");
out = fopen(filename, "w+"); //打开文件
fclose(out);
KeyGeneration(); //获取公钥密钥
sprintf(filename, "%s%s", filepath, "plain.txt");
inp = fopen(filename, "r+"); //读取原文件
if (inp == NULL)
{
printf("Error opening Source File.\n");
exit(1);
}
sprintf(filename, "%s%s", filepath, "cipher.txt");
out = fopen(filename, "w+");
if (out == NULL)
{
printf("Error opening Destination File.\n");
exit(1);
}
// encryption starts
while (1)
{
char ch = getc(inp); //读取文件字符,一个字符一个字符的读取输出
if (ch == -1)
break;
int value = toascii(ch); //toascii将字符转化成对应ascall值
Encryption(value, out); //加密输出
}
fclose(inp);
fclose(out);
// decryption starts
sprintf(filename, "%s%s", filepath, "cipher.txt");
inp = fopen(filename, "r");
if (inp == NULL)
{
printf("Error opening Cipher Text.\n");
exit(1);
}
sprintf(filename, "%s%s", filepath, "decipher.txt");
out = fopen(filename, "w+");
if (out == NULL)
{
printf("Error opening File.\n");
exit(1);
}
while (1)
{
int cip;
if (fscanf(inp, "%d", &cip) == -1)
break;
Decryption(cip, out); //解密
}
fclose(out);
return 0;
}
密码为什么一定要用到素数(质数)?
这个问题谁能回答我一下?