Java之RSA加解密解析

1,030 阅读4分钟

  说明:在上篇中提到Base64加解密(详细参见上),但Base64本身并不是用来做加解密的。虽然可以通过变化的序列来达到加解密目的,但Base64有一个问题那就是不具备验证性,它不验证目标源是不是经过Base64加密过的,是否能解的开;通通来者不拒,解出来的东西有时候莫名其妙,所以一般还是建议少用Base64去做加解密。   本文提到RSA是一个标准的非对称加解密算法,使用场景:根据RSA的特性,它有公钥和私钥两个密匙,通常可以用在一端加密、另外一通需要解密的场景,另外一个加密的长度问题在正文中讨论;如果仅仅用来做验签则没有必要用。

一、什么是非对称算法


  非对称密钥算法是指一个加密算法的加密密钥和解密密钥是不一样的,或者说不能由其中一个密钥推导出另一个密钥。具体实现算法原理可参见官方说明,这里主要基于java的应用级别介绍。


二、RSA中的长度问题


1、RSA初始化长度   Java中实现的初始化长度默认是512,所以如果小于512会抛异常,这里的长度是bit

在这里插入图片描述
2、目标文本长度
在这里插入图片描述
在这里插入图片描述
  根据上面两个图示,同样的初始化长度,唯一的区别就是第二个图中的目标文本长度是54,导致加解密失败。那目标文本长度与初始化长度有什么关系?在Java对RSA的实现中有一个计算公式,目标文本的长度(字节)<=RSA初化长度-11;结合公式分析一下上面RSA的字节长度=512/8,即64个字节;64-11=53字节,所以要求目标文本长度一定是小于等于53个字节长度。   这里也可以解决在最开始应用场景中的另外一个问题,在使用RSA对文本加解密时一定要考虑目标文本的长度,如果文本较长,可以适当调节初始化长度(建议是8的倍数,原因不用多说了吧)。   注意:RSA的初始化长度越大,可以加密的目标文本长度越大,加密后的密文越长,但加密时的性能越差,需要综合平衡(若目标文本过长,建议分段加密);另外注意上面长度的单位。


三、RSA为什么常和Base64一起使用


1、编码一致性,Base64设计最初的目的就是解决网络传速时,编码不一致问题,这个很容易理解。 2、二进制的不可见性

在这里插入图片描述
  RSA中字符串String用来存放明文,Byte数组用来存放加密后的密文;既然是密文自然不想容易被转换;通过上面的小demo可以很清楚的看出,String和Byte数组之间可以很容易转换,所以这也是为什么引入Base64的原因。


四、完整的DEMO


   如果让Base64具有加解密的功能,至少要一部分是变化的;这里可以通过变化标准序列的方式;建议大家用到的时候,可以先看一下第3部分中标出那个类的源码(没几行代码);这个变化的序列可以根据时间、根据UUID、根据一切可以变换的东西来生成,这里是根据UUID来随机生成序列。    1、生成随机序列

import java.security.Key;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;
public final class RSAUtils {
    /**
     * 指定长度来生成RSA公私钥对。
     */
    public static KeyPair generateRSAKeyPair(int bitLength) throws NoSuchAlgorithmException {
        KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
        if (bitLength > 0)
            kpg.initialize(bitLength);
        return kpg.generateKeyPair();
    }
    public static String getBase64Key(Key key) {
        byte[] encoded = key.getEncoded();
        return new BASE64Encoder().encode(encoded);
    }
    /**
     * 根据提供的base64编码生成RSA公钥。
     */
    public static PublicKey getPubKey(String base64String) throws Exception {
        X509EncodedKeySpec pubSpec = new X509EncodedKeySpec(new BASE64Decoder().decodeBuffer(base64String)); 
        return KeyFactory.getInstance("RSA").generatePublic(pubSpec);
    }
    /**
     * 根据提供的base64编码生成RSA私钥。
     */
    public static PrivateKey getPriKey(String base64String) throws Exception {
        PKCS8EncodedKeySpec priSpec = new PKCS8EncodedKeySpec(new BASE64Decoder().decodeBuffer(base64String));
        return KeyFactory.getInstance("RSA").generatePrivate(priSpec);
    }
}

public static void main(String[] args) throws Exception {
		KeyPair key = RSAUtils.generateRSAKeyPair(960);
		String publicKey = RSAUtils.getBase64Key(key.getPublic());
		String privateKey = RSAUtils.getBase64Key(key.getPrivate());

		System.out.println(publicKey);
		System.out.println(privateKey);

		StringBuilder sb = new StringBuilder();
		sb.append("rank=1&sessionId=")
				.append("0123456789ABCDEF0123456789ABCDEF").append("&ts=")
				.append(System.currentTimeMillis() + 1000000000);

		String en = encode(sb.toString(), key);
		String de = decode(en, key);
		System.out.println("原文:" + sb.toString());
		System.out.println("密文:" + en);
		System.out.println("密文:"+encode(sb.toString(), key));
		System.out.println("解密:" + de);
		
		

	}

	public static String encode(String src, KeyPair key) {
		String hashCode = null;
		try {
			PrivateKey publicKey = (PrivateKey) key.getPrivate();
			Cipher cipher = Cipher.getInstance("RSA");
			cipher.init(Cipher.ENCRYPT_MODE, publicKey);
			byte[] output = cipher.doFinal(src.getBytes());

			hashCode = new BASE64Encoder().encode(output);
		} catch (Exception e) {

		}
		return hashCode;
	}

	public static String decode(String src, KeyPair key) {
		String returnRes = null;
		try {
			PublicKey publicKey = (PublicKey) key.getPublic();
			Cipher cipher = Cipher.getInstance("RSA");
			cipher.init(Cipher.DECRYPT_MODE, publicKey);
			byte[] output = cipher.doFinal(new BASE64Decoder()
					.decodeBuffer(src));
			returnRes = new String(output);
		} catch (Exception e) {

		}
		return returnRes;
	}

持续更新中,可以关注........

javaview.jpg