JAVA數據加密壓縮傳輸給服務端(Gzip加AES)

上一話說到HTTP數據傳輸用Gzip方式壓縮,用springboot分別寫了客戶端和服務端的代碼測試。
對應博客地址
Java數據壓縮HTTP方式傳輸Gzip(附帶測試代碼及springboot-HTTP客戶端服務端代碼)
本文場景:
在數據傳輸中,除了需要壓縮數據大小,如果傳輸的是敏感數據,例如用戶信息等,就需要加密。

本文用到的加密算法是AES

首先貼一下AESUtils工具類代碼

package com.yolanda.mycompressclient.encrypt;

import java.io.UnsupportedEncodingException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.spec.AlgorithmParameterSpec;


import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.KeyGenerator;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;

/**
 * @Author:Yolanda
 * @Date: 2020/5/9 9:51
 */
public class AESUtils {
    private static final String GZIP_ENCODE_UTF_8 = "UTF-8";

    private static byte[] keyValue = new byte[] {       //用戶密鑰
            22,25,-35,-45,25,98,-55,-45,10,35,-45,25,
            26,-95,25,-65,-78,-99,85,45,-62,10,-0,11,
            -35,48,-98,65,-32,14,-78,25,36,-56,-45,-45,
            12,15,-35,-75,15,-14,62,-25,33,-45,55,68,-88
    };
    private static byte[] iv = new byte[] {             //算法參數
            -12,35,-25,65,45,-87,95,-22,-15,45,55,-66,32,5-4,84,55
    };
    private static SecretKey key;                       //加密密鑰
    private static AlgorithmParameterSpec paramSpec;    //算法參數
    private static Cipher ecipher;                      //加密算法

