RSA浏览器加密,服务端解密

6 篇文章 0 订阅
0 篇文章 0 订阅

       最近公司一直提及网络安全,特别是密码传输安全,由于LZ一直给运营商做web平台,切均为内网访问,所以也一直未使用https管理用户登陆和密码重置页面。首先声明若想做到彻底的密码安全,https是必需的,可以防止传输过程中的抓包窃取行为,从根本上解决密码泄露问题。LZ本次尝试的js端加密,java端解密的方式,只能从一定意义上实现密码安全传输,不能防止恶意模仿http进行请求操作。

       LZ本次使用的是RSA非对称加密算法,该算法需要生成一个秘钥对,称为公钥和私钥,发送方使用公钥加密,接收方使用私钥解密,只要私钥不泄露,采用1024位方式生成的秘钥加密,几乎是不可能破解的,更多该加密算法详情,请自行了解。js端加解密,参考附件jsencrypt.zip,不做赘述,使用比较简单。RSA秘钥对的生成,使用OpenSSL工具1024位方式生成,网上亦有资料可供参考。

       仅贴出java端代码供大家参考使用,其中public_key.txt和private_key.txt分别存放OpenSSL生成的公私钥信息,方便管理,同时此类需要引用第三方jar包bcprov-jdk14-138.jar,版本需自己选定。

package com.ai.wyw.utils;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.security.InvalidKeyException;
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.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;

import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;

import org.bouncycastle.jce.provider.BouncyCastleProvider;

import sun.misc.BASE64Decoder;

public final class RSAUtils
{
	/**
	 * openSSL生成的公钥串
	 */
	private static final String DEFAULT_PUBLIC_KEY = "";
	
	/**
	 * openSSl生成的公钥串
	 */
	private static final String DEFAULT_PRIVATE_KEY = "";
	
	/**
	 * 私钥
	 */
	private static RSAPrivateKey privateKey;
	
	/**
	 * 公钥
	 */
	private static RSAPublicKey publicKey;
	
	/**
	 * 字节数据转字符串专用集合
	 */
	private static final char[] HEX_CHAR = { '0', '1', '2', '3', '4', '5', '6', '7', '8',
			'9', 'a', 'b', 'c', 'd', 'e', 'f' };
	
	/**
	 * 私有构造方法
	 */
	private RSAUtils()
	{
		
	}

	/**
	 * 获取私钥
	 * 
	 * @return 当前的私钥对象
	 */
	public static RSAPrivateKey getPrivateKey()
	{
		return privateKey;
	}

	/**
	 * 获取公钥
	 * 
	 * @return 当前的公钥对象
	 */
	public static RSAPublicKey getPublicKey()
	{
		return publicKey;
	}

	/**
	 * 随机生成密钥对
	 */
	public static void genKeyPair()
	{
		KeyPairGenerator keyPairGen = null;
		try
		{
			keyPairGen = KeyPairGenerator.getInstance("RSA");
		}
		catch (NoSuchAlgorithmException e)
		{
			e.printStackTrace();
		}
		keyPairGen.initialize(1024, new SecureRandom());
		KeyPair keyPair = keyPairGen.generateKeyPair();
		privateKey = (RSAPrivateKey) keyPair.getPrivate();
		publicKey = (RSAPublicKey) keyPair.getPublic();
	}

	/**
	 * 从文件中输入流中加载公钥
	 * 
	 * @param in
	 *            公钥输入流
	 * @throws Exception
	 *             加载公钥时产生的异常
	 */
	public static void loadPublicKey(InputStream in) throws Exception
	{
		try
		{
			BufferedReader br = new BufferedReader(new InputStreamReader(in));
			String readLine = null;
			StringBuilder sb = new StringBuilder();
			while ((readLine = br.readLine()) != null)
			{
				if (readLine.charAt(0) == '-')
				{
					continue;
				}
				else
				{
					sb.append(readLine);
					sb.append('\r');
				}
			}
			loadPublicKey(sb.toString());
		}
		catch (IOException e)
		{
			throw new Exception("公钥数据流读取错误");
		}
		catch (Exception e)
		{
			throw new Exception("公钥输入流为空");
		}
	}

