记一次前后端数据加密的学习

最近项目涉及到一些敏感信息,业务要求数据在传输过程中需要加密。

这里数据传输包含2中

  1. 前后端数据传输过程
  2. 与其它服务(系统)数据交互时,数据的传输过程

这里我们先简要介绍加密算法的优缺点。再通过前后端(vue、java)代码的形式,演示加密解密的demo

1.加密算法简介

参考并整理了目前比较流行的2种加密算法

1.1.RAS 非对称加密

RAS 加密是基于质数对的方式加密的,具体原理以及优缺点可以参考RSA加密算法原理简书

该加密方式存在缺点,明文的长度不能超过 117 bytes,如果超长,就会产生异常

javax.crypto.IllegalBlockSizeException: Data must not be longer than 117 bytes
	at com.sun.crypto.provider.RSACipher.doFinal(RSACipher.java:346)
	at com.sun.crypto.provider.RSACipher.engineDoFinal(RSACipher.java:391)
	at javax.crypto.Cipher.doFinal(Cipher.java:2168)
	at com.xsaas.hr.stockapp.base.utils.RsaUtils.encrypt(RsaUtils.java:137)
	at com.xsaas.RasTest.testRas(RasTest.java:122)

可以通过分段加密的方式进行处理此种问题。但是 JS 前端代码中分段加密存在缺陷,偶尔会有数据无法解密的情况(这里我没有深究,不清楚原因),分段加解密也存在代码繁琐等问题(常规加密方式是直接调用库,这里在调用库的基础上,还需要加工代码,可能这也是导致解密失败的原因)

1.2.AES 标准加密

AES 加密的原理详解见AES 加密算法的原理详解,微信小程序加密传输就是用这个加密算法加密的。

该算法比较简答,属于对称加密,但是没有 RAS 非对称加密 安全。

2. 代码实现

2.1.RAS 非对称加密

