RSA加密算法原理及其Java实现

烂漫一生 提交于 2019-12-02 05:41:28
简单介绍了RSA加密算法的原理及其Java实现

RSA加密算法的基本原理

主要步骤

本文所有的字母都为正整数。
其主要步骤如下:
1、取两个不相等的质数p、q,一般都比较大。例如:p=67,q=79。
2、n=p×q,其中n所对应的二进制长度即为密钥的长度,一般为1024位或2048位。n=67×79=5293,转化为二进制数为1 0100 1010 1101,共13位。
3、计算n的欧拉函数f(n):
欧拉函数f(n)定义为:小于n的正整数中和n组成互斥关系的正整数的个数。由欧拉函数的性质:(1)如果正整数可以分解为两个互斥整数之积,则有该正整数的欧拉函数等于两个互斥整数的欧拉函数之积;(2)如果整数为质数,其欧拉函数为该整数减一。则有:
f(n)=f(p)f(q)=(p1)(q1)(1)f(n)=f(p)*f(q)=(p-1)*(q-1)\tag{1}
f(n)=66×78=5148
4、任选一整数x满足1<x<f(n),且x与f(n)互斥。选x=19
5、计算x关于f(n)的模反元素y,即满足:
(xy)%f(n)=1xy=hf(n)+1(2)(x*y)\%f(n)=1\tag{2}\\ xy=hf(n)+1
%为取余。
通过扩展欧几里得算法求解19y=5148h+1:
在这里插入图片描述
求得y=271,h=-1。
则(n,x)为公钥,(n,y)为私钥,假设原文为a(这里的原文是要转换为ascii码或者其他编码方式,用数字表示),且a小于n,则获取密文b的加密过程为:
b=ax%n(3)b=a^{x}\%n\tag{3}
解密过程为:
a=by%n(4)a=b^{y}\%n\tag{4}
假设传递字符A,其ascii码值为65,则通过(3)式计算得到b=1863,通过(4)式解密得到a=65。注意这里数字足够大,以至于使用普通整数计算得到的结果是错误的。

BigInteger num1 = new BigInteger("65");
BigInteger num2 = new BigInteger("5293");
BigInteger b = num1.pow(19).mod(num2);
System.out.println(b); 
System.out.println("普通的整数运算是错误的:"+Math.pow(65, 19)%5293);  
BigInteger a = b.pow(271).mod(num2);
System.out.println(a);
// 输出为:
1863
普通的整数运算是错误的:4055.0
65

可靠性说明:因为公钥是公开的,所以破解的方法就是获取y,那么在已知n和x的情况下能否求解出y呢?
(1)通过步骤5可知,只有知道x和f(n)才能求解y;
(2)要计算f(n)需要得到p和q;
(3)n=pq,只有将n进行因式分解才能算出p和q。
大整数的因式分解是很难的,所以RSA加密算法可靠。

解密过程证明

