非对称加密之 - RSA

一、什么是非对称加密算法?

非对称主要是相对于对称加密算法而言的。对称加密算法加解密使用同一个秘钥;非对称算法则有一个公钥和一个私钥,公钥与私钥是一对的。一般,我们利用公钥加密,私钥解密。

RSA是可以双向加密的:私钥加密,公钥解密;公钥加密,私钥解密。

二、非对称加密算法的特点

算法强度复杂、安全性依赖于算法与密钥。但是由于算法复杂,使得非对称算法加解密速度没有对称算法加解密的速度快。

对称密钥体制中只有一种密钥,并且是非公开的,如果要解密就得让对方知道密钥。所以保证其安全性就是保证密钥的安全。

非对称密钥体制有两种密钥,其中一个是公开的,这样就可以不需要像对称密码那样向对方传输密钥了。因此安全性就大了很多。

算法复杂度:对称密钥 < 非对称密钥
加解密速度:对称密钥 > 非对称密钥
安全性:对称密钥 < 非对称密钥

三、RSA 算法简介

RSA算法1978年就出现了,算法的名字以发明者的名字命名:Ron Rivest, AdiShamir 和Leonard Adleman。
它是第一个既能用于数据加密也能用于数字签名的算法。
它易于理解和操作,也很流行。
这种加密算法的特点主要是密钥的变化,密钥长度越长,安全性越高。

RSA算法的JDK实现默认密钥长度是1024,BC则是2048,密钥长度必须是64的倍数,在512到65536位之间。
有些算法只规定了公钥加密、私钥解密,RSA算法则支持公钥加密、私钥解密.私钥加密、公钥解密。

四、RSA加密算法使用到的接口、类、相关方法和参数

  • KeyPairGenerator 密钥对生成器,用于生成密钥对
  • KeyPair 密钥对,可以用于生成公私钥
  • KeyFactory 密钥工厂,作用是生成密钥,包括公钥和私钥
         - generatePublic()方法 用来生成公钥
         - generatePrivate()方法 用来生成私钥
  • PublicKey 公钥
  • PrivateKey 私钥
  • RSAPublicKey RSA公钥,继承自 PublicKey 与 RSAKey
  • RSAPrivateKey RSA私钥,继承自 PrivateKey 与 RSAKey
  • RSAPublicKeySpec 此类指定 RSA 公用密钥
         - RSAPublicKeySpec(BigInteger modulus,BigInteger publicExponent) 构造方法
             modulus - 系数 , publicExponent - 公用指数
         - getModulus() 方法 返回改系数
         - getPublicExponent() 方法 返回改公用指数
  • RSAPrivateKeySpec 类比 RSAPublicKeySpec 类,构造方法与属性方法类似
  • SecureRandom 强加密随机数生成器
  • Cipher 提供加解密的功能

五、java 实现简单的 RSA 加解密

    public static void rsa() {
        try {
            
            /* step1 : 生成密钥对生成器
             * 
             * 如果默认的提供程序(procider)提供RSA算法的实现,则返回包含该实现的 KeyPairGenerator 的实例。
             * 如果默认包中不存在该算法,则搜索其他包。
             * 
             * 也可以指定其它加密算法提供程序 , 如 BC:
             * KeyPairGenerator.getInstance("RSA", new BouncyCastleProvider());
             */
            KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
            //KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA", new BouncyCastleProvider());
            
            /* step2:初始化密钥对生成器 , 确定密钥长度大小与随机源
             * 
             * 使用默认的参数集合并使用提供程序(以最高优先级安装)的 SecureRandom 实现作为随机源。
             * 如果任何安装的提供程序都不提供 SecureRandom 的实现,则使用系统提供的随机源。
             * 
             * 当然也可以指定随机源,如下:
             * keyPairGenerator.initialize(1024,new SecureRandom(byte[] bytes));
             */
            keyPairGenerator.initialize(1024);
            //keyPairGenerator.initialize(1024,new SecureRandom(DateFormatUtils.format(new Date(),"yyyyMMdd").getBytes()));
            // step3:获取密钥对
            KeyPair keyPair = keyPairGenerator.generateKeyPair();
            // step4:获取密钥
            RSAPublicKey publicKey = (RSAPublicKey)keyPair.getPublic();
            RSAPrivateKey privateKey = (RSAPrivateKey)keyPair.getPrivate();
            // step5:创建 Cipher 对象
            Cipher cipher = Cipher.getInstance("RSA");
            // step6:加密
            cipher.init(Cipher.ENCRYPT_MODE, publicKey);
            byte[] encodeByte = cipher.doFinal(str.getBytes());
            String encodeStr = Hex.encodeHexString(encodeByte);
            System.out.println("加密数据:" + encodeStr);
            // step7:解密
            cipher.init(Cipher.DECRYPT_MODE, privateKey);
            byte[] decodeByte = cipher.doFinal(Hex.decodeHex(encodeStr.toCharArray()));
            String decodeStr = new String(decodeByte);
            System.out.println("解密数据:"+decodeStr);
        } catch (Exception e) {
        }
    }
  • KeyPairGenerator 密钥对生成器在创建时可以指定加密算法提供程序,一般使用 BouncyCastle(简称 BC);也可以使用 JDK 自带的默认加密算法提供程序。
    初始化 KeyPairGenerator 时可以指定随机源 SecureRandom;如果不指定,则使用提供程序(以最高优先级安装)的 SecureRandom 实现作为随机源。
  • SecureRandom 强随机数生成器;在 new 对象时可以传参 byte[] ,参数称为 "种子" 。如果选择无参构造,则自供种子。
    /**
     * 加密方法返回 encodeByteArray ;
     * Hex.encodeHexString 将encodeByteArray数组转换为 encodeString ;
     */
    byte[] encodeByte = cipher.doFinal(str.getBytes());
    String encodeStr = Hex.encodeHexString(encodeByte);
    System.out.println("加密数据:" + encodeStr);
    // step7:解密
    /**
     * Hex.decodeHex(encodeStr.toCharArray()) 将 encodeString 转换为 byteArray ;
     */
    cipher.init(Cipher.DECRYPT_MODE, privateKey);
    byte[] decodeByte = cipher.doFinal(Hex.decodeHex(encodeStr.toCharArray()));
    String decodeStr = new String(decodeByte);