2.1.1.Java代码

  1. 获取密钥对
    /**
     * RAS非对称加密,随机生成密钥对
     *
     * @return 密钥对
     */
    public static Map<String, String> genKeyPair() {
        // KeyPairGenerator类用于生成公钥和私钥对,基于RSA算法生成对象
        KeyPairGenerator keyPairGen = null;
        try {
            keyPairGen = KeyPairGenerator.getInstance("RSA");
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        // 初始化密钥对生成器,密钥大小为96-1024位
        assert keyPairGen != null;
        keyPairGen.initialize(1024, new SecureRandom());
        // 生成一个密钥对,保存在keyPair中
        KeyPair keyPair = keyPairGen.generateKeyPair();
        RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();   // 得到私钥
        RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();  // 得到公钥
        // 将公钥和私钥保存到Map
        Map<String, String> res = new HashMap<String, String>(2) {{
            put(AdminConstant.PUBLIC_KEY, new String(Base64.encodeBase64(publicKey.getEncoded())));
            put(AdminConstant.PRIVATE_KEY, new String(Base64.encodeBase64((privateKey.getEncoded()))));
        }};
        return res;
    }
  1. 公钥加密
    /**
     * RAS非对称加密: 公钥加密
     *
     * @param str       加密字符串
     * @param publicKey 公钥
     * @return 密文
     */
    public static String encrypt(String str, String publicKey) {
        //base64编码的公钥
        byte[] decoded = Base64.decodeBase64(publicKey);
        RSAPublicKey pubKey;
        String outStr = null;

        try {
            pubKey = (RSAPublicKey) KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(decoded));
            Cipher cipher = Cipher.getInstance("RSA");
            cipher.init(Cipher.ENCRYPT_MODE, pubKey);
            outStr = Base64.encodeBase64String(cipher.doFinal(str.getBytes(StandardCharsets.UTF_8)));
        } catch (InvalidKeySpecException | BadPaddingException | IllegalBlockSizeException | InvalidKeyException | NoSuchPaddingException | NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        //RSA加密
        return outStr;
    }
  1. 私钥解密
    /**
     * RSA私钥解密
     *
     * @param str        加密字符串
     * @param privateKey 私钥
     * @return 铭文
     */
    public static String decrypt(String str, String privateKey) {
        //64位解码加密后的字符串
        byte[] inputByte = Base64.decodeBase64(str.getBytes(StandardCharsets.UTF_8));
        //base64编码的私钥
        byte[] decoded = Base64.decodeBase64(privateKey);
        RSAPrivateKey priKey;
        //RSA解密
        Cipher cipher;
        String outStr = null;

        try {
            priKey = (RSAPrivateKey) KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(decoded));
            cipher = Cipher.getInstance("RSA");
            cipher.init(Cipher.DECRYPT_MODE, priKey);
            outStr = new String(cipher.doFinal(inputByte));
        } catch (InvalidKeySpecException | NoSuchAlgorithmException | NoSuchPaddingException | BadPaddingException | IllegalBlockSizeException | InvalidKeyException e) {
            e.printStackTrace();
        }
        return outStr;
    }
  1. demo
    @Test
    public void testRasDemo() {
        JSONObject object = new JSONObject();
        object.put("name", "隔壁老樊");
        object.put("id", "123456");
        object.put("age", 20);
        // 随机获取秘钥对
        Map<String, String> keyPair = RsaUtils.genKeyPair();
        // 秘钥
        String privateKey = keyPair.get(AdminConstant.PUBLIC_KEY);
        // 公钥
        String publicKey = keyPair.get(AdminConstant.PUBLIC_KEY);
        // 加密后的数据
        String encryptData = null;
        try {
            // 使用公钥加密
            encryptData = RsaUtils.encrypt(JSON.toJSONString(object), publicKey);
        } catch (Exception e) {
            log.error("加密失败", e);
        }
        // 解密后的数据
        String decryptData = null;
        try {
            // 使用私钥解密
            decryptData = RsaUtils.decrypt(encryptData, privateKey);
        } catch (Exception e) {
            e.printStackTrace();
        }
        log.info("原始明文数据={}", object.toJSONString());
        log.info("公钥={}", publicKey);
        log.info("私钥={}", privateKey);
        log.info("加密后的数据={}", encryptData);
        log.info("解密后的数据={}", decryptData);
    }
  1. 执行结果
10:14:26.814 [main] INFO com.xsaas.RasTest - 原始明文数据={"name":"隔壁老樊","id":"123456","age":20}
10:14:26.822 [main] INFO com.xsaas.RasTest - 公钥=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCEczsBcEfFdljhQ/MsMythd96xpPR1RzrveDwAwazETBPnmxuK8MEKecY/B3cdGmSEcPlZRy3hDgxwVfSgxcxqIXt68S3/U+jfndrCtPbJJ+SYj61X/MT82lqFWuhF0lNj2RbyjohzaW+GSBqLbX7gyuZRw57ZcoXpRhx+bNvhLQIDAQAB
10:14:26.822 [main] INFO com.xsaas.RasTest - 私钥=MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAIRzOwFwR8V2WOFD8ywzK2F33rGk9HVHOu94PADBrMRME+ebG4rwwQp5xj8Hdx0aZIRw+VlHLeEODHBV9KDFzGohe3rxLf9T6N+d2sK09skn5JiPrVf8xPzaWoVa6EXSU2PZFvKOiHNpb4ZIGottfuDK5lHDntlyhelGHH5s2+EtAgMBAAECgYAG1DYfnd1ldfOhMbKw/bZn4RlPSXT9Mv376NQXKeUxfcas81dZM46QbrTk/QqMKpcyKO0CSGQ6LVJA3H2vaGNgpmvj36qKiZ3AKsbc90JvRWlULBk1XlmyduU6i+hS8dPZXuh3GKum8kpCjK/Qb1e8hKu+1EQ3wQFO0EYSjbq7wQJBAMbgXMmuQ0cVR9qAnG8mq482ES6q16xTUPyMutO9NdOraoZQP15HFKAEFs8m05Hy0Hsw4IobxAg+A1dYgE1t/j0CQQCqfnUWRQDn6JhnrYHcGDNtc7F4ZDLrOI+P8ZfVNoOFOaY7EzgkAD5w1pM8d2IoqPCJRJg9wExAAD2yZFF53g2xAkEAtJyB5+9Izj93V+rBJviZiZ/yjs08vRWVUSaFbVJClg7w2TX7tqUbCA9un4aFUeCQkbBb21FIAKxA4IxRSQCBiQJADBF9ekESKlhFiXk3qvuvkDzTQCFflVTgnKDOTZJZRvHouV/H5ox53wThUTNmKFilBiJr4FsfSpx5wYnmVokIUQJAexXN46GNJZYZzz2Twtj/lJ/dOEToplh6AG0uLTtjkJ2H2hiha8oFgwICQKj8LYcLSpqwUsxVQa4IkhYzMmPzJQ==
10:14:26.822 [main] INFO com.xsaas.RasTest - 加密后的数据=LpD/ZLlQugYYpuaPYfkpX6ZNjRZlS+5516jos92O8rSnPnlBMzqtqohWrPugtZ25jvDjSzNrxKvLiSh9EweCl93QjomjUsuhia5TjpSV5smDqjUezxLQGP/E+zamZw9ZvaRdJstd4kyBfhQwYb66m9HiwK8qa4EpzuUyr94hcHw=
10:14:26.822 [main] INFO com.xsaas.RasTest - 解密后的数据={"name":"隔壁老樊","id":"123456","age":20}

