Java后端生成RSA随机密钥对,并实现前端(app和web)使用公钥加密,后端使用私钥解密

       最近在思考网站登录注册时,如何保证用户的密码即使在传输过程中被劫持和破解(我常常喜欢将HTTP和HTTPS比作是押运公司,其功能是护送客户端与第三方服务器之间的交互数据,但是即便是强大如HTTPS,恐怕也不可能百分百保证数据的传输安全吧!况且,这家押运公司还是属于公共的收费服务,假如你托人家运输一箱RMB现金,然后装RMB的是快递用的纸箱,求你此刻的内心忐忑不?假设HTTPS请求仍有1%的几率被劫持与破解,那么身为开发人员的我们,是不是有义务保护自己网站用户的一些敏感信息的安全了)后也能保证账号密码的安全。

        为方便大家理解,这里不妨先假设一个大前提——用户的登录注册请求(包括HTTP和HTTPS请求)在传输过程被劫持并被破解了,攻击者拿到了用户的登录账号和登录密码。通常后端开发人员编写登录注册接口时,都会要求用户名明文传入,也就是用户输入的是什么,传到后台的就是什么(如果你要求的安全非常高,也可以另行加密)。但是密码的话,就得分情况了,每个公司的情况都不一样了。搞笑版的就是开发人员直接明文传输用户的登录密码(比如我接手的这个项目,以前的产品经理和测试、开发人员估计是刚培训出来的。。。),即使网站使用了HTTPS协议,但是后台开发人员只要在登录接口加条打印语句,就能轻松获取到用户的账号和对应的密码,这对用户的隐私恐怕有些不尊重了,尤其是遇到一些心术不正的开发,他们离职后对你网站所能造成的伤害可能是成吨的!低配版的,就是只对密码进行简单的MD5加密后传给后台,但是MD5也是可以暴力破解的,只是时间问题;而且攻击者压根不需要破解出密码的明文,只需要用它截取到的用户名和MD5加密后的密码作为参数然后直接调你网站的登录接口就好了,因为通常登录接口都是完全暴露的(即任何人都可以调用),校验通过后攻击者拿到授权令牌,接下来就可以为所欲为了。中配版的,就是使用RSA密钥对,前端保留公钥文件,服务器后端保留私钥文件,每次登录时,前端使用公钥对密码进行加密,后端使用私钥解密,乍一看,这个应该挺安全了,毕竟使用公钥加密后,每次加密后得到的字符串都是不一样的;但是只是不一样,并不是不能用!如果攻击者拿着这个用户名和公钥加密后的密码直接调用你的登录接口,一样能登录成功,并获得授权令牌。高配版的方法,就是用户每次登录时,由服务端生成一个随机密钥对,将公钥返回给前端,私钥保存到服务器,并设置该密钥对的有效期为30分钟(有效时间大家随意),用户登录的时候,要求前端对用户的明文密码进行公钥加密后传给后端,然后后端用私钥对密码进行解密,然后与数据库保存的密码进行匹配,登录成功之后,服务器立即清除这个密钥对。这样即使攻击者拦截到了加密后的密码,并用这个加密后的密码再次伪造登录,也无法成功。

 

下面直接上代码,首先是Java生成密钥对的工具类(从这篇博客https://blog.csdn.net/qy20115549/article/details/83105736抄的,亲测有用):

package rsa;

import org.apache.commons.codec.binary.Base64;
import org.apache.log4j.Logger;

import javax.crypto.Cipher;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.Map;

public class RSAEncrypt {
	
	private static final Logger logger = Logger.getLogger(RSAEncrypt.class);

    public static void main(String[] args) throws Exception {
		String s="haha123";
		Map map = RSAEncrypt.genKeyPair();
		String s2 = RSAEncrypt.encrypt(s, map.get(0));
		System.out.println("公钥加密后:"+s2);
		System.out.println("私钥解密后:"+RSAEncrypt.decrypt(s2, map.get(1)));
		
	}

	/** 
	 * 随机生成密钥对  
	 */  
	public static Map genKeyPair() {  
		
		Map keyMap = new HashMap();  //用于封装随机产生的公钥与私钥
		
		try {
			// KeyPairGenerator类用于生成公钥和私钥对,基于RSA算法生成对象  
			KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA");
			
			// 初始化密钥对生成器,密钥大小为96-1024位  
			keyPairGen.initialize(1024,new SecureRandom());  
			
			// 生成一个密钥对,保存在keyPair中  
			KeyPair keyPair = keyPairGen.generateKeyPair();  
			RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();   // 得到私钥  
			RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();  // 得到公钥  
			
			// 得到公钥字符串  
			String publicKeyString = new String(Base64.encodeBase64(publicKey.getEncoded()));  
			// 得到私钥字符串  
			String privateKeyString = new String(Base64.encodeBase64((privateKey.getEncoded())));  
			
			// 将公钥和私钥保存到Map
			keyMap.put(0,publicKeyString);  //0表示公钥
			keyMap.put(1,privateKeyString);  //1表示私钥
		} catch (Exception e) {
			logger.info("生成公钥私钥异常:"+e.getMessage());
			return null;
		}
		
		return keyMap;
	}  
	/** 
	 * RSA公钥加密 
	 * @param str  需要加密的字符串
	 * @param publicKey  公钥 
	 * @return 公钥加密后的内容
	 */  
	public static String encrypt( String str, String publicKey ){
		String outStr=null;
		try {
			//base64编码的公钥
			byte[] decoded = Base64.decodeBase64(publicKey);
			RSAPublicKey pubKey = (RSAPublicKey) KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(decoded));
			//RSA加密
			Cipher cipher = Cipher.getInstance("RSA");
			cipher.init(Cipher.ENCRYPT_MODE, pubKey);
			outStr = Base64.encodeBase64String(cipher.doFinal(str.getBytes("UTF-8")));
		} catch (Exception e) {
			logger.info("使用公钥加密【"+str+"】异常:"+e.getMessage());
		}
		return outStr;
	}

	/** 
	 * RSA私钥解密
	 * @param str  加密字符串
	 * @param privateKey  私钥 
	 * @return 私钥解密后的内容
	 */  
	public static String decrypt(String str, String privateKey){
		String outStr=null;
		try {
			//64位解码加密后的字符串
			byte[] inputByte = Base64.decodeBase64(str.getBytes("UTF-8"));
			//base64编码的私钥
			byte[] decoded = Base64.decodeBase64(privateKey);  
			RSAPrivateKey priKey = (RSAPrivateKey) KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(decoded));  
			//RSA解密
			Cipher cipher = Cipher.getInstance("RSA");
			cipher.init(Cipher.DECRYPT_MODE, priKey);
			outStr = new String(cipher.doFinal(inputByte));
		} catch (Exception e) {
			logger.info("使用私钥解密异常:"+e.getMessage());
		}
		return outStr;
	}

}

 

使用的jar包:



    commons-codec
    commons-codec
    1.9

 

web前端引用的js加密库:


                    
                    

你可能感兴趣的:(Java后端,工具类)