RSA非对称加密算法解析:密钥、明文及密文长度的约定--以及使用RSA算法实现登录时的前后端的加解密

1、RSA算法基础

RSA算法是一种非对称加密算法,非对称即:加解密用的不是同一个秘钥,它有一对秘钥,分为公钥和私钥。公钥加密,一般是客户端进行处理;私钥解密,一般是后端处理。公钥要暴露给加密方使用,私钥则要藏起来,一般由服务器管理。

1.1关于秘钥长度

一般来说,我们默认使用或常用的秘钥长度值是1024bit位,即1024/8=128byte,目前主流可选值:1024、2048、3072、4096,最小好像是512位,但如果老大就要256位的,也是有解决办法的,参考本文最底部。在使用RSA加密的过程中,显然秘钥长度越长,加密的强度也就越强,同时程序计算的时间也会变长。秘钥长度增加一倍,密钥对生成的时间就增加16倍,公钥加密操作时长增加4倍,私钥解密操作时长增加8倍,所以秘钥长度视情况而定,不宜太大,否则效率低下。

1.2关于明文长度

一般来说,如果我们“定长定量自己可控可理解”的加密(比如下面加密代码),则可以视明文长度=秘钥bit长度值/8-11,比如我下面使用的秘钥长度是1024bit,那么她一次可加密的明文长度就是:1024/8-11=117byte,所以当明文较长时或不确定时,就需要进行分块加密,而每一块就是117byte。

1.3关于密文长度

分段加密后,每一块明文加密后的密文长度=密钥的长度,比如我下面使用的秘钥是 1024bit,那么在此条件下生成的每一块的密文长度1024bit/8=128Byte

所以在加密和解密的过程中都需要判断明文或密文的长度,然后分块进行。RSA加解密对内容的长度是有限制的,如果加密数据过大会抛出如下异常:

Exception in thread "main" javax.crypto.IllegalBlockSizeException: Data must not be longer than 117 bytes

2、java实现RSA加解密以及密钥对的生成:

package com.util;

import java.io.ByteArrayOutputStream;
import java.security.Key;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.HashMap;
import java.util.Map;

import javax.crypto.Cipher;


/**
 * SRA算法加解密工具
 * @author aigov
 */
public class RSAEncryptUtil {
	/**加密算法RSA*/
	public static final String KEY_ALGORITHM = "RSA";
	/**获取公钥的key*/
	private static final String PUBLIC_KEY = "RSAPublicKey";
	/**获取私钥的key*/
	private static final String PRIVATE_KEY = "RSAPrivateKey";
	/**RSA最大加密明文大小*/
	private static final int MAX_ENCRYPT_BLOCK = 117;
	/**RSA最大解密密文大小*/
	private static final int MAX_DECRYPT_BLOCK = 128;
	
	/**
	 * 生成密钥对(公钥和私钥)
	 * @return base64格式的公私钥
	 */
	public static Map<String, String> genKeyPair()  {
		//map存放密钥对
		Map<String, String> keyMap = null ;
		try{
	        //秘钥对生成器
			KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(KEY_ALGORITHM);
			//初始化秘钥大小为1024位
			keyPairGen.initialize(1024);
			//生成密钥对
			KeyPair keyPair = keyPairGen.generateKeyPair();
			RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
			RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
			//公钥转base64编码
			byte[] pk = publicKey.getEncoded();
			String publicKeyB64 = Base64Utils.encode(pk);
			//私钥转base64编码
			byte[] privPk = privateKey.getEncoded();
			String privateKeyB64 = Base64Utils.encode(privPk);
			
			keyMap = new HashMap<String, String>(2);
			keyMap.put(PUBLIC_KEY, publicKeyB64);
			keyMap.put(PRIVATE_KEY, privateKeyB64);
		}catch(Exception e){
			e.getStackTrace();
		}
		return keyMap;
	}
	