2.1.2 VUE 前端代码

  1. 引入加密库
npm i jsencrypt
import JSEncrypt from 'jsencrypt'
  1. 加密
const encrypt = new JSEncrypt();
encrypt.setPublicKey('你的公钥');
var encryptData = encrypt.encrypt(‘你的数据’);// 加密后的字符串
  1. 解密
const decrypt =new JSEncrypt()
decrypt.setPrivateKey(privateKey)
var decryptData = decrypt.decrypt(msg)
  1. Demo

对 java 加密后的数据进行解密,解密结果如下

      const privateKey = 'MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAIRzOwFwR8V2WOFD8ywzK2F33rGk9HVHOu94PADBrMRME+ebG4rwwQp5xj8Hdx0aZIRw+VlHLeEODHBV9KDFzGohe3rxLf9T6N+d2sK09skn5JiPrVf8xPzaWoVa6EXSU2PZFvKOiHNpb4ZIGottfuDK5lHDntlyhelGHH5s2+EtAgMBAAECgYAG1DYfnd1ldfOhMbKw/bZn4RlPSXT9Mv376NQXKeUxfcas81dZM46QbrTk/QqMKpcyKO0CSGQ6LVJA3H2vaGNgpmvj36qKiZ3AKsbc90JvRWlULBk1XlmyduU6i+hS8dPZXuh3GKum8kpCjK/Qb1e8hKu+1EQ3wQFO0EYSjbq7wQJBAMbgXMmuQ0cVR9qAnG8mq482ES6q16xTUPyMutO9NdOraoZQP15HFKAEFs8m05Hy0Hsw4IobxAg+A1dYgE1t/j0CQQCqfnUWRQDn6JhnrYHcGDNtc7F4ZDLrOI+P8ZfVNoOFOaY7EzgkAD5w1pM8d2IoqPCJRJg9wExAAD2yZFF53g2xAkEAtJyB5+9Izj93V+rBJviZiZ/yjs08vRWVUSaFbVJClg7w2TX7tqUbCA9un4aFUeCQkbBb21FIAKxA4IxRSQCBiQJADBF9ekESKlhFiXk3qvuvkDzTQCFflVTgnKDOTZJZRvHouV/H5ox53wThUTNmKFilBiJr4FsfSpx5wYnmVokIUQJAexXN46GNJZYZzz2Twtj/lJ/dOEToplh6AG0uLTtjkJ2H2hiha8oFgwICQKj8LYcLSpqwUsxVQa4IkhYzMmPzJQ=='
      const msg = 'LpD/ZLlQugYYpuaPYfkpX6ZNjRZlS+5516jos92O8rSnPnlBMzqtqohWrPugtZ25jvDjSzNrxKvLiSh9EweCl93QjomjUsuhia5TjpSV5smDqjUezxLQGP/E+zamZw9ZvaRdJstd4kyBfhQwYb66m9HiwK8qa4EpzuUyr94hcHw='
      const encrypt = new JSEncrypt()
      encrypt.setPrivateKey(privateKey)
      console.log('加密前的数据=' + msg)
      console.log('解密后的数据=' + encrypt.decrypt(msg))
  1. 执行结果

