使用rsa对用户名,密码加解密传输(js前端加密,java后台解密)

由于安全原因,需要对用户名密码做加密传输,,考虑几种方式,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;
}

在此方法上加上同步,解决问题。

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值