	/**
	 * 私钥解密
	 * @param encryptedData  已加密数据
	 * @param privateKey  私钥(BASE64编码)
	 * @return
	 * @throws Exception
	 */
	public static byte[] decryptByPrivateKey(byte[] encryptedData, String privateKey) throws Exception {
		//base64解密私钥
		byte[] keyBytes = Base64Utils.decode(privateKey);
		//PKCS8EncodedKeySpec类继承EncodedKeySpec类,以PKCS#8标准的编码格式来表示*私钥*。
		PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
		//实例化秘钥工厂
		KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
		//生成私钥
		Key privateK = keyFactory.generatePrivate(pkcs8KeySpec);
		//实例化加密对象
		Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
		cipher.init(Cipher.DECRYPT_MODE, privateK);
		int inputLen = encryptedData.length;
		ByteArrayOutputStream out = new ByteArrayOutputStream();
		int offSet = 0;
		byte[] cache;
		int i = 0;
		
		// 分段解密
		while (inputLen - offSet > 0) {
			if (inputLen - offSet > MAX_DECRYPT_BLOCK) {
				cache = cipher.doFinal(encryptedData, offSet, MAX_DECRYPT_BLOCK);
			} else {
				cache = cipher.doFinal(encryptedData, offSet, inputLen - offSet);
			}
			out.write(cache, 0, cache.length);
			i++;
			offSet = i * MAX_DECRYPT_BLOCK;
		}
		byte[] decryptedData = out.toByteArray();
		out.close();
		return decryptedData;
	}
	
	/**
	 * 私钥解密
	 * @param data  已加密数据(BASE64字符)
	 * @param PRIVATEKEY  私钥(BASE64编码)
	 * @return
	 */
	public static String decryptDataOnJava(String data, String PRIVATEKEY) {
		String temp = "";
		try {
			byte[] rs = Base64Utils.decode(data);
			temp = new String(RSAEncryptUtil.decryptByPrivateKey(rs, PRIVATEKEY));
		} catch (Exception e) {
			e.printStackTrace();
		}
		return temp;
	}



	/**
     * 公钥加密--一般前端做加密
     * @param data 明文
     * @param publicKey 公钥(BASE64编码)
     * @return
     * @throws Exception
     */
    /**
    public static byte[] encryptByPublicKey(byte[] data, String publicKey)throws Exception {
        byte[] keyBytes = Base64Utils.decode(publicKey);
        //X509EncodedKeySpec类继承EncodedKeySpec类,以X.509标准的编码格式来表示*公钥*。
        X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        Key publicK = keyFactory.generatePublic(x509KeySpec);
        Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
        cipher.init(Cipher.ENCRYPT_MODE, publicK);
        int inputLen = data.length;
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        int offSet = 0;
        byte[] cache;
        int i = 0;
        // 对数据分段加密
        while (inputLen - offSet > 0) {
            if (inputLen - offSet > MAX_ENCRYPT_BLOCK) {
                cache = cipher.doFinal(data, offSet, MAX_ENCRYPT_BLOCK);
            } else {
                cache = cipher.doFinal(data, offSet, inputLen - offSet);
            }
            out.write(cache, 0, cache.length);
            i++;
            offSet = i * MAX_ENCRYPT_BLOCK;
        }
        byte[] encryptedData = out.toByteArray();
        out.close();
        return encryptedData;
    }
    **/

	
}

3、controller调用

//登录	
public void toLogin() {

	//缓存秘钥
	Map<String, String> KeyPair = RSAEncryptUtil.genKeyPair();
	setAttr("publicKeys", KeyPair.get("RSAPublicKey"));
	setSessionAttr("privateKeys", KeyPair.get("RSAPrivateKey"));
		
    render("");
}
//私钥解密解密
String privateKey = getSessionAttr("privateKeys");
String loginPwd = RSAEncryptUtil.decryptDataOnJava(loginPwd, privateKey);

//拿解密后的密码做其它业务判断:验证登录信息、、、、

4、前端加密

引入

//加密
var encrypt = new JSEncrypt();
var publicKey = '${publicKeys}';//  公钥
var pwdYw = $('#user_password').val(); // 密码原文 
encrypt.setPublicKey(publicKey);
var pwdMw = encrypt.encrypt(pwdYw);// 密码密文

5、补充

密钥越长越安全,但是老大就要用256位的密钥的话,参考下面:

.....
.....
KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA",new org.bouncycastle.jce.provider.BouncyCastleProvider());
final int KEY_SIZE = 256;
keyPairGen.initialize(KEY_SIZE, new SecureRandom());
KeyPair keyPair = keyPairGen.generateKeyPair();
.....
.....

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值