image

2.2.AES标准加密

2.2.1.Java 代码实现

  1. 随机生成 加密 key

此处使用默认标准,加密key的长度为16位,此代码为随机生成16为编码

    /**
     * 随机生成16位的编码
     */
    public static String getPrivateKey() {
        //随机生成一位整数
        int random = (int) (Math.random() * 9 + 1);
        String valueOf = String.valueOf(random);
        //生成uuid的hashCode值
        int hashCode = UUID.randomUUID().toString().hashCode();
        //可能为负数
        if (hashCode < 0) {
            hashCode = -hashCode;
        }
        return valueOf + String.format("%015d", hashCode);
    }
  1. AES解密
    /**
     * AES算法:解密方法
     *
     * @param data 要解密的数据
     * @param key  解密key
     * @param iv   解密iv
     * @return 解密的结果
     * @throws Exception
     */
    public static String desEncrypt(String data, String key, String iv) throws Exception {
        try {
            byte[] encrypted1 = new Base64().decode(data);

            Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
            SecretKeySpec keyspec = new SecretKeySpec(key.getBytes(), "AES");
            IvParameterSpec ivspec = new IvParameterSpec(iv.getBytes());

            cipher.init(Cipher.DECRYPT_MODE, keyspec, ivspec);

            byte[] original = cipher.doFinal(encrypted1);
            String originalString = new String(original);
            return originalString;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
  1. AES 加密
    /**
     * AES算法:AES加密方法
     *
     * @param data 要加密的数据
     * @param key  加密key
     * @param iv   加密iv
     * @return 加密的结果
     * @throws Exception
     */
    public static String encrypt(String data, String key, String iv) throws Exception {
        try {

            Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");//"算法/模式/补码方式"NoPadding PkcsPadding
            int blockSize = cipher.getBlockSize();

            byte[] dataBytes = data.getBytes();
            int plaintextLength = dataBytes.length;
            if (plaintextLength % blockSize != 0) {
                plaintextLength = plaintextLength + (blockSize - (plaintextLength % blockSize));
            }

            byte[] plaintext = new byte[plaintextLength];
            System.arraycopy(dataBytes, 0, plaintext, 0, dataBytes.length);

            SecretKeySpec keyspec = new SecretKeySpec(key.getBytes(), "AES");
            IvParameterSpec ivspec = new IvParameterSpec(iv.getBytes());

            cipher.init(Cipher.ENCRYPT_MODE, keyspec, ivspec);
            byte[] encrypted = cipher.doFinal(plaintext);
            return new Base64().encodeToString(encrypted);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
  1. Demo
   @Test
    public void testAesDemo() {
        // 原始数据
        JSONObject object = new JSONObject();
        object.put("name", "隔壁老樊");
        object.put("id", "123456");
        object.put("age", 20);
        // 随机生成 16 位 key
        String privateKey = RsaUtils.getPrivateKey();
        String ivKey = RsaUtils.getPrivateKey();
        // 加密后的数据
        String encryptData = null;
        try {
            encryptData = RsaUtils.encrypt(JSON.toJSONString(object), privateKey, ivKey);
        } catch (Exception e) {
            log.error("加密失败", e);
        }
        // 解密后的数据
        String decryptData = null;
        try {
            decryptData = RsaUtils.desEncrypt(encryptData, privateKey, ivKey);
        } catch (Exception e) {
            log.error("解密失败", e);
        }
        log.info("原始数据={}", object.toJSONString());
        log.info("privateKey={}", "privateKey");
        log.info("ivKey={}", ivKey);
        log.info("加密后的数据={}", encryptData);
        log.info("解密后的数据={}", decryptData);
    }
  1. 执行结果
10:43:00.142 [main] INFO com.xsaas.RasTest - 原始数据={"name":"隔壁老樊","id":"123456","age":20}
10:43:00.148 [main] INFO com.xsaas.RasTest - privateKey=8000000915632506
10:43:00.148 [main] INFO com.xsaas.RasTest - ivKey=7000001933997723
10:43:00.148 [main] INFO com.xsaas.RasTest - 加密后的数据=3oMUkGtm7Yp1V2zCQJqLt9u5BrbOPDqL56zX5ycRx+znREu9UEsJHxlPNgBvaoiW
10:43:00.148 [main] INFO com.xsaas.RasTest - 解密后的数据={"name":"隔壁老樊","id":"123456","age":20}  

2.2.2.VUE 前端代码

  1. 引入库
npm install crypto-js
import CryptoJS from 'crypto-js/crypto-js'
  1. 解密
decrypt(word, keyStr, ivStr) {
  let key = CryptoJS.enc.Utf8.parse(keyStr)
  let iv = CryptoJS.enc.Utf8.parse(ivStr)
  if (keyStr) {
    key = CryptoJS.enc.Utf8.parse(keyStr)
    iv = CryptoJS.enc.Utf8.parse(ivStr)
  }
  const base64 = CryptoJS.enc.Base64.parse(word)
  const src = CryptoJS.enc.Base64.stringify(base64)
  var decrypt = CryptoJS.AES.decrypt(src, key, {
    iv: iv,
    mode: CryptoJS.mode.CBC,
    padding: CryptoJS.pad.ZeroPadding
  })
  var decryptedStr = decrypt.toString(CryptoJS.enc.Utf8)
  return decryptedStr.toString()
},
  1. 加密
export function Encrypt(word, keyStr, ivStr) {
let key = KEY
let iv = IV
if (keyStr) {
   key = CryptoJS.enc.Utf8.parse(keyStr);
   iv = CryptoJS.enc.Utf8.parse(ivStr);
}
let srcs = CryptoJS.enc.Utf8.parse(word);
var encrypted = CryptoJS.AES.encrypt(srcs, key, {
   iv: iv,
   mode: CryptoJS.mode.CBC,
   padding: CryptoJS.pad.ZeroPadding
});
console.log("-=-=-=-", encrypted.ciphertext)
return CryptoJS.enc.Base64.stringify(encrypted.ciphertext);
  1. Demo

    基于上述加密加密数据进行解密

      const word = '3oMUkGtm7Yp1V2zCQJqLt9u5BrbOPDqL56zX5ycRx+znREu9UEsJHxlPNgBvaoiW'
      const keyStr = '8000000915632506'
      const ivStr = '7000001933997723'
      console.log('=============' + this.decrypt(word, keyStr, ivStr))
    decrypt(word, keyStr, ivStr) {
      let key = CryptoJS.enc.Utf8.parse(keyStr)
      let iv = CryptoJS.enc.Utf8.parse(ivStr)

      if (keyStr) {
        key = CryptoJS.enc.Utf8.parse(keyStr)
        iv = CryptoJS.enc.Utf8.parse(ivStr)
      }
      const base64 = CryptoJS.enc.Base64.parse(word)
      const src = CryptoJS.enc.Base64.stringify(base64)
      var decrypt = CryptoJS.AES.decrypt(src, key, {
        iv: iv,
        mode: CryptoJS.mode.CBC,
        padding: CryptoJS.pad.ZeroPadding
      })

      var decryptedStr = decrypt.toString(CryptoJS.enc.Utf8)
      return decryptedStr.toString()
    },
  1. 执行结果
    image
posted @ 2021-08-07 10:56  光头才能强  阅读(2087)  评论(1编辑  收藏  举报