    static{
        KeyGenerator kgen;
        try {
            //爲指定算法生成一個密鑰生成器對象。
            kgen = KeyGenerator.getInstance("AES");
            //使用用戶提供的隨機源初始化此密鑰生成器,使其具有確定的密鑰長度。
            SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG");
            secureRandom.setSeed(keyValue);
            kgen.init(128, secureRandom);
            //使用KeyGenerator生成(對稱)密鑰。
            key = kgen.generateKey();
            //使用iv中的字節作爲IV來構造一個 算法參數。
            paramSpec = new IvParameterSpec(iv);
            //生成一個實現指定轉換的 Cipher 對象
            ecipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (NoSuchPaddingException e) {
            e.printStackTrace();
        }
    }

    /**
     * 加密,使用指定數據源生成密鑰,使用用戶數據作爲算法參數進行AES加密
     * @param msg 加密的數據
     * @return
     */
    public static String encrypt(String msg) {
        String str = "";
        try {
            //用密鑰和一組算法參數初始化此 cipher
            ecipher.init(Cipher.ENCRYPT_MODE, key, paramSpec);
            //加密並轉換成16進制字符串
            str = asHex(ecipher.doFinal(msg.getBytes(GZIP_ENCODE_UTF_8)));
        } catch (BadPaddingException e) {
            e.printStackTrace();
        } catch (InvalidKeyException e) {
            e.printStackTrace();
        } catch (InvalidAlgorithmParameterException e) {
            e.printStackTrace();
        } catch (IllegalBlockSizeException e) {
            e.printStackTrace();
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return str;
    }

    /**
     * 解密,對生成的16進制的字符串進行解密
     * @param value 解密的數據
     * @return
     */
    public static String decrypt(String value) {
        try {
            ecipher.init(Cipher.DECRYPT_MODE, key, paramSpec);
            return new String(ecipher.doFinal(asBin(value)),GZIP_ENCODE_UTF_8);
        } catch (BadPaddingException e) {
            e.printStackTrace();
        } catch (InvalidKeyException e) {
            e.printStackTrace();
        } catch (InvalidAlgorithmParameterException e) {
            e.printStackTrace();
        } catch (IllegalBlockSizeException e) {
            e.printStackTrace();
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return "";
    }

    /**
     * 將字節數組轉換成16進制字符串
     * @param buf
     * @return
     */
    private static String asHex(byte buf[]) {
        StringBuffer strbuf = new StringBuffer(buf.length * 2);
        int i;
        for (i = 0; i < buf.length; i++) {
            if (((int) buf[i] & 0xff) < 0x10)//小於十前面補零
                strbuf.append("0");
            strbuf.append(Long.toString((int) buf[i] & 0xff, 16));
        }
        return strbuf.toString();
    }

    /**
     * 將16進制字符串轉換成字節數組
     * @param src
     * @return
     */
    private static byte[] asBin(String src) {
        if (src.length() < 1)
            return null;
        byte[] encrypted = new byte[src.length() / 2];
        for (int i = 0; i < src.length() / 2; i++) {
            String str1 = src.substring(i * 2, i * 2 + 1);
            int high = Integer.parseInt(src.substring(i * 2, i * 2 + 1), 16);//取高位字節
            int low = Integer.parseInt(src.substring(i * 2 + 1, i * 2 + 2), 16);//取低位字節
            encrypted[i] = (byte) (high * 16 + low);
        }
        return encrypted;
    }

}

傳輸兩端都要寫這個工具類,一端加密,一端解密。

是先壓縮再加密還是先加密再壓縮呢?

貼一下隨手寫的測試類

package com.yolanda.mycompressclient.compress;

import com.yolanda.mycompressclient.encrypt.AESUtils;

import java.io.IOException;

/**
 * @Author:Yolanda
 * @Date: 2020/5/7 15:01
 */
public class Main {

    public static void main(String [] args){
        String test="test1234567890 test1234567890 test1234567890 test1234567890 test1234567890 test1234567890 test1234567890 test1234567890 test1234567890 test1234567890 test1234567890 test1234567890 test1234567890 test1234567890 test1234567890 test1234567890 test1234567890 test1234567890 test1234567890 test1234567890 test1234567890 test1234567890 test1234567890 test1234567890 test1234567890 test1234567890 test1234567890 test1234567890 test1234567890 test1234567890 test1234567890 test1234567890 test1234567890 test1234567890 test1234567890 test1234567890 test1234567890 test1234567890 test1234567890 test1234567890 test1234567890 test1234567890 test1234567890 test1234567890 test1234567890 test1234567890 test1234567890 test1234567890 test1234567890 test1234567890 test1234567890 test1234567890 test1234567890 test1234567890 test1234567890 test1234567890 test1234567890 test1234567890 test1234567890 test1234567890 test1234567890 test1234567890 test1234567890 test1234567890 test1234567890 test1234567890 test1234567890 test1234567890 test1234567890 test1234567890 test1234567890 test1234567890 test1234567890 test1234567890 test1234567890 test1234567890 test1234567890 test1234567890 test1234567890 test1234567890 test1234567890 test1234567890 test1234567890 test1234567890";
        System.out.println("壓縮前長度" + test.length());
        try{
            String afterCompressStr = CompressUtils.compress(test);
            System.out.println("壓縮後長度" + afterCompressStr.length());

            // 加密
            String afterEncryptStr = AESUtils.encrypt(afterCompressStr);
            System.out.println("先壓縮後加密的長度" + afterEncryptStr.length());

            // 加密
            String _afterEncryptStr = AESUtils.encrypt(test);
            String _afterCompressStr = CompressUtils.compress(_afterEncryptStr);
            System.out.println("先加密後壓縮的長度" + _afterCompressStr.length());

        }catch (IOException e){
            System.out.println(e.getMessage());
        }
    }
}

控制檯輸出

在這裏插入圖片描述

所以答案是先壓縮再加密。
其實這個很好理解,加密以後字符串變的比以前長多了。

好啦,再測試一下,客戶端發送加密壓縮後的數據,服務端能不能解密解壓到正確數據。

客戶端:

package com.yolanda.mycompressclient.controller;

import com.yolanda.mycompressclient.aop.TestAop;
import com.yolanda.mycompressclient.compress.CompressUtils;
import com.yolanda.mycompressclient.encrypt.AESUtils;
import com.yolanda.mycompressclient.http.HttpClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpMethod;
import org.springframework.stereotype.Controller;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import java.io.IOException;

/**
 * @Author:Yolanda
 * @Date: 2020/5/7 18:05
 */
@Controller
public class TestController {

    @Autowired
    private TestAop testAop;

    @RequestMapping("/test")
    @ResponseBody
    public String testSpringBootAop(String str){
        return testAop.testSpringBootAop(str);
    }

    @Autowired
    HttpClient httpClient;

    @RequestMapping(value = "/compress")
    @ResponseBody
    public String hello(){
        String url = "http://localhost:8081/hello";
        HttpMethod method = HttpMethod.GET;
        MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
        String test="test1234567890 test1234567890 test1234567890 test1234567890 test1234567890 test1234567890 test1234567890 test1234567890 test1234567890 test1234567890 test1234567890 test1234567890 ";
        String afterCompressStr = "";
        String afterEncryptStr = "";
        try {
            // 壓縮
            afterCompressStr = CompressUtils.compress(test);
            // 加密
            afterEncryptStr = AESUtils.encrypt(afterCompressStr);
        }catch (IOException e){
            System.out.println(e.getMessage());
        }

//        params.add("data", afterCompressStr);
        params.add("data", afterEncryptStr);

        String response = httpClient.client(url, method, params);
        System.out.println(response);
        return response;
    }
}

服務端:

package com.yolanda.myspringboot;

import com.yolanda.myspringboot.compress.CompressUtils;
import com.yolanda.myspringboot.encrypt.AESUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

/**
 * @Author:Yolanda
 * @Date: 2020/5/7 13:41
 */

@RestController
public class HelloController {

    @RequestMapping("/hello")
    @ResponseBody
    public String hello(String data){

        return "Hello, Yolanda!我在服務端接收到了你發送過來的數據,解密解壓後是:"
                + CompressUtils.decompressToStr(AESUtils.decrypt(data));
    }
}

測試結果:
在這裏插入圖片描述

好啦,成功啦~
完整代碼看這看這
JAVA數據加密壓縮傳輸給服務端GitHub代碼

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章