	/**
	 * 从字符串中加载公钥
	 * 
	 * @param publicKeyStr
	 *            公钥数据字符串
	 * @throws Exception
	 *             加载公钥时产生的异常
	 */
	public static void loadPublicKey(String publicKeyStr) throws Exception
	{
		try
		{
			BASE64Decoder base64Decoder = new BASE64Decoder();
			byte[] buffer = base64Decoder.decodeBuffer(publicKeyStr);
			KeyFactory keyFactory = KeyFactory.getInstance("RSA");
			X509EncodedKeySpec keySpec = new X509EncodedKeySpec(buffer);
			publicKey = (RSAPublicKey) keyFactory.generatePublic(keySpec);
		}
		catch (NoSuchAlgorithmException e)
		{
			throw new Exception("无此算法");
		}
		catch (InvalidKeySpecException e)
		{
			throw new Exception("公钥非法");
		}
		catch (IOException e)
		{
			throw new Exception("公钥数据内容读取错误");
		}
		catch (Exception e)
		{
			throw new Exception("公钥数据为空");
		}
	}

	/**
	 * 从文件中加载私钥
	 * 
	 * @param keyFileName
	 *            私钥文件名
	 * @return 是否成功
	 * @throws Exception
	 */
	public static void loadPrivateKey(InputStream in) throws Exception
	{
		try
		{
			BufferedReader br = new BufferedReader(new InputStreamReader(in));
			String readLine = null;
			StringBuilder sb = new StringBuilder();
			while ((readLine = br.readLine()) != null)
			{
				if (readLine.charAt(0) == '-')
				{
					continue;
				}
				else
				{
					sb.append(readLine);
					sb.append('\r');
				}
			}
			loadPrivateKey(sb.toString());
		}
		catch (IOException e)
		{
			throw new Exception("私钥数据读取错误");
		}
		catch (NullPointerException e)
		{
			throw new Exception("私钥输入流为空");
		}
	}

	public static void loadPrivateKey(String privateKeyStr) throws Exception
	{
		try
		{
			BASE64Decoder base64Decoder = new BASE64Decoder();
			byte[] buffer = base64Decoder.decodeBuffer(privateKeyStr);
			PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(buffer);
			KeyFactory keyFactory = KeyFactory.getInstance("RSA");
			privateKey = (RSAPrivateKey) keyFactory.generatePrivate(keySpec);
		}
		catch (NoSuchAlgorithmException e)
		{
			throw new Exception("无此算法");
		}
		catch (InvalidKeySpecException e)
		{
			throw new Exception("私钥非法");
		}
		catch (IOException e)
		{
			throw new Exception("私钥数据内容读取错误");
		}
		catch (Exception e)
		{
			throw new Exception("私钥数据为空");
		}
	}

	/**
	 * 加密过程
	 * 
	 * @param publicKey
	 *            公钥
	 * @param plainTextData
	 *            明文数据
	 * @return
	 * @throws Exception
	 *             加密过程中的异常信息
	 */
	public static byte[] encrypt(RSAPublicKey publicKey, byte[] plainTextData) throws Exception
	{
		if (publicKey == null)
		{
			throw new Exception("加密公钥为空, 请设置");
		}
		Cipher cipher = null;
		try
		{
			cipher = Cipher.getInstance("RSA", new BouncyCastleProvider());
			cipher.init(Cipher.ENCRYPT_MODE, publicKey);
			byte[] output = cipher.doFinal(plainTextData);
			return output;
		}
		catch (NoSuchAlgorithmException e)
		{
			throw new Exception("无此加密算法");
		}
		catch (NoSuchPaddingException e)
		{
			e.printStackTrace();
			return null;
		}
		catch (InvalidKeyException e)
		{
			throw new Exception("加密公钥非法,请检查");
		}
		catch (IllegalBlockSizeException e)
		{
			throw new Exception("明文长度非法");
		}
		catch (Exception e)
		{
			throw new Exception("明文数据已损坏");
		}
	}