注意:
如果使用 BC 作为算法提供者,BouncyCastleProvider 对象应使用单例模式。
因为这个类里面有较多静态的属性与方法, new一个多一个, GC不回收 。
必然会造成内存上升,直至内存溢出,服务宕机

六、实现简单的 js 加密 + java 解密

1. 后台 java 生成加密模(modulus)公钥指数(exponent)提供给前端
RSAPublicKey publicKey = (RSAPublicKey)keyPair.getPublic();
String modulus = 
    new String(Hex.encodeHex(publicKey.getModulus().toByteArray()));
String exponent = 
    new String(Hex.encodeHex(publicKey.getPublicExponent().toByteArray()));

  • 加密模与公钥指数由 RSA公钥 RSAPublicKey 生成
2. 前端 js 进行 RSA 加密,此例引用第三类库 security.js,示例代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>RSA加密demo</title>
  <script src="./security.js"></script>
  <script>
    window.onload = function () {
      // 加密模
      let modulus = '009792706f90962e037126c0c0deeccd38d55fabcae6470fe049327f' +
        '53f84045882d7217581b6bd86c759d647e585486b0f708cc6cedf1a521752ba9c89bc' +
        'ff1d9d356b802984ca538e0a6f689ace76ee7f5087663637239eefbc95bca7afaabd3e' +
        '22cf7acaeec36cf3febd2240bb25656a2cce0eacee73fb9a95b091d0f038049';
      // 公钥指数
      let exponent = '010001';
      /**
       * 选择使用 encodeURIComponent 方法
       *
       * 若采用 encodeURI 方法编码,java 用 URLDecoder.decode(pwd,"UTF-8") 解码时,
       * 会出现部分符号丢失,例如 '+' ;
       *
       * 使用 encodeURIComponent , 可以避免此问题
       */
        // let strOld = encodeURI('我+Admin@1234');
      let strOld = encodeURIComponent('我+Admin@1234');
      /**
       * 当exponent、modulus为''时可能会导致浏览器卡死,通常出现在ajax异步请求时。
       */
      let publicKey = RSAUtils.getKeyPair(exponent, '', modulus);
      let encodeStr = RSAUtils.encryptedString(publicKey, strOld);

      console.log('原文:' + strOld + '\n密文:' + encodeStr)
    }
  </script>
</head>
<body>

</body>
</html>

  • RSAUtils.getKeyPair(exponent, '', modulus) 方法
    其中,exponent(公钥指数)modulus(加密模) 生成代码参考上面步骤。
  • RSAUtils.encryptedString(publicKey, strOld) 加密方法

注意:

  • exponent、modulus''空时,可能会导致浏览器卡死;
  • 使用 encodeURIComponent 方法对明文进行编码转换,使用 encodeURI必然会造成 特殊符号+丢失(如果原明文中存在+的话);
    后台 java程序可使用URLDecoder.decode(pwd,"UTF-8")进行解码;
3. 后台解密
// step1 : 将密文转换为16进制 byte 数组
byte[] en_data = Hex.decodeHex(encrypttext.toCharArray());
// step2:解密得到 decryptByteArray
Cipher ci = Cipher.getInstance("RSA", DEFAULT_PROVIDER);
ci.init(Cipher.DECRYPT_MODE, privateKey);
byte[] decryptByteArray = ci.doFinal(data);
// step3:将 decryptByteArray 转换为 decryptString 
String decryptString = new String(decryptByteArray);
// step4:将 decryptString 反转
decryptString = StringUtils.reverse(decryptString);
// step5:对 decryptString 进行 URLDecoder 解码,并得到解密后明文
decryptString = URLDecoder.decode(decryptString ,"UTF-8");
哈哈哈

至此,完成的前端 js 进行 RSA 加密,后台 java 解密的流程已经结束。

本博客只是博主方便复习而记录。如有误导,概不负责~

demo 源码下载

  1. 下载连接         密码:bqxq

  2. 或者直接扫下面二维码下载


    扫一扫可直接下载源码
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,902评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,037评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,978评论 0 332
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,867评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,763评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,104评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,565评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,236评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,379评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,313评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,363评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,034评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,637评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,719评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,952评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,371评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,948评论 2 341

推荐阅读更多精彩内容