下面给出解密公式正确性的证明,即证明(4)式成立:
通过(3)式得:
b=axkn(5)b=a^{x}-kn\tag{5}
将(5)式代入到(4)式等号右边得:
by%n=(axkn)y%n=(axy+nK(n))%n=axy%n(6)b^{y}\%n=(a^{x}-kn)^{y}\%n=(a^{xy}+nK(n))\%n=a^{xy}\%n\tag{6}
这里用到了多项式的因式分解:
(x+y)n=xn+(n1)xyn1+...+(nn1)xn1y+yn(7)(x+y)^n=x^n+\binom{n}{1}xy^{n-1}+...+\binom{n}{n-1}x^{n-1}y+y^{n}\tag{7}
其中只有第一项不包含y。
将(2)式代入到(6)式,则只需要证明:
ahf(n)+1%n=a(8)a^{hf(n)+1}\%n=a\tag{8}
下面分情况进行讨论:
情况1、a和n互斥。
这里引入欧拉定理:如果a和n互斥,n的欧拉函数为f(n),则
af(n)%n=1(9)a^{f(n)}\%n=1\tag{9}
根据欧拉定理:
af(n)=1+gnahf(n)=(1+gn)hahf(n)+1=af(n)a=(1+gn)ha=(1+hgn+...+hgh1nh1+ghnh)a(10)a^{f(n)}=1+gn→a^{hf(n)}=(1+gn)^{h}\\ a^{hf(n)+1}=a^{f(n)}*a=(1+gn)^{h}*a=(1+hgn+...+hg^{h-1}n^{h-1}+g^{h}n^{h})a\tag{10}
将(10)式代入到(8)式,则可证明(8)式成立。
情况2、a和n不互斥。
由于n为两个质数的乘积,a<n,所以必有a=kp或a=kq,不妨假设a=kp,此时a与q互斥。根据欧拉定理:
af(q)%q=1af(q)=1+gq(11)a^{f(q)}\%q=1→a^{f(q)}=1+gq\tag{11}
ahf(n)+1%q=(af(q))f(p)a%q=(1+gq)f(p)a%q=aahf(n)+1=a+tq(12)a^{hf(n)+1}\%q=(a^{f(q)})^{f(p)}*a\%q=(1+gq)^{f(p)}*a\%q=a\tag{12}\\ a^{hf(n)+1}=a+tq
因为a=kp,所以必然有:
ahf(n)+1%p=0(13)a^{hf(n)+1}\%p=0\tag{13}

(a+tq)%p=tq%p=0(14)(a+tq)\%p=tq\%p=0\tag{14}
又因为pq互斥,则由t=rp,代入到式(12)得:
ahf(n)+1=a+rpq=a+rn(a+rn)%n=aahf(n)+1%n=aa^{hf(n)+1}=a+rpq=a+rn\\ (a+rn)\%n=a→a^{hf(n)+1}\%n=a
得证(8)式。
结合情况1、2,则可证明解密公式得正确性。

java实现

package mytest;

import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.interfaces.RSAPrivateCrtKey;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Arrays;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;

import javax.crypto.Cipher;


public class RSAUtil {
	// 密钥长度,可调
	public static int keyLength = 1025;
	// 注意这里1024对应128byte,其中11byte用来存储相关信息,所以1024最多加密117Byte数据
	// 若超过117Byte需要分段,计算公式为keyLength/8并向上取整再减去11
	// 解密时候长度为keyLength/8向上取整

	// 存储密钥
	public static Map<String, String> keyMap = new HashMap<String, String>();
	
	public static void main(String[] args) throws Exception {
		//生成公钥和私钥
		geneKeyPair();
		// 输入原文
		System.out.print("输入原文:");
		Scanner in = new Scanner(System.in);
		String message = in.nextLine();
		//String message = "1aa";
		System.out.println("随机生成的公钥为:" + keyMap.get("publicKey"));
		System.out.println("随机生成的私钥为:" + keyMap.get("privateKey"));
		//加密字符串
		String messageEnctypt = encrypt(message, keyMap.get("publicKey"));
		System.out.println("原文:"+message + "\n加密后的字符串为:" + messageEnctypt);
		
		String messageDecrypt = decrypt(messageEnctypt, keyMap.get("privateKey"));
		System.out.println("还原后的字符串为:" + messageDecrypt);
		
		in.close();
	}
	
	/**
	 * function:随机生成密钥对并存放在keyMap中
	 * 也可以直接保存RSAPublicKey对象和RSAPrivateCrtKey对象
	 * 一般情况下密钥可能会保存在本地或者其他地方,然后直接读取密钥,这时候存储的一般是密钥字符串
	 * @throws NoSuchAlgorithmException 
	 */
	public static void geneKeyPair() throws NoSuchAlgorithmException {
		// RSA密钥生成器
		KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
		// 初始化密钥生成器
		keyPairGenerator.initialize(keyLength, new SecureRandom());
		// 生成密钥对并保存
		KeyPair keyPair = keyPairGenerator.generateKeyPair();
		RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();  // 公钥
		RSAPrivateCrtKey privateKey = (RSAPrivateCrtKey) keyPair.getPrivate();  // 私钥

		String publicKeyString = Base64.getEncoder().encodeToString(publicKey.getEncoded());  // 公钥字符串
		String privateKeyString = Base64.getEncoder().encodeToString(privateKey.getEncoded());  // 私钥字符串
		// 存放
		keyMap.put("publicKey", publicKeyString);
		keyMap.put("privateKey", privateKeyString);
	}
	