	/**
	 * 解密过程
	 * 
	 * @param privateKey
	 *            私钥
	 * @param cipherData
	 *            密文数据
	 * @return 明文
	 * @throws Exception
	 *             解密过程中的异常信息
	 */
	public static byte[] decrypt(RSAPrivateKey privateKey, byte[] cipherData) throws Exception
	{
		if (privateKey == null)
		{
			throw new Exception("解密私钥为空, 请设置");
		}
		Cipher cipher = null;
		try
		{
			cipher = Cipher.getInstance("RSA", new BouncyCastleProvider());
			cipher.init(Cipher.DECRYPT_MODE, privateKey);
			byte[] output = cipher.doFinal(cipherData);
			return output;
		}
		catch (NoSuchAlgorithmException e)
		{
			throw new Exception("无此解密算法");
		}
		catch (NoSuchPaddingException e)
		{
			e.printStackTrace();
			return null;
		}
		catch (InvalidKeyException e)
		{
			throw new Exception("解密私钥非法,请检查");
		}
		catch (IllegalBlockSizeException e)
		{
			throw new Exception("密文长度非法");
		}
		catch (Exception e)
		{
			throw new Exception("密文数据已损坏");
		}
	}

	/**
	 * 字节数据转十六进制字符串
	 * 
	 * @param data
	 *            输入数据
	 * @return 十六进制内容
	 */
	public static String byteArrayToString(byte[] data)
	{
		StringBuilder stringBuilder = new StringBuilder();
		for (int i = 0; i < data.length; i++)
		{
			// 取出字节的高四位 作为索引得到相应的十六进制标识符 注意无符号右移
			stringBuilder.append(HEX_CHAR[(data[i] & 0xf0) >>> 4]);
			// 取出字节的低四位 作为索引得到相应的十六进制标识符
			stringBuilder.append(HEX_CHAR[(data[i] & 0x0f)]);
			if (i < data.length - 1)
			{
				stringBuilder.append(' ');
			}
		}
		return stringBuilder.toString();
	}

	public static void main(String[] args)
	{
		// rsaEncrypt.genKeyPair();
		// 加载秘钥
		try
		{
			//rsaUtils.loadPublicKey(RSAUtils.DEFAULT_PUBLIC_KEY);
			//rsaUtils.loadPrivateKey(RSAUtils.DEFAULT_PUBLIC_KEY);
			RSAUtils.loadPublicKey(new FileInputStream(new File("public_key.txt")));
			RSAUtils.loadPrivateKey(new FileInputStream(new File("private_key.txt")));
		}
		catch (Exception e)
		{
			System.out.println(e.getMessage());
		}
		
		// 测试字符串
		String encryptStr = 1234!@#$;
		try
		{
			// 加密
			byte[] cipher = RSAUtils.encrypt(RSAUtils.getPublicKey(), encryptStr.getBytes());
			// 解密
			byte[] plainText = RSAUtils.decrypt(RSAUtils.getPrivateKey(), cipher);
			System.out.println("密文长度:" + cipher.length);
			System.out.println(RSAUtils.byteArrayToString(cipher));
			System.out.println("明文长度:" + plainText.length);
			System.out.println(RSAUtils.byteArrayToString(plainText));
			System.out.println(new String(plainText));
		}
		catch (Exception e)
		{
			System.out.println(e.getMessage());
		}
	}
}

        LZ看过一些关于密码安全的书籍,采用md5withRSA的数字签名的方式,可以很好的解决此类问题,首先使用md5方式生成内容摘要,对内容+salt进行公钥加密,一起发送后台,对密文内容私钥解密后去salt生成md5摘要,对比前台发送的md5摘要值,确保内容是否伪造,对此LZ没有进行实现,但的确是一个完善的安全策略。

      

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值