赞
踩
由于安全原因,需要对用户名密码做加密传输,,考虑几种方式,md5,base64,rsa,由于md5加密不可逆,为了减小代码的改动放弃,base64加密严格来说并不是用来加密的主要是用来方便数据传输的,所以采用rsa非对称加密的方式!
贴代码
java
- public class Test {
-
- public static void main(String[] args) throws Exception {
-
- //生产秘钥对
- //bouncy castle(轻量级密码术包)是一种用于 Java 平台的开放源码的轻量级密码术包
- Provider provider = new BouncyCastleProvider();
- KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA", provider);
- keyPairGen.initialize(1024, new SecureRandom());
- KeyPair keyPair = keyPairGen.generateKeyPair();
- //打印公钥
- System.out.println(keyPair.getPublic());
- //打印私钥
- System.out.println(keyPair.getPrivate());
执行以上代码得到如下
其中private exponent是私钥用户后台解密不能泄露出去
RSA Public Key 中的modulus和public exponent 公钥用于交给前段js加密
前端:引入js文件 security.js
附上security.js地址 https://download.csdn.net/download/zuoyigehaizei/11329183
html页面中导入此js,以下为js加密代码
- //rsa 私钥
- var password = 'ceshi';
- var username = 'user_01';
- var key = RSAUtils.getKeyPair('10001', '', '9a12794abc04faa6f78350fe0e73403994eb58199d030477f97d6acd9825e9fc13b230edcb68741d1f87b257506e909df0926ad9c12ab99b40c40087222ed0686c133bf2f7f01c005c9b9f01a9891527afc41364e0d24ce8924237f0868fc87770397f0c4386dbfe178de40273adcf6a3de05d60dc4abd945a14ed257af6e033');
- //加密后的密码
- password = RSAUtils.encryptedString(key, password);
- console.log("password:" + password);
- //加密后的用户名
- username = RSAUtils.encryptedString(key,username);
- console.log("username" + username);
java后台解密
public static void main(String[] args) throws Exception { //rsa js加密,后台java解密 //RSA Private CRT Key的modulus String hexModulus = "9a12794abc04faa6f78350fe0e73403994eb58199d030477f97d6acd9825e9fc13b230edcb68741d1f87b257506e909df0926ad9c12ab99b40c40087222ed0686c133bf2f7f01c005c9b9f01a9891527afc41364e0d24ce8924237f0868fc87770397f0c4386dbfe178de40273adcf6a3de05d60dc4abd945a14ed257af6e033"; //RSA Private CRT Key的private exponent String hexPrivateExponent = "5131951bee53cd67ba1e48cb6a00108387d832786507170ec6baf252e070728b7631bc99444d8a8b62775763ae2e6625e586b3aa87a2a5678cccfdde390464ad1d4e2ef44be958238f3fae52ee988b3190a6e61c62512de90393ee599fb528726d74d987eb174732224245de0dc3701ccf3596d9ad9ecb919fbbf41c8241b781"; // js页面加密后的密码和用户名 String passwrod = "03f87bada52fc13c892b39498cb06d4641eb0aefb92d7e243cdd7683c0aab29424954cb18cc53e92c589f9a1096cc8dbe5a75429321ba177f1804a5e23fd966d73dbfa5a6bbcdc6dc0cc231c342fec317c27c72cf7396677da2a424e808b30eaa173bf5c7247cefda4b98fb89e13d53f44e432f6e30948877616240bfbb647c6"; String username = "5354f3480cce53432b29fd212577951dd8b0fc6a002e440f96d7519a5233e4040fe9ad2d27d0fe8b20dfe390a1d21b0cf362818df9706c5f7f49dada15bc032aab11aa86a8d99ed41a21796936cea7e8391e5712fe64e6ccc5d66e4f1f4d3f811975615f0272eb7434f196bcf9740e389af74b0ef9ff88b14d71251f38be5234"; Provider provider = new BouncyCastleProvider(); KeyFactory keyFac = KeyFactory.getInstance("RSA", provider); RSAPrivateKeySpec priKeySpec = new RSAPrivateKeySpec(new BigInteger(hexModulus, 16), new BigInteger(hexPrivateExponent, 16)); // 生成用于解密的私钥 RSAPrivateKey pk = (RSAPrivateKey) keyFac.generatePrivate(priKeySpec); // 解密 Cipher cipher = Cipher.getInstance("RSA", provider); cipher.init(2, pk); byte[] pwd = cipher.doFinal(Hex.decodeHex(passwrod.toCharArray())); byte[] usn = cipher.doFinal(Hex.decodeHex(username.toCharArray())); System.out.println(StringUtils.reverse(new String(pwd))); System.out.println(StringUtils.reverse(new String(usn)));
运行结果,解密成功
题外话:rsa加解密效率比较慢,可以做一些小改动来提升效率,上代码,测试过户解密大概在3毫秒,改动之前解密一次需要200毫秒左右!
static Cipher cipher; static RSAPrivateKey pk; static org.slf4j.Logger log = LoggerFactory.getLogger(LogonAction.class); //传入加密后的密码,进行解密 pwOrUn加密后的密码 public String passwordAndUsernameRsaDecode(String pwOrUn) { try { long l = System.currentTimeMillis(); pk = getPk(); // 解密 cipher = getCipher(); cipher.init(2, LogonAction.pk); byte[] rawPasswordByte = cipher.doFinal(Hex.decodeHex(pwOrUn.toCharArray())); pwOrUn = StringUtils.reverse(new String(rawPasswordByte)); long l1 = System.currentTimeMillis(); log.info("解密成功,用时" + (l1-l)); }catch (Exception e){ throw new RuntimeException("Rsa密码解密失败"); } return pwOrUn; } public static synchronized RSAPrivateKey getPk() throws Exception{ if(pk == null){ log.info("私钥为空,生成私钥"); String hexModulus = "c6e442535e1dd6968e4ccd7735299278d989cb938a2f97c1081c4e6796895a3063510592e2e90ed427d5a604428ce46391dcb2ba6b5f4a86af1347237d1de489a0dc2f68a1f9a265d1ec350fccd8a76be5004211cee5bf05a083afa17cf335871b141e5c4329f69d1a3546613e0fa7833b7a253c460e5bb0c075dacfccfd6d0d"; String hexPrivateExponent = "1ce625c15c66146a983d82cd493c95242ae35603ba73b4f810682c838d0f4bbb242d5c2bc9cee12b41bff1108b369885fadaa05f0f68bafeb915445e5cd006645eb816d9a8d89b155aeb8478a60325cf4d7e69c12b3076a4cf31b8c24530e7f13533826bc7e87ddf65b7ccc65951bd7c34238b9f08a1e8a1c2c4dd762c50309"; // 这就是上面html输出的密文 Provider provider = new BouncyCastleProvider(); KeyFactory keyFac = KeyFactory.getInstance("RSA", provider); RSAPrivateKeySpec priKeySpec = new RSAPrivateKeySpec(new BigInteger(hexModulus, 16), new BigInteger(hexPrivateExponent, 16)); // 生成用于解密的私钥 pk = (RSAPrivateKey) keyFac.generatePrivate(priKeySpec); } return pk; } public static synchronized Cipher getCipher() throws NoSuchAlgorithmException, NoSuchPaddingException { if (cipher == null) { cipher = Cipher.getInstance("RSA", new BouncyCastleProvider()); } return cipher; }
以上改动后的方法经过线上测试存在问题:问题如下 :
java.lang.ArrayIndexOutOfBoundsException: too much data for RSA block
问题描述:加密后的密码,进行解密,会出现同样的加密密码有时会解密成功,有时解密失败的情况!解密失败时报以上错误!
查阅资料,出现此错误有两种可能性
1,加密的密码过长导致
2,多线程引起的
分析之后排除1,那么就是多线程引起的问题
原因:javax.crypto.Cipher 是有状态的,不要把 Cipher 当做一个静态变量,除非你的程序是单线程的,也就是说你能够保证同一时刻只有一个线程在调用 Cipher。否则你可能会遇到 java.lang.ArrayIndexOutOfBoundsException: too much data for RSA block 异常。
解决方法:
public synchronized String passwordAndUsernameRsaDecode(String pwOrUn) {
try {
long l = System.currentTimeMillis();
pk = getPk();
// 解密
cipher = getCipher();
cipher.init(2, LogonAction.pk);
byte[] rawPasswordByte = cipher.doFinal(Hex.decodeHex(pwOrUn.toCharArray()));
pwOrUn = StringUtils.reverse(new String(rawPasswordByte));
long l1 = System.currentTimeMillis();
log.info("解密成功,用时" + (l1-l));
}catch (Exception e){
throw new RuntimeException("Rsa密码解密失败");
}
return pwOrUn;
}
在此方法上加上同步,解决问题。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。