	/*
	 * function:加密
	 * @param 明文
	 * @param 公钥字符串,十六进制字符串
	 * @return 密文
	 * @throws Exception
	 */
	public static String encrypt(String input, String publicKeyString) throws Exception {
		// 从字符串获取公钥
		byte[] decoded = Base64.getDecoder().decode(publicKeyString.getBytes());
		RSAPublicKey publicKey = (RSAPublicKey) KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(decoded)); 
		// 加密
		Cipher cipher = Cipher.getInstance("RSA");
		cipher.init(Cipher.ENCRYPT_MODE, publicKey);
		byte[] inputArr = input.getBytes();
		StringBuilder output = new StringBuilder();
		int splitLength = (int) Math.ceil((float)keyLength/8) - 11;  // 分段长度
		for(int i = 0; i < inputArr.length; i += splitLength) {  // 分段加密
			int len = inputArr.length-i >= splitLength ? splitLength :  inputArr.length-i;
			output.append(byteArray2HexString(cipher.doFinal(Arrays.copyOfRange(inputArr, i, i+len))));
		}
		return output.toString();
	}
	
	
	/*
	 * function:私钥解密
	 * @param 密文
	 * @param 私钥字符串
	 * @return 明文
	 * @throws Exception
	 */
	public static String decrypt(String input, String privateKeyString) throws Exception {
		// 从字符串获取私钥
		byte[] decoded = Base64.getDecoder().decode(privateKeyString.getBytes());
		RSAPrivateKey privateKey = (RSAPrivateKey) KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(decoded)); 
		// 解密
		Cipher cipher = Cipher.getInstance("RSA");
		cipher.init(Cipher.DECRYPT_MODE, privateKey);
		
		int splitLength = (int) Math.ceil((float)privateKey.getModulus().bitLength()/8);  // 分段长度
		byte[] inputArr = hexString2byteArray(input);
		StringBuilder output = new StringBuilder();
		for(int i = 0; i < inputArr.length; i += splitLength) {  // 分段解密
			int len = inputArr.length-i >= splitLength ? splitLength :  inputArr.length-i;
			output.append(new String(cipher.doFinal(Arrays.copyOfRange(inputArr, i, i+len))));
		}
		return output.toString();
	}
	
	public static int getKeyLength() {
		return keyLength;
	}
	/*
	 * function:设置密钥长度
	 */
	public static void setKeyLength(int keyLength) {
		// RSA keys must be at least 512 bits long
		if(keyLength < 512) {
			keyLength = 512;
		}
		RSAUtil.keyLength = keyLength;
	}
	
	/*
	 * function:byte[]转换为十六进制字符串
	 * byte占8位,可以用两位16进制数表示
	 * @param byte[]
	 * @return String
	 */
	public static String byteArray2HexString(byte[] bytes) {
		StringBuilder output = new StringBuilder();
		String temp;
		for(byte b : bytes) {
			temp = Integer.toHexString(0xff & b);
			if(temp.length() == 1) {
				output.append(0);
			}
			output.append(temp);
		}
		return output.toString();
	}
	
	/*
	 * function:十六进制字符串转换位byte[]
	 * @param String
	 * @return byte[]
	 */
	public static byte[] hexString2byteArray(String hexString) {
		int len = hexString.length() / 2;
		byte[] output = new byte[len];
		for(int i = 0; i < len; i++) {
			int pos = i * 2;
			output[i] = (byte) (char2Byte(hexString.charAt(pos)) << 4 | char2Byte(hexString.charAt(pos+1)));
		}
		return output;
	}
	
	/*
	 * function: char转换位byte,这里的char只包含0123456789abcdef
	 * @param char
	 * @return byte
	 */
	private static byte char2Byte(char c) {
		byte b = (byte)"0123456789abcdef".indexOf(c);
		return b;
	}
}

测试结果:

输入原文:sadjhfkkjasdghjkhadkjsdhgksdhgjkshgdweuosyhjkhfsdg5sd4g5s4dg1564h56d4as1gd521f56a1sa3f54a654g5s6d41fg56ha456sd41f23g1h5a6s4dg1gf2asd5g45
随机生成的公钥为:MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCo+UfWVK/CQVayLR5DRy9ydyahLUjr0Eq4xD6pXmMouNWgSjWkiEiQ2eWE3RfAVI89qGYwvD76VvBd+uO9lMLZmh3aVk+Iu0ajTwEt4UxI3T3oGpirYYHWIBHNmcKrRyuGPpQZd+klNzzJeiS+/4Ee5mOPEKun5kJs33D61o4kKwIDAQAB
随机生成的私钥为:MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAKj5R9ZUr8JBVrItHkNHL3J3JqEtSOvQSrjEPqleYyi41aBKNaSISJDZ5YTdF8BUjz2oZjC8PvpW8F36472UwtmaHdpWT4i7RqNPAS3hTEjdPegamKthgdYgEc2ZwqtHK4Y+lBl36SU3PMl6JL7/gR7mY48Qq6fmQmzfcPrWjiQrAgMBAAECgYBZKkodMM0abc4o8aQRjoPcHEH3NWVQgrabb3s9dsBOodKg5egOrZfVUBZMmTrKVBTOTYm3V+7HvY7TmOwKg3CZ9UzxYrhhIbh7p42gAIlpnyiq7YCz3X0hz+FrRKEOJJ5eLINKxl6w3jOG1ZeY9FIHUrGIb0KJe/a5m1gkG8zFoQJBAN0zS/DPpDNJKCrnxRs1TijtLdL9JgOle7ecPKp2kbJQB6RLmJ5/675JrlR1SD5N9E4KUdgEsyF4uUr3cew6XlsCQQDDjpcTHn3wgjZOGU8WAuNHV132TSfT1YiCttFjRuXNtGVrmYdbyZh77g6qbk+DxjJ1Zw0dNh1WhsGyLOTCI9pxAkAJxW1SWunG9jFXC9vyIr2sIyYGDvax7Ip1hupLIWe4N77OrCQ2xDHWuwx/YJrrXagwFladMz/yd5G/1QRsSfvHAkB50wuEapt0R/oCnzuob7YczG2Jsbkc+0pme/NnUFR62GXSKTusz6LBmaTjQYMhiUgH4WHHD94o+BwUnmkIFIPRAkAr5ToL9zeevlwYvIxqd4++Vdcfm+1uDbxa0Rjm00ay5S3fS/3jXHs0j+CB2faxakVUYueMKmdUoYpWKfgOOk7A
原文:sadjhfkkjasdghjkhadkjsdhgksdhgjkshgdweuosyhjkhfsdg5sd4g5s4dg1564h56d4as1gd521f56a1sa3f54a654g5s6d41fg56ha456sd41f23g1h5a6s4dg1gf2asd5g45
加密后的字符串为:9186b953a69941d47b35e0abff66356c03b29e3b0be1b1187f845fd212f2bf14e9fe56484fd5b15b23782d960545bb6cb1a992142315857a6f2854e81f89c20216b5e15cf9b5496350c17224f1820fe4445044ce0adf10eba16d130dd63885b6ede13a3670009e2493f1eeaaee35e67fdcd8373692449dab4382196d85ee84861680fc1613ce132c731d6be0d34e00c7ed04a6a0c8b695a0f91983931de47ec2221575d60023cf223ac5b70c047c9c6b155e20723770cbdb13f45014656244f1894205cb8a2a4d029b3cdd2de9b1cbf1729e5ddcdf3314145c6db93a593b8e1f38c70dc064147a3d6ffaf9a21d338fa5d9a676a9449e37c8d9446abea15f1dfc
还原后的字符串为:sadjhfkkjasdghjkhadkjsdhgksdhgjkshgdweuosyhjkhfsdg5sd4g5s4dg1564h56d4as1gd521f56a1sa3f54a654g5s6d41fg56ha456sd41f23g1h5a6s4dg1gf2asd5g45
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!