乡下人产国偷v产偷v自拍,国产午夜片在线观看,婷婷成人亚洲综合国产麻豆,久久综合给合久久狠狠狠9

  • <output id="e9wm2"></output>
    <s id="e9wm2"><nobr id="e9wm2"><ins id="e9wm2"></ins></nobr></s>

    • 分享

      前后端API交互數(shù)據(jù)加密——AES與RSA混合加密完整實(shí)例

       hncdman 2022-05-13

        前言

         前段時(shí)間看到一篇文章講如何保證API調(diào)用時(shí)數(shù)據(jù)的安全性(傳送門:https://blog.csdn.net/ityouknow/article/details/80603617),文中講到利用RSA來(lái)加密傳輸AES的秘鑰,用AES來(lái)加密數(shù)據(jù),并提供如下思路:

        說(shuō)人話就是前、后端各自生成自己的RSA秘鑰對(duì)(公鑰、私鑰),然后交換公鑰(后端給前端的是正常的明文公鑰,前端給后端的是用后端公鑰加密后的密文公鑰;PS:其實(shí)我覺(jué)得直接交換兩個(gè)明文公鑰就行了),后端生成AES的明文key,用明文key進(jìn)行AES加密得到密文數(shù)據(jù),用前端的公鑰進(jìn)行RSA加密得到密文key,API交互時(shí)并將密文數(shù)據(jù)與密文key進(jìn)行傳輸,前端用自己的私鑰進(jìn)行RAS解密的到明文key,用明文key進(jìn)行AES解密得到明文數(shù)據(jù);前端給后端發(fā)送數(shù)據(jù)時(shí)同理,這樣一來(lái),傳輸?shù)臄?shù)據(jù)都是密文,且只有秘鑰才能解密

        可惜這篇博客只提供了思路,但并沒(méi)有具體的代碼,我們?cè)诰W(wǎng)上查找一下資料,開(kāi)始生擼代碼,實(shí)現(xiàn)一個(gè)前后端API交互數(shù)據(jù)加密——AES與RSA混合加密,并應(yīng)用到項(xiàng)目中

        后端加、解密

        從網(wǎng)上查找工具類,再進(jìn)行改造

        先引入Base64工具類

              <!-- Base64編碼需要  -->
              <dependency>
                  <groupId>org.apache.directory.studio</groupId>
                  <artifactId>org.apache.commons.codec</artifactId>
                  <version>1.8</version>
              </dependency>

        AES

       AesUtil

        RSA

       RsaUtil

        簡(jiǎn)單測(cè)試

        AES對(duì)稱加密、解密簡(jiǎn)單測(cè)試

        1、字符串

      復(fù)制代碼

         public static void main(String[] args) {        //16位
              String key = "MIGfMA0GCSqGSIb3";        //字符串
              String str = "huanzi.qch@qq.com:歡子";        try {            //加密
                  String encrypt = AesUtil.encrypt(str, key);            //解密
                  String decrypt = AesUtil.decrypt(encrypt, key);
      
                  System.out.println("加密前:" + str);
                  System.out.println("加密后:" + encrypt);
                  System.out.println("解密后:" + decrypt);
              } catch (Exception e) {
                  e.printStackTrace();
              }
          }

      復(fù)制代碼

      加密前:huanzi.qch@qq.com:歡子
      加密后:dXPRtcdHPQSTwxLnmixkaSvNfGHhg5Gz8sGTtiqCpPo=解密后:huanzi.qch@qq.com:歡子

        2、復(fù)雜對(duì)象

      復(fù)制代碼

            
              String key = "MIGfMA0GCSqGSIb3"
              ImsUserVo userVo = "123456""111111"
                  String encrypt =
                  String decrypt ="加密前:" +"加密后:" +"解密后:" +

      復(fù)制代碼

      加密前:ImsUserVo(id=null, userName=123456, password=111111, nickName=null, gender=null, avatar=null, email=null, phone=null, sign=null, createdTime=null, updataTime=null)
      加密后:AXv8ewfY+gbuZ/dCmGAxngLry+Idlp1NKZ8yyf9+bmrBggUBo3b+e4XRwMAE/DP+vFS2HpgeYQTrZM1ECjo01uvZ/T6lY7b2C6L8PTotYHQyJM3kOs+YNXL/uyvFZ2EICSQWhmM1XX+g0juHLCbgQDMNXc56S/7eH2p+su1+CTMygUBCF0U/gZaSzqylqujTb3sg7q4xMuxCQ6ne6xmL3ebjanOLeMJHypTDy1rlJTw=解密后:ImsUserVo(id=null, userName=123456, password=111111, nickName=null, gender=null, avatar=null, email=null, phone=null, sign=null, createdTime=null, updataTime=null)

        RAS非對(duì)稱加密、解密簡(jiǎn)單測(cè)試

        1、字符串的RSA公鑰加密、私鑰解密

      復(fù)制代碼

          public static void main(String[] args) {        //字符串
              String str = "huanzi.qch@qq.com:歡子";        try {
                  System.out.println("私鑰:" + RsaUtil.getPrivateKey());
                  System.out.println("公鑰:" + RsaUtil.getPublicKey());            //公鑰加密
                  byte[] ciphertext = RsaUtil.encryptByPublicKey(str.getBytes(), RsaUtil.getPublicKey());            //私鑰解密
                  byte[] plaintext = RsaUtil.decryptByPrivateKey(ciphertext, RsaUtil.getPrivateKey());
      
                  System.out.println("公鑰加密前:" + str);
                  System.out.println("公鑰加密后:" + Base64.encodeBase64String(ciphertext));
                  System.out.println("私鑰解密后:" + new String(plaintext));
              } catch (Exception e) {
                  e.printStackTrace();
              }
          }

      復(fù)制代碼

      私鑰:MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBANG08b2L0Hk1QCJXyTUI6A4CqW+KENCedZyJCYMteZ/vx93KeYZbPShhI3IWJJtj9U+ibiAVRjzmikI9lkKdgnCaOgTmEZis2RWgLzhcOpSqdp/J6d+YtmCD6UDeO3E6QPyfVv9d3qPrqaYUCxi7CmouzVaa/cJqrfYB7qGYt3u5AgMBAAECgYBbovQX3ebFcG2MFExKLpAovyUHJo/eeb/vHTrY5aBGMWNnGbks6uW4pWn1ypNIi8+AcvwobON6bUtxUrQ8e9OpUlDYTAAqDE8JvJoRC3theHpJbkHCdDLeNnz1EizUwxfe3X3IVwEYd29C00WXt0rUW2D/Fsa7ECp08taeV+ukAQJBAOyfO8opGp8t40bbyMRVsIR2zK19rN6Kd/NGvjjW/7BPgzDJZsybcN6e0AuhFaWTyHNSonpDztEQ0VWhF0mHokECQQDi4W9xCmzQf0l8mgXUP2IDY5YtQN9g9vL51qEwpcxhHxCCcid62R0y6T2GnRTmkEpSwPYZ2EZQrKtpGiEk4wt5AkAhwwqd6sWApuSB7MQ1t2BLVkQYERGEY0+AJ7zmkU7EUmQOpv4C/b7aFODsd9yF1pNIWScTuO8eh37G8AhJlo/BAkEAuIHfME3rGlA5whQ8I1T8b4cgjWLRhrit9tI+OiLLqDwsH/mX88b3gPy/pWa/pZW4a74zJeeFn3wc1heC1s2x+QJAXHVf9fZaFwDlD6nD3x0Sgu8Mdp8tsfdz2wIkvjtANc+eojkfxwdZd6PKWgmiPTLKNNqbPaLgtU74WVAnlpSgsw==公鑰:MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDRtPG9i9B5NUAiV8k1COgOAqlvihDQnnWciQmDLXmf78fdynmGWz0oYSNyFiSbY/VPom4gFUY85opCPZZCnYJwmjoE5hGYrNkVoC84XDqUqnafyenfmLZgg+lA3jtxOkD8n1b/Xd6j66mmFAsYuwpqLs1Wmv3Caq32Ae6hmLd7uQIDAQAB
      公鑰加密前:huanzi.qch@qq.com:歡子
      公鑰加密后:MQa65DyVZg/L8SBilLX1yUiajtiTBqUFpQ/qlrSRyMGCubylbp9KisowRghPxk9BuI3+ea/4QpidIZKJaZAbQQ+ZKyslSTk3nm6H+0BF9pMA7BUeC33xHSy+3lJrNOr5S+Vup1Oir3Nu8i2vJYQV1pPkB5+zyUVEcNLD3xr/eNQ=私鑰解密后:huanzi.qch@qq.com:歡子

        2、復(fù)雜對(duì)象的RSA公鑰加密、私鑰解密

      復(fù)制代碼

          public static void main(String[] args) {        //復(fù)雜對(duì)象
              ImsUserVo userVo = new ImsUserVo();
              userVo.setUserName("123456");
              userVo.setPassword("111111");        try {
                  System.out.println("私鑰:" + RsaUtil.getPrivateKey());
                  System.out.println("公鑰:" + RsaUtil.getPublicKey());            //公鑰加密
                  byte[] ciphertext = RsaUtil.encryptByPublicKey(userVo.toString().getBytes(), RsaUtil.getPublicKey());            //私鑰解密
                  byte[] plaintext = RsaUtil.decryptByPrivateKey(ciphertext, RsaUtil.getPrivateKey());
      
                  System.out.println("公鑰加密前:" + userVo.toString());
                  System.out.println("公鑰加密后:" + Base64.encodeBase64String(ciphertext));
                  System.out.println("私鑰解密后:" + new String(plaintext));
              } catch (Exception e) {
                  e.printStackTrace();
              }
          }

      復(fù)制代碼

      私鑰:MIICeAIBADANBgkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBAL6gSKs2G4iFrhPo0aLELfGzsCAaB5hztvclD9J2hZT2KXfs6S5JwZ0RWRR28rqHm0e2RNW3fzYyOLvSoq93n/TRAkmXBbVia3BCTrSzLPrKFY8JvLyXqbrV0NrxywY+4ZlgR5R+scWaj3LtUR63sSXb5ddmOg9XctrWBGvsKrNJAgMBAAECgYEAoql9OPPDzNxdbcnGUQDcP5pYGRx9DL75Cq2KccoHNNRVEGuNkp0HZLLv84GIoFikzS2gUUnyeFmkhck4X0hRqYpCo9DwRsBgBpqn+4ebjSu4bd3lG5KCAtMaPC5sAbznY1uuuJnUdul3p9PuF7AmFTsoFFB4YvstvkRna5ZPFA0CQQDfpxPYVpZjOsgng7187vEpFa9vlQxmyamvJ2iAeFLRHCqJwlq4VYqJkgr08SE1XCBqSVhXkLyIPAtdeqxU0iFLAkEA2jJfKVSy4I/BHmk/rdpw7InQ3ERBc/a09t2ZiI3bqtnobTIf/sMZEWPeMkY83RrWL9ZQvMNDa843cans3bm1OwJAIdipGi5QaAf3TnOTc5q9iFgtypcl31BZi5ZNLFQJRHgcv+hXzlmzs4oUemkbe3XLugoLgoT24y8jESyFc/iw7QJBAJdR26EENlF6IIoAn8Ln/Oxt30UCqQnNDE8v+2wyRSdFm+Uun/XEQ7xFsDDZeRg1pljinndqS3WWO+k92SEjy0UCQQCr5UsIMBAjpGCYXeXrRWYoYdfI6+R20I+uWoOGzoly+KK4ixqMLFuimEwrmXhYnJMzvVHfbLsoogBv9NOP9ffH
      公鑰:MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC+oEirNhuIha4T6NGixC3xs7AgGgeYc7b3JQ/SdoWU9il37OkuScGdEVkUdvK6h5tHtkTVt382Mji70qKvd5/00QJJlwW1YmtwQk60syz6yhWPCby8l6m61dDa8csGPuGZYEeUfrHFmo9y7VEet7El2+XXZjoPV3La1gRr7CqzSQIDAQAB
      公鑰加密前:ImsUserVo(id=null, userName=123456, password=111111, nickName=null, gender=null, avatar=null, email=null, phone=null, sign=null, createdTime=null, updataTime=null)
      公鑰加密后:Un+1m/CbpzVkkxYrwNOWyEXqpsawxcdv4p3G+9b+SQRiC/THL8YG+IvqFCHnxizzYGB9LEvLbQxw72JB0Wlo1+/SvX7AJb2h0ddpvVUkPjmtXNo073SV1zMK+9NTCJUMMoHu/TIptxRbVxlBoGMHa+jq8h2y3RUOPtx/9zhBWlQmzZEifv0MjgAhKX5ucExYfXctcAVGHL959+TwKqKQmTENw5o0ElksaA0KIF+4L7RvpWVSqZT1Y4O2gMP9ALjamCx6ziRcmk4b4Q5Goph0nmw6nA387qVi3Vz6rQHrIpL0HT5OSiz1O7+2L3N0Him2IZeAgg3EZCi5xTGl54jGEw==私鑰解密后:ImsUserVo(id=null, userName=123456, password=111111, nickName=null, gender=null, avatar=null, email=null, phone=null, sign=null, createdTime=null, updataTime=null)

        如需使用更多加密、填充方式,引入

      <dependency>
          <groupId>org.bouncycastle</groupId>
          <artifactId>bcprov-jdk16</artifactId>
          <version>1.46</version></dependency>

        加解密的時(shí)候改成

      Cipher cipher = Cipher.getInstance(ALGORITHMS ,new BouncyCastleProvider());

        重要更新

        2020-05-21更新

        重要Bug修復(fù):后端加解密中,不能在代碼里new BouncyCastleProvider(),JceSecurity. getVerificationResult內(nèi)部會(huì)進(jìn)行判斷,如果是新值,則每次都會(huì)put到map中,導(dǎo)致內(nèi)存緩便被耗盡,程序假死崩潰(參考博客:https://www./A/lk5aQo7451/ )

       

         應(yīng)該改成,我已經(jīng)在開(kāi)源項(xiàng)目改了,博客上面之前貼出來(lái)的代碼我就不改了,具體代碼大家去開(kāi)源項(xiàng)目查看看吧

       

         前端加、解密

         AES我們采用CryptoJS,是一個(gè)標(biāo)準(zhǔn)和安全加密算法的JavaScript庫(kù),它的AES加密支持AES-128、AES-192和AES-256。下載或查看詳情介紹請(qǐng)戳官網(wǎng)地址

        GitHub地址:https://github.com/brix/crypto-js

        官網(wǎng)地址:https://code.google.com/archive/p/crypto-js/

        RSA我們采用JSEncrypt,它是一個(gè)很好用的RSA加密算法的JavaScript庫(kù),使用PKCS#1進(jìn)行填充,加解密使用方式很簡(jiǎn)單,具體的介紹或者下載請(qǐng)移步官網(wǎng)

        GitHub地址:https://github.com/travist/jsencrypt

        官網(wǎng)地址:http:///jsencrypt/

        下載下來(lái)后我們?cè)陧?xiàng)目頭部head.html引入,并新建兩個(gè)小工具類

        AES

       aesUtil

        RSA

       rsaUtil

        簡(jiǎn)單測(cè)試

        AES對(duì)稱加密、解密簡(jiǎn)單測(cè)試

        1、字符串

      復(fù)制代碼

      //字符串let text = "huanzi.qch@qq.com:歡子";//keylet genKey = aesUtil.genKey();//key加密let ciphertext = aesUtil.encrypt(text,genKey);//key解密let plaintext = aesUtil.decrypt(ciphertext,genKey);
      
      console.log("key:");console.log(genKey);
      console.log("加密前:");console.log(text);
      console.log("key加密后:" + ciphertext);
      console.log("key解密后:");console.log(plaintext);

      復(fù)制代碼

      key:q99IsnEuryk1ZvgX
      加密前:huanzi.qch@qq.com:歡子
      key加密后:aZn58GtEj9Is0hNWbJoqpRD6RkiBVPCHOvva3Xq2PYo=key解密后:huanzi.qch@qq.com:歡子

        2、復(fù)雜對(duì)象

      復(fù)制代碼

      //復(fù)雜對(duì)象let user = {username: "歡子", password: 123456, remark: "abcd!@#$:"};//keylet genKey = aesUtil.genKey();//key加密let ciphertext = aesUtil.encrypt(user,genKey);//key解密let plaintext = aesUtil.decrypt(ciphertext,genKey);
      
      console.log("key:");console.log(genKey);
      console.log("加密前:");console.log(user);
      console.log("key加密后:" + ciphertext);
      console.log("key解密后:");console.log(plaintext);

      復(fù)制代碼

      key:e6gzizHIpDfc6hbg
      加密前:{username: "歡子", password: 123456, remark: "abcd!@#$:"}
      key加密后:YdNw5AwteEp8WZs5xMv0YiGcXvX81P9MCLOvroHjfLUyQV/GwJ6obRqi4DT2ucJy8DWrKueOzLGLSQXUVhAgIA==
      key解密后:{username: "歡子", password: 123456, remark: "abcd!@#$:"}

        RAS非對(duì)稱加密、解密簡(jiǎn)單測(cè)試

        1、字符串的RSA公鑰加密、私鑰解密

      復(fù)制代碼

      //普通字符串let text = "huanzi.qch@qq.com:歡子";//秘鑰對(duì)let keyPair = rsaUtil.genKeyPair();//公鑰加密let ciphertext = rsaUtil.encrypt(text,keyPair.publicKey);//私鑰解密let plaintext = rsaUtil.decrypt(ciphertext,keyPair.privateKey);
      
      console.log("秘鑰:");console.log(keyPair.privateKey);
      console.log("公鑰:" + keyPair.publicKey);
      console.log("加密前:" + text);
      console.log("公鑰加密后:" + ciphertext);
      console.log("解密后:" + plaintext);

      復(fù)制代碼

      秘鑰:MIICXQIBAAKBgQDtBKNg9NJ0+mMWq+99geoi32t+xkbJuvQ4Wr7x8I+zGT8xiG+jG+OAuSjvi5yA7IEMMAj8Y8vS7IPPo2mAr/PH0DsNiHMATJm8mNIEDzfP4WOFOdidzqP+6/9iOLMfe4cHtGq+kdX7QPx4uabnIXAREnR4nVl5Mtxf+vEHXGmEPwIDAQABAoGBANH92gJ85jld3YyoqHa6M4bSC5s2cGEqklWbkLEqQSacp7BrAP2yJ85UPkB9oRtYbr0tkciLYnptshq03TR2r7QT5+ovb5KJ2MQExTXk8GZTO/5sSqD0zwA9SESlAmWj8yc49p5Tk0h5UYFgsRATTer1n1llziryXa4QMiIfKrmBAkEA+za3DryZnMyNqre6Kx+FYsFVvIbHRU7tJ5LOiZ43Vl0DXq44zmeNQeh6MzfH6sc0Avu1c61/+KNDVf23yfVt7QJBAPGIr0GokOZ+L0sttiEoQSq/dBdYaSCBfTht+rA/9ie8RgcFJkYj4h/6RPzdYIRWDco5RzI+oPnZmFC4rfPjFVsCQHE2XFMw3c2TRfj86dKLVxKFbL0UxHNQuYIPIDNW8TtjmaQuwf0LH9bnDUNNzTPaaG87vq+OLlEAStVTDWPfzpUCQDVJvbjTstxXfKGufR9FnVMMGFXKOK9mQjU/9m4KPom3vQ9xcGdLJWl+stfDE7c+sR4rkuyf6q4U9sjgZeiH8j8CQQCfaxXfxiDJPztUDm1AKI6uDwz4P4eiYRbbbQ5x+iQSunHbq0Y7U9UUkWcLw0xDhReHEYkFuOeiBj2ViAPJz1r0
      公鑰:MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDtBKNg9NJ0+mMWq+99geoi32t+xkbJuvQ4Wr7x8I+zGT8xiG+jG+OAuSjvi5yA7IEMMAj8Y8vS7IPPo2mAr/PH0DsNiHMATJm8mNIEDzfP4WOFOdidzqP+6/9iOLMfe4cHtGq+kdX7QPx4uabnIXAREnR4nVl5Mtxf+vEHXGmEPwIDAQAB
      加密前:huanzi.qch@qq.com:歡子
      公鑰加密后:aUkUMYC7lF8M1xzcx5ZEdc0DQt4FrqvWqEnD30raV++j7rwsfEcyXpPmeF1g2LR86FVG3oxgdTptorkwUDSXB3Tv4av7toGg7Zcf9l1vs5WQX7kCDTitwBVwyBNTZq22xed1J/LAkDujDav6tUJHdMmRKYVe2NeTswvWLOqWWW4=
      解密后:huanzi.qch@qq.com:歡子

        2、復(fù)雜對(duì)象的RSA公鑰加密、私鑰解密

      復(fù)制代碼

      //復(fù)雜對(duì)象let user = {username:"歡子",password:123456,remark:"abcd!@#$:"};//秘鑰對(duì)let keyPair = rsaUtil.genKeyPair();//公鑰加密let ciphertext = rsaUtil.encrypt(user,keyPair.publicKey);//私鑰解密let plaintext = rsaUtil.decrypt(ciphertext,keyPair.privateKey);
      
      console.log("秘鑰:");console.log(keyPair.privateKey);
      console.log("公鑰:" + keyPair.publicKey);
      console.log("加密前:" + user);
      console.log("公鑰加密后:" + ciphertext);
      console.log("解密后:" + plaintext);

      復(fù)制代碼

      秘鑰:MIICXAIBAAKBgQCsAE5TN8kD7U4mFyxBzN1w23Rkf4K8MQ3B0bCZE5crjYp81eUtWrfUM+zLPmF9e1P/ws2yGHvL6mueU9PxtDJn5rSLsQBSxIkN0QB/nq76S4uh2Nrmmrjomejy5LqXnTVbEoIW2RTFBzyMWy4AjQY6P2pAJ8zCagvcdYcweUIqMQIDAQABAoGAbwrLhkIvjk938nNnaRufoqqrW+5OMrzgis6bWlghckawr6NPj5ZPs7nKF/Sv79jdA/N55I6V7bHrxI2N+S9Ckm2ygv8nNYimSjzspR48SqVRuH/xYmQQ9hi8Iy4dTlCMud34oXsV2sYI5tEn7f3bypOVfJa6kHSqxe1PIQTxirkCQQDjmHOp2JMcltpL+639nnNgZ2U06cRhPHX+tcGTIgoqu1Sqp0bKH7QuUF9WPNWxHrbYY5+s5jnnhTTwQZg74atTAkEAwXelo0JLYHpML7+sLs8aUzitRJXjkW3dY4JPf1wLTNLbawvi4KA/6NA3jx7kCD6KzM7vsWWsRgArrUWa1Dbn6wJARY5pAuZyh1E/I+umEBWl0zemQZaT8tekhBSONWY4zzhzNrhqtQkdau4bROLQuBHX9af0u8WcuroGJMsXOG3OiwJBALc91OPJ8cziaPC80Z/QRvXV877HXTCsZ4lNrnBJxOYxvOMp8eyhu4aOWGE1d/QbEKolwj86tq3ikXvfNmOT0ZsCQBkfviB7CKdHCrUCnpAK0upa9x8uFraomDNxFP/HwTFPSOPGhA5pgAzJgygSu2hpEwFFIwfC3E+pQ2EAhoeIcdw=公鑰:MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCsAE5TN8kD7U4mFyxBzN1w23Rkf4K8MQ3B0bCZE5crjYp81eUtWrfUM+zLPmF9e1P/ws2yGHvL6mueU9PxtDJn5rSLsQBSxIkN0QB/nq76S4uh2Nrmmrjomejy5LqXnTVbEoIW2RTFBzyMWy4AjQY6P2pAJ8zCagvcdYcweUIqMQIDAQAB
      加密前:[object Object]
      公鑰加密后:LwggD8SIeWoqzh4gHh/vJ9nEZsqeNZfoxgrRRPD7k0wpp9/uZmR5kfRJ8O59yW5IaOt1z50mJ26ylRBOKNoTTl7Rt4zVmBYX4EXr4Ajq3CINFcPI/j5l8yQRSIgLPUvOxhIAKmfrgNKCaLjSdjK/CnTbPrZoDArI8iAHq/ih4r8=
      解密后:{\"username\":\"歡子\",\"password\":123456,\"remark\":\"abcd!@#$:\"}

        聯(lián)調(diào)測(cè)試

        自己解密自己加密的的數(shù)據(jù)基本上沒(méi)有什么問(wèn)題,重要的是解密對(duì)方加密的數(shù)據(jù)會(huì)不會(huì)成功,前后端相互加解密的工程中,最重要的就是保持兩邊的加密、填充方式一致、加密位數(shù)一致,還有就是后端Base64字符串轉(zhuǎn)成byte[]數(shù)組的時(shí)候要注意,Base64工具類轉(zhuǎn)跟直接字符串getByte()跟用輸入輸出流來(lái)轉(zhuǎn),得到的數(shù)組結(jié)果有差異,在本次測(cè)試中我也是搞了好久才使得前后端一致,緊跟上面的簡(jiǎn)單測(cè)試,接下來(lái)我們進(jìn)行前后端聯(lián)調(diào)測(cè)試

        1、AES:前后端相互用對(duì)方的key解密對(duì)方加密的數(shù)據(jù)

        2、RSA:前后端相互用對(duì)方的公鑰進(jìn)行加密數(shù)據(jù),然后將數(shù)據(jù)叫給對(duì)方解密

        這里要講一下步驟,不然大家看不懂下面這幾張圖,為了確保后端加密解密用的是同一個(gè)密鑰對(duì),我們采用控制臺(tái)輸入前端秘鑰跟前端使用后端公鑰加密后的密文,然后再使用私鑰去解密從而得到前端的明文,而js前端部分,只有不刷新頁(yè)面,對(duì)象數(shù)據(jù)會(huì)存在瀏覽器內(nèi)存中,確保了加密解密是用同一個(gè)對(duì)密鑰對(duì)

        總而言之,測(cè)試結(jié)果是正確的,接下來(lái)就可以再項(xiàng)目中進(jìn)行加解密了

         項(xiàng)目應(yīng)用

        理論思路

        前、后端的代碼都封裝好了,并且都通過(guò)了簡(jiǎn)單測(cè)試,接下來(lái)就是應(yīng)用到項(xiàng)目中,首先我們要解決的是生成公鑰秘鑰并交換的問(wèn)題,思路如下:

        生成:

          1、后端:在項(xiàng)目啟動(dòng)的時(shí)候生成RSA公鑰秘鑰并在整個(gè)項(xiàng)目運(yùn)行中不發(fā)生改變(或者每隔一段時(shí)間更新一次也行),AES的key是每次響應(yīng)之前隨機(jī)獲取

          2、前端:我們?cè)谠L問(wèn)頁(yè)面開(kāi)始生成RSA公鑰秘鑰并且希望頁(yè)面在刷新之前都不發(fā)生改變,因此將它們存在window對(duì)象中(如果需要更加健全,使用H5的本地存儲(chǔ)localStorage、sessionStorage)在head.html中生成,AES的key在每次發(fā)起請(qǐng)求之前隨機(jī)獲取

        交換:

          1、前端獲取后端RSA公鑰:前端訪問(wèn)登錄頁(yè)面(網(wǎng)站入口),后臺(tái)返回modelAndView時(shí)注入RSA的公鑰,前端獲取用存到sessionStorage中,直到回話關(guān)閉

          2、后端獲取前端RSA公鑰:前端公鑰跟隨http請(qǐng)求發(fā)送到后端

        生成與交換公鑰的問(wèn)題解決了,接下來(lái)就是如何傳輸AES加密后的數(shù)據(jù)RSA公鑰加密后的AES的key,思路如下:

        1、前端:重寫$.ajax方法(或者封裝一個(gè)ajax),發(fā)送數(shù)據(jù)前用AES加密數(shù)據(jù)(key隨機(jī)生成),用后端的RSA公鑰加密AES的key,將加密后的data數(shù)據(jù)、加密后的AES的key、前端RSA公鑰發(fā)送到后端;觸發(fā)回調(diào)后,先用前端RSA私鑰解密AES的key,在用明文key去解密

        2、后端:寫兩個(gè)自定義注解Encrypt、Decrypt,AOP攔截所有帶自定義注解的post請(qǐng)求進(jìn)行加密解密,有@Encrypt需要對(duì)返回值進(jìn)行加密,有@Decrypt需要對(duì)參數(shù)進(jìn)行解密,加密解密過(guò)程與前端的操作同理

        前端代碼

        引入js

              <!--CryptoJS jsencrypt -->
              <script th:src="@{/js/cryptojs.js}"></script>
              <script th:src="@{/js/jsencrypt.js}"></script>
              <script th:src="@{/js/aesUtil.js}"></script>
              <script th:src="@{/js/rsaUtil.js}"></script>

        下載CryptoJs跟jsencrypt下來(lái)發(fā)現(xiàn)CryptoJs需要引入很多js,因此在網(wǎng)上找了這個(gè)整合的js,引它就夠了

       cryptojs.js

        獲取后端公鑰

      <script th:inline="javascript">
          //獲取后端RSA公鑰并存到sessionStorage    sessionStorage.setItem('javaPublicKey', [[${publicKey}]]);</script>

        重寫ajax以及生成前端密鑰對(duì),因?yàn)槲覀冺?xiàng)目中大部分都是使用$.ajax方法,所有重寫它比較合適,并且我們只攔截post請(qǐng)求

       head.html

        后端代碼

        兩個(gè)自定義注解

       Decrypt
       Encrypt

        aop掃描所有的controller,攔截帶自定義標(biāo)簽的post請(qǐng)求,進(jìn)行解密、加密再將明文設(shè)置回去(如何使用aop?請(qǐng)戳我之前的博客:SpringBoot系列——aop 面向切面

        要注意的是:

        1、我們?cè)赼op只設(shè)置了第一個(gè)參數(shù),因此controller方法需要是實(shí)體接參且第一個(gè)參數(shù)就是,所有要求,要么有一個(gè)實(shí)體Vo參數(shù),要么沒(méi)有參數(shù);

        2、對(duì)于返回值,需要是統(tǒng)一的返回值,因?yàn)槲覀兡壳笆前唇y(tǒng)一的返回值設(shè)置值的,例如本例中的Result,是我們約定好的統(tǒng)一返回值(后續(xù)升級(jí)可以用反射來(lái)設(shè)置值);

        3、還有一個(gè)需要注意的地方,method方法必須是要public修飾的才能設(shè)置方法的形參值,private的設(shè)置不了;

        PS:2019-06-12補(bǔ)充,我們之前在進(jìn)行jackson序列化和反序列化忘記對(duì)date進(jìn)行處理,導(dǎo)致時(shí)間格式錯(cuò)亂,現(xiàn)在補(bǔ)充一下

       SafetyAspect.java

        在需要進(jìn)行加密解密的controller方法上加自定義注解,需要加密@Encrypt,需要解密@Decrypt,兩個(gè)都有就兩個(gè)都加

      復(fù)制代碼

          /**
           * 登錄     */
          @PostMapping("login")
          @Decrypt
          @Encrypt    public Result<ImsUserVo> login(ImsUserVo userVo, HttpServletResponse response)

      復(fù)制代碼

        到這里就準(zhǔn)備好了,可以發(fā)現(xiàn)除了要在需要進(jìn)行加密解密的controller方法上加自定義注解,根本不需要對(duì)之前的代碼進(jìn)行修改,也不影響之前的業(yè)務(wù),當(dāng)然,因?yàn)槲覀冎苯又貙懥?.ajax方法,所以我們需要對(duì)所有的post請(qǐng)求的controller都加注解,好在我們用的是單表繼承通用common、自動(dòng)生成單表基礎(chǔ)增、刪、改、查接口(詳情請(qǐng)戳:SpringBoot系列——Spring-Data-JPA(究極進(jìn)化版) 自動(dòng)生成單表基礎(chǔ)增、刪、改、查接口),基礎(chǔ)的API我們只需要在common的controller加注解就好了,自定義controller跟重寫的controller也要記得加,接下來(lái)就可以把項(xiàng)目跑起來(lái)進(jìn)行測(cè)試

        效果演示

        未加密之前的效果

        數(shù)據(jù)直接暴露在http數(shù)據(jù)包中

        AES與RSA混合加密之后的效果

        http數(shù)據(jù)包中傳輸?shù)氖敲芪?/p>

       

        解密之后才能看到明文數(shù)據(jù)

        到這里我們實(shí)現(xiàn)了加解密與項(xiàng)目的結(jié)合,如果項(xiàng)目已經(jīng)是按照我前面說(shuō)的約定的話,即插即用,不影響項(xiàng)目原有業(yè)務(wù),直接可以使用這一套,把我的代碼拿過(guò)去就可以跑起來(lái)

        后記

         前后端API交互數(shù)據(jù)加密——AES與RSA混合加密完整實(shí)例先記錄到這里,后續(xù)有空再更新升級(jí),雖然說(shuō)沒(méi)有絕對(duì)的安全,但加密總比不加密的要好,整篇文章從頭看起來(lái)也沒(méi)什么,比較簡(jiǎn)單,實(shí)際上...中間踩了很多坑,心酸血淚史就不在這里闡述了,希望這篇博客能幫到你

        2019-09-24補(bǔ)充:完成這個(gè)混合加密后不久,我試著將它應(yīng)用在websocket中,而后就有了另一篇文章,感興趣的可以移步前往閱讀《WebSocket數(shù)據(jù)加密——AES與RSA混合加密

        代碼開(kāi)源

        2019-09-24補(bǔ)充:混合加密的代碼一直嵌在ims項(xiàng)目里沒(méi)有整理出來(lái),而且ims還沒(méi)完工尚未開(kāi)源出來(lái),好在前段時(shí)間我開(kāi)源的一個(gè)簡(jiǎn)單通用的后臺(tái)管理系統(tǒng),Base Admin(開(kāi)源一套簡(jiǎn)單通用的后臺(tái)管理系統(tǒng)),里面整合了這套API混合加密,大家前往這個(gè)項(xiàng)目查看這套API混合加密代碼

        代碼已經(jīng)開(kāi)源、托管到我的GitHub、碼云:

        GitHub:https://github.com/huanzi-qch/base-admin

        碼云:https:///huanzi-qch/base-admin

        中間人攻擊

        什么是中間人攻擊?維基百科:https://zh./wiki/%E4%B8%AD%E9%97%B4%E4%BA%BA%E6%94%BB%E5%87%BB

        以下介紹摘自維基百科:

        中間人攻擊英語(yǔ):Man-in-the-middle attack,縮寫:MITM)在密碼學(xué)和計(jì)算機(jī)安全領(lǐng)域中是指攻擊者與通訊的兩端分別創(chuàng)建獨(dú)立的聯(lián)系,并交換其所收到的數(shù)據(jù),使通訊的兩端認(rèn)為他們正在通過(guò)一個(gè)私密的連接與對(duì)方直接對(duì)話,但事實(shí)上整個(gè)會(huì)話都被攻擊者完全控制。在中間人攻擊中,攻擊者可以攔截通訊雙方的通話并插入新的內(nèi)容。在許多情況下這是很簡(jiǎn)單的(例如,在一個(gè)未加密的Wi-Fi 無(wú)線接入點(diǎn)的接受范圍內(nèi)的中間人攻擊者,可以將自己作為一個(gè)中間人插入這個(gè)網(wǎng)絡(luò))。

        一個(gè)中間人攻擊能成功的前提條件是攻擊者能將自己偽裝成每一個(gè)參與會(huì)話的終端,并且不被其他終端識(shí)破。中間人攻擊是一個(gè)(缺乏)相互認(rèn)證的攻擊。大多數(shù)的加密協(xié)議都專門加入了一些特殊的認(rèn)證方法以阻止中間人攻擊。例如,SSL協(xié)議可以驗(yàn)證參與通訊的一方或雙方使用的證書(shū)是否是由權(quán)威的受信任的數(shù)字證書(shū)認(rèn)證機(jī)構(gòu)頒發(fā),并且能執(zhí)行雙向身份認(rèn)證。

        攻擊示例

                                                                                                                                                           

      中間人攻擊示意圖

        假設(shè)愛(ài)麗絲(Alice)希望與鮑伯(Bob)通信。同時(shí),馬洛里(Mallory)希望攔截竊會(huì)話以進(jìn)行竊聽(tīng)并可能在某些時(shí)候傳送給鮑伯一個(gè)虛假的消息。

      首先,愛(ài)麗絲會(huì)向鮑勃索取他的公鑰。如果Bob將他的公鑰發(fā)送給Alice,并且此時(shí)馬洛里能夠攔截到這個(gè)公鑰,就可以實(shí)施中間人攻擊。馬洛里發(fā)送給愛(ài)麗絲一個(gè)偽造的消息,聲稱自己是鮑伯,并且附上了馬洛里自己的公鑰(而不是鮑伯的)。

        愛(ài)麗絲收到公鑰后相信這個(gè)公鑰是鮑伯的,于是愛(ài)麗絲將她的消息用馬洛里的公鑰(愛(ài)麗絲以為是鮑伯的)加密,并將加密后的消息回給鮑伯。馬洛里再次截獲愛(ài)麗絲回給鮑伯的消息,并使用馬洛里自己的私鑰對(duì)消息進(jìn)行解密,如果馬洛里愿意,她也可以對(duì)消息進(jìn)行修改,然后馬洛里使用鮑伯原先發(fā)給愛(ài)麗絲的公鑰對(duì)消息再次加密。當(dāng)鮑伯收到新加密后的消息時(shí),他會(huì)相信這是從愛(ài)麗絲那里發(fā)來(lái)的消息。

        1.愛(ài)麗絲發(fā)送給鮑伯一條消息,卻被馬洛里截獲:

      愛(ài)麗絲“嗨,鮑勃,我是愛(ài)麗絲。給我你的公鑰” --> 馬洛里 鮑勃

        2.馬洛里將這條截獲的消息轉(zhuǎn)送給鮑伯;此時(shí)鮑伯并無(wú)法分辨這條消息是否從真的愛(ài)麗絲那里發(fā)來(lái)的:

      愛(ài)麗絲 馬洛里“嗨,鮑勃,我是愛(ài)麗絲。給我你的公鑰” --> 鮑伯

        3.鮑伯回應(yīng)愛(ài)麗絲的消息,并附上了他的公鑰:

      愛(ài)麗絲 馬洛里<-- [鮑伯的公鑰]-- 鮑伯

        4.馬洛里用自己的密鑰替換了消息中鮑伯的密鑰,并將消息轉(zhuǎn)發(fā)給愛(ài)麗絲,聲稱這是鮑伯的公鑰:

      愛(ài)麗絲<-- [馬洛里的公鑰]-- 馬洛里 鮑勃

        5.愛(ài)麗絲用她以為是鮑伯的公鑰加密了她的消息,以為只有鮑伯才能讀到它:

      愛(ài)麗絲“我們?cè)诠财囌疽?jiàn)面!”--[使用馬洛里的公鑰加密] --> 馬洛里 鮑勃

        6.然而,由于這個(gè)消息實(shí)際上是用馬洛里的密鑰加密的,所以馬洛里可以解密它,閱讀它,并在愿意的時(shí)候修改它。他使用鮑伯的密鑰重新加密,并將重新加密后的消息轉(zhuǎn)發(fā)給鮑伯:

      愛(ài)麗絲 馬洛里“在家等我!”--[使用鮑伯的公鑰加密] --> 鮑伯

        7.鮑勃認(rèn)為,這條消息是經(jīng)由安全的傳輸通道從愛(ài)麗絲那里傳來(lái)的。

        這個(gè)例子顯示了愛(ài)麗絲和鮑伯需要某種方法來(lái)確定他們是真正拿到了屬于對(duì)方的公鑰,而不是拿到來(lái)自攻擊者的公鑰。否則,這類攻擊一般都是可行的,在原理上,可以針對(duì)任何使用公鑰——密鑰技術(shù)的通訊消息發(fā)起攻擊。幸運(yùn)的是,有各種不同的技術(shù)可以幫助抵御MITM攻擊。

        ----------- end -----------

        單純的加密只能防監(jiān)聽(tīng)偷窺,不能防中間人偽裝,那么我們應(yīng)該如何阻止中間人攻擊呢?SSL協(xié)議

        HTTPS SSL協(xié)議:數(shù)字證書(shū)、CA機(jī)構(gòu)、數(shù)字簽名,請(qǐng)看大佬的這篇,通俗易懂:看完這篇文章,我奶奶都懂了https的原理 

        本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
        轉(zhuǎn)藏 分享 獻(xiàn)花(0

        0條評(píng)論

        發(fā)表

        請(qǐng)遵守用戶 評(píng)論公約

        類似文章 更多