程序猿成长之路之密码学篇-RSA非对称分组加密算法介绍

好久不见各位,最近事情有点多,没来得及更新博客,这两天抽空把RSA算法初步实现了一下,下面和大家分享一下经验。

什么是非对称加密?

非对称加密是相对于对称加密而言的,它具有加解密密钥不一致(不重复使用加密的密钥来进行解密)、安全性能高的特点,一般而言非对称加密的算法有RSA, SM2等。而对称加密的算法则主要有DES、3DES、AES、SM1等。

什么是分组加密

分组加密是将明文分成按照字节或字符进行分组,从而提高加解密效率的一种处理方式。大家可以查看我之前的博客去了解一下。
https://blog.csdn.net/qq_31236027/article/details/128003587?spm=1001.2014.3001.5501

了解RSA算法前需要掌握的内容

  1. 欧拉函数:f(n) = n -1 (n为质数) f(pq) = (p-1)(q-1) (p,q互质)f(p^n) = p^n - p^(n-1)
  2. 欧拉定理: 若 a 与 n互质,则有 a ^ f(n) = 1 mod n 必然成立。(是费马小定理的一种特殊情况)
  3. 欧几里得公式及其扩展公式:gcd(a,b) 和 gcdExtend(a,b)
    这里重点介绍一下扩展公式,它是通过逆递推的方式实现 x *a + y *b = 1 的方程式求解的一种办法。实现原理如下:
    1. 设置几个初始变量,r0,r1,r2: r0 - a与b中的较大者,r1 - a与b中的较小者,r2 - 余数
    2. 设置其余变量,q1 - r0 整除 r1 后的值
    3. 初始化公式:r0 = q1 * r1 + r0 & ri = si*r i-2 + ti * ri-1
    4. 利用数学归纳法有: si = si-2- qi-1*si-1; ti = ti-2 - qi-1*ti-1
    5. 当r2 为0 或 r1 为0的时候 退出循环,返回tn和sn的数组。
  4. Muller素性测试:这个不再赘述,大家可以自己网上查阅资料。

RSA算法的优缺点

优点:RSA算法安全性能好
缺点:执行效率较低,没有对称加密效率高。

RSA算法的实现原理

  1. 取两个互质的大素数p,q
  2. 求其乘积n,即n =p*q
  3. 利用欧拉函数求f(n) = (p-1)*(q-1)
  4. 在[2,fn-1]中取e,即其中加密参数的值,记得要和fn互质,即获得私钥(n,e)
  5. 利用欧几里得扩展公式求 d,即为公钥,有 e * d = 1 mod fn
  6. 之后利用数学特性有 (明文)^(e)^(d) = (明文)

RSA算法总体实现代码:

/**
	 * 测试类
	 * @param args
	 */
	public static void main(String[] args) {
		
		String send = "lalalala";
		StringBuffer sb = new StringBuffer();
		
		BigInteger b1,b2;
		//获取两个大素数
		b1 = RNG.getPrime(300);
		b2 = RNG.getPrime(300);
		for(char ch: send.toString().toCharArray()) {
			int i = (ch - '0' + 48);
			BigInteger s = BigInteger.valueOf(i);
			//求大素数的乘积
			BigInteger n = b1.multiply(b2);
			//求f(n) 欧拉函数 = (b1-1) * (b2-1) => 表示与n互质的数的个数
			BigInteger fn = (b1.subtract(BigInteger.ONE)).multiply((b2.subtract(BigInteger.ONE)));
			BigInteger e = fn.subtract(BigInteger.valueOf(2));
			//从后面出发出错概率会小
			while(e.compareTo(BigInteger.valueOf(2)) > 0) {
				if (EucleidesUtils.gcd(e, fn).compareTo(BigInteger.ONE) == 0) {
					break;
				}
				e = e.subtract(BigInteger.ONE);
			}
//			System.out.println("e = "  + e.toString());
			//求e-1,利用欧几里得扩展公式
//			System.out.println("fn = " + fn.toString());
			BigInteger d = EucleidesUtils.eucleides(e, fn)[0];
//			System.out.println("d = " + d.toString());
			s = s.modPow(e,n);
//			System.out.println("s' = " + s.toString());
			s = s.compareTo(BigInteger.ZERO) == 0 ? BigInteger.ZERO :s.modPow(d, n);
//			System.out.println("s mod = " + s.toString());
			sb.append((char)((s.intValue() - 48) + '0'));
		}
		System.out.println("result = " + sb.toString());
	}

欧几里得扩展公式

	/**
	 * 欧几里得扩展公式(numb1 numb2分别是)
	 * @param numb1 numb1
	 * @param numb2 numb2 
	 * @return 
	 */
	public static BigInteger[] eucleides(BigInteger numb1,BigInteger numb2) { 
		BigInteger s0 = BigInteger.ONE,t0 =BigInteger.ZERO,s1 = BigInteger.ZERO
				,t1 = BigInteger.ONE,sn = BigInteger.ZERO,tn = BigInteger.ZERO; //i-轮数
		int i = 0;
		BigInteger r0,r1,r2,q1;
		if (numb1.compareTo(numb2) < 0) {
			r0 = numb2;
			r1 = numb1;
		} else {
			r0 = numb1;
			r1 = numb2;
		}
		do{
			if (r1.compareTo(BigInteger.ZERO) == 0) {
				return new BigInteger[]{tn,sn};
			}
			r2 = r0.remainder(r1); 		//step1. 求numb1与numb2的余数
			q1 = (r0.subtract(r2)).divide(r1);	//step2. 求numb1与numb2的除数
			sn = s0.subtract(q1.multiply(s1));	//step3. 根据数学归纳法 rn = snr0 + tnr1=> sn = sn-2r0 + tn-2r1
			tn = t0.subtract(q1.multiply(t1));	//step4. 根据数学归纳法 rn = snr0 + tnr1=> tn = sn-1r0 + tn-1r
			//获取新的数据
			//第一轮
			if(i == 0) {
				BigInteger temp = sn.multiply(r0).add(tn.multiply(r1)); //gcd(r0,r1) = s*r0 + t*r1;
				r0 = r1;
				r1 = temp;
			} else {
				//后续几轮
				r0 = r1;
				r1 = r2;
				if (r1.compareTo(BigInteger.ZERO) == 0) {
					return new BigInteger[]{tn,sn};
				}
				r2 = r0.remainder(r1); 	
			}	
			//获取新数据
			s0 = s1;
			s1 = sn;
			t0 = t1;
			t1 = tn;
			i++;
		} while(r2.compareTo(BigInteger.ZERO) != 0);
		return new BigInteger[]{tn,sn};
	}

------------------------------------------------ 2023.11.15 --------------------------------------------------------------------
优化了一下,支持任意字符的加解密

/**
	 * b1,b2
	 */
	public static BigInteger b1= null, b2 = null;
	public static BigInteger fn = null, d = null, n = null, e = null;
	
	static {
		//获取两个大素数
		b1 = RNG.getPrime(300);
		b2 = RNG.getPrime(300);
		
		//求f(n) 欧拉函数 = (b1-1) * (b2-1) => 表示与n互质的数的个数
		fn = (b1.subtract(BigInteger.ONE)).multiply((b2.subtract(BigInteger.ONE)));
		//求大素数的乘积
		n = b1.multiply(b2);
		e = fn.subtract(BigInteger.valueOf(2));
		//从后面出发出错概率会小
		while(e.compareTo(BigInteger.valueOf(2)) > 0) {
			if (EucleidesUtils.gcd(e, fn).compareTo(BigInteger.ONE) == 0) {
				break;
			}
			e = e.subtract(BigInteger.ONE);
		}
		//求e-1,利用欧几里得扩展公式
		d = EucleidesUtils.eucleides(e, fn)[0];
	}
	
	
	/**
	 * 测试类
	 * @param args
	 */
	public static void main(String[] args) {
		
		String send = "{\"code\":200,\"message\":\"成功!\",\"data\":{\"id\":\"2103813902831\",\"name\":\"章鱼哥是我啊\",\"gender\":\"男\"}}";
		RsaUtil rsa = new RsaUtil();
		String encryptedText = rsa.encrypt(send, null, null);
		System.out.println(rsa.decrypt(encryptedText, null, null));
	}

	/**
	 * 加密
	 */
	@Override
	public String encrypt(String text, String iv, EncryptMode mode) {
		StringBuffer encryptedText = new StringBuffer();
		List<String> list = new ArrayList<>();
		for(char ch: text.toString().toCharArray()) {
			int i = (ch - '0' + 48);
			BigInteger s = BigInteger.valueOf(i);
			s = s.modPow(e,n);
			list.add(EncodeUtil.toBinary(s.toString(),EncodeRadix.DEC, true));
			encryptedText.append(
					EncodeUtil.binaryToHexStr(
							EncodeUtil.toBinary(s.toString(),EncodeRadix.DEC, true
					),false)
			).append("-");	
		}
		System.out.println("encryptedText = " + encryptedText.toString());
		return encryptedText.toString();
	}

	/**
	 * 解密
	 */
	@Override
	public String decrypt(String encrytedText, String iv, EncryptMode mode) {
		StringBuffer sb = new StringBuffer();
		String encyptedText = encrytedText.toString();
		for(String str: encyptedText.split("\\-+")) {
			String tmp = EncodeUtil.toBinary(str, EncodeRadix.HEX, true);
			tmp = new BigInteger(tmp).toString();
			BigInteger s = new BigInteger(EncodeUtil.binaryToDec(tmp));
			s = s.compareTo(BigInteger.ZERO) == 0 ? BigInteger.ZERO: s.modPow(d,n);
			sb.append((char)((s.intValue() - 48) + '0'));
		}
		System.out.println("result = " + sb.toString());
		return sb.toString();
	}

其中,EncodeUtil详见前面的文章介绍。
https://blog.csdn.net/qq_31236027/article/details/128579451?spm=1001.2014.3001.5501

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zygswo

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值