加解密--aes,des

加解密(aes,des)

前言

  • 數據加密方式有很多種,每個人都有自己的選擇,一旦要跟別人對接加密的數據,一定要先去了解下加密方式的類型和各種參數的設置要求,以下就是筆者在跟別人對接過程中遇到的坑,一方面是自己對加密瞭解不深,另一方面使用加密的一方自己本身也可能是個半桶水,從網上抄了一段代碼可以成功運行後就以爲搞定了,連加密的各種參數設置都不太清楚的情況下,很容易導致互相加解密失敗

AES

  • 首先,放上一個比較好用的工具:在線AES加密解密、AES在線加密解密、AES encryption and decryption–查錯網
  • 使用aes加解密前,雙方一定要協商好aes位數(是128bit還是256bit等),加密模式(是ECB還是CBC等),偏移量(ECB沒有偏移量,偏移量不同加密的結果是不一致的,即同一密鑰的明文在不同偏移量下加密的結果不同),密鑰(格式是什麼,什麼字符集,字節長度是多少,是16進制字符串,還是utf8字符串等)
  • 上面提到的4個一定要雙方確認清楚,否則這塊的調試就能耗掉你許多時間
  • 很多庫會有意無意地屏蔽掉aes的配置,從而宣稱其使用起來多方便,但是如果使用者不瞭解這些參數的配置,那麼在跨平臺時就一定會有各種各樣的問題,因爲每個平臺的實現庫的默認配置一般都不會相同,既然配置不同,怎麼可能相互加解密
  • 最後,提供一個js的加解密庫 brix/crypto-js: JavaScript library of crypto standards.crypto-js - npm
  • 詳細的api可以查看這裏:https://cryptojs.gitbook.io/docs/

代碼示例

js


var CryptoJS = require("../miniprogram_npm/crypto-js/index.js");
//加密設置,根據實際情況修改
var cfg={
  mode: CryptoJS.mode.ECB,
 // padding: CryptoJS.pad.Pkcs7 //pkcs7是默認的
  padding: CryptoJS.pad.NoPadding //本套協議採用的nopadding


}
//var key = CryptoJS.enc.Hex.parse("bc7a3dc9e79563291d7bfa93df67d545");
var key = CryptoJS.enc.Hex.parse("db1c29b3a9093117f773295b3f07bf93ff");//祕鑰(16進制字符串)


/**
 * 設置aes祕鑰,在加解密之前必須設置
 * 
 * @param {string} key  祕鑰,16進制字符串
 */
function SetHexKey(hexKey){
  key = CryptoJS.enc.Hex.parse(hexKey)
}


/**
 * aes加密方法,返回16進制字符串
 * 
 * @param {string} hexWord  16進制字符串
 */
function AesEncrypt(hexWord) {
  let srcs = CryptoJS.enc.Hex.parse(hexWord);
  let encrypted = CryptoJS.AES.encrypt(srcs, key, cfg);
  return encrypted.ciphertext.toString().toUpperCase();
}



/**
 * aes解密方法,返回16進制字符串
 * @param {string} hexWord 16進制字符串
 */
function AesDecrypt(hexWord) {
  let encryptedHexStr = CryptoJS.enc.Hex.parse(hexWord);
  let srcs = CryptoJS.enc.Base64.stringify(encryptedHexStr);
  let decrypt = CryptoJS.AES.decrypt(srcs, key, cfg);
  let decryptedStr = decrypt.toString(CryptoJS.enc.Hex);
  return decryptedStr.toString();
}



/**
 *  將字符轉爲base64編碼
 *  @param {string} utf8Str  字符串,utf8編碼
 */
function Base64Encode(utf8Str) {
  let str = CryptoJS.enc.Utf8.parse(utf8Str);
  let base64 = CryptoJS.enc.Base64.stringify(str);
  return base64;
}

/**
 *  還原base64編碼,utf8編碼
 *  @param {string} base64Str  base64編碼字符串
 */
function Base64Decode(base64Str) {
  let words = CryptoJS.enc.Base64.parse(base64Str);
  return words.toString(CryptoJS.enc.Utf8);
}

//暴露接口
module.exports = {
  SetHexKey,
  AesEncrypt,
  AesDecrypt,
  Base64Encode,
  Base64Decode
}


3DES(TripleDES)

  • 注意3des的祕鑰長度是24字節,有些人發現js和java的加密結果不同,其中可能的原因就是密鑰長度不同導致,因爲不管是java還是js,如果密鑰長度不足24個字節,它們都有各自的策略去補到24個字節,而策略不同,導致實際的密鑰是不一樣的,因此纔會產生不同的結果
  • 偏移量一般是8個字節
  • 下面的java代碼中(其他代碼會幫你補齊還是報錯我就不得而知了,感覺這裏應該直接報錯纔好,查了老半天才知道是背地裏偷偷幫我補齊到24個字節,但使用者完全不知道),如果密鑰不足24個字節,比如只有16個字節,那麼默認會將前8個字節補到後面湊齊24個字節,比如你在java裏寫的密鑰是00112233445566778899AABBCCDDEEFF,那麼實際上的密鑰是00112233445566778899AABBCCDDEEFF0011223344556677,因此如果在web平臺js裏不能用00112233445566778899AABBCCDDEEFF當做密鑰,要用00112233445566778899AABBCCDDEEFF0011223344556677去加解密,才能正確解出結果
  • 當然會造成這些問題都是因爲對3des不夠熟悉造成的,如果熟悉就會用24個字節作爲密鑰,就不會出現這種需要補齊的問題,因此坑都是自己挖的,還得自己好好填

代碼示例

java

import org.apache.commons.codec.binary.Base64;

import javax.crypto.*;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.spec.AlgorithmParameterSpec;


public class TripleDESUtil {

	public static void main(String[] args) throws Exception{
		encryptTest();//8FB06F38DCEBA75C3D2B3D6257EEAEB0
	}
	
    public static String encryptTest(){
        try {
            byte[] src = hexStringToByte("61207375706572206d616e2104040404");
            byte[] key = hexStringToByte("00112233445566778899AABBCCDDEEFF1122334455667788");
              byte[] ivs = new byte[] { 0x0, 0x0, 0x0,0x0, 0x0, 0x0, 0x0,0x0};
              IvParameterSpec iv = new IvParameterSpec(ivs);
            SecretKey deskey = new SecretKeySpec(key, "desede"); // 生成密鑰21
            Cipher c1 = Cipher.getInstance("DESede/CBC/NoPadding"); // 實例化負責加密/解密的Cipher工具類22
            c1.init(Cipher.ENCRYPT_MODE, deskey, iv); // 初始化爲加密模式23
            return bytesToHexString(c1.doFinal(src));
        } catch (java.security.NoSuchAlgorithmException e1) {
            e1.printStackTrace();
        } catch (javax.crypto.NoSuchPaddingException e2) {
            e2.printStackTrace();
        } catch (java.lang.Exception e3) {
            e3.printStackTrace();
        }
        return null;
    }

    public static final String bytesToHexString(byte[] var0) {
        if (var0 == null) {
            return "";
        } else {
            StringBuffer var1 = new StringBuffer(var0.length);

            for(int var3 = 0; var3 < var0.length; ++var3) {
                String var2 = Integer.toHexString(255 & var0[var3]);
                if (var2.length() < 2) {
                    var1.append(0);
                }

                var1.append(var2.toUpperCase());
            }

            return var1.toString();
        }
    }

    public static byte[] hexStringToByte(String var0) {
        if (var0 != null && !var0.isEmpty()) {
            var0 = var0.toUpperCase();
            int var1 = var0.length() / 2;
            byte[] var2 = new byte[var1];
            char[] var3 = var0.toCharArray();

            for(int var4 = 0; var4 < var1; ++var4) {
                int var5 = var4 * 2;
                var2[var4] = (byte)(toByte(var3[var5]) << 4 | toByte(var3[var5 + 1]));
            }

            return var2;
        } else {
            return null;
        }
    }

    private static byte toByte(char var0) {
        byte var1 = (byte)"0123456789ABCDEF".indexOf(var0);
        return var1;
    }

}

js

var CryptoJS = require("../crypto/index.js")


var iv = CryptoJS.enc.Hex.parse("db1c29b3a9093117f773295b3f07bf93")
//var iv = CryptoJS.enc.Hex.parse("11223344556677889900")
//var key = CryptoJS.enc.Hex.parse("bc7a3dc9e79563291d7bfa93df67d545")
var key = CryptoJS.enc.Hex.parse("db1c29b3a9093117f773295b3f07bf93")//祕鑰(16進制字符串)

//加密設置,根據實際情況修改
var cfg = {
  mode: CryptoJS.mode.CBC,
  // padding: CryptoJS.pad.Pkcs7 //pkcs7是默認的
  padding: CryptoJS.pad.NoPadding, //本套協議採用的nopadding
  iv: iv
};

/**
 * 設置3des祕鑰,在加解密之前必須設置
 * 
 * @param {string} hexKey  祕鑰,16進制字符串
 */
function setHexKey(hexKey){
  key = CryptoJS.enc.Hex.parse(hexKey)
}

/**
 * 設置3des偏移量,在加解密之前必須設置
 * 
 * @param {string} hexKey  祕鑰,16進制字符串
 */
function setHexIV(hexIV) {
  iv = CryptoJS.enc.Hex.parse(hexIV)
  cfg.iv=iv
}

/**
 * 加密方法,返回16進制字符串
 * 
 * @param {string} hexWord  16進制字符串,必須是8個字節的倍數
 */
function encrypt(hexWord) {
  let srcs = CryptoJS.enc.Hex.parse(hexWord)
  let encrypted = CryptoJS.TripleDES.encrypt(srcs, key, cfg)
  return encrypted.ciphertext.toString().toUpperCase()
}


/**
 * 解密方法,返回16進制字符串
 * @param {string} hexWord 16進制字符串
 */
function decrypt(hexWord) {
  let encryptedHexStr = CryptoJS.enc.Hex.parse(hexWord)
  let srcs = CryptoJS.enc.Base64.stringify(encryptedHexStr)
  let decrypt = CryptoJS.TripleDES.decrypt(srcs, key, cfg)
  let decryptedStr = decrypt.toString(CryptoJS.enc.Hex)
  return decryptedStr.toString()

}


//暴露接口
module.exports = {
  setHexKey,
  setHexIV,
  encrypt,
  decrypt
}

crc校驗碼

  • js-crc - npm
    https://www.npmjs.com/package/js-crc

  • crc在線計算
    http://www.ip33.com/crc.html

工具

  • js工具類

/**
 * ArrayBuffer轉16進制字符串
 * @param {ArrayBuffer} buffer 
 */
function ab2hex(buffer) {
    let hexArr = Array.prototype.map.call(
      new Uint8Array(buffer),
      function(bit) {
        return ('00' + bit.toString(16)).slice(-2)
      }
    )
    return hexArr.join('');
  }

  /**
   * 16進制字符串轉ArrayBuffer
   * @param {String} hexStr 
   */
function hex2ab(hexStr){

    var typedArray = new Uint8Array(hexStr.match(/[\da-fA-F]{2}/gi).map(function (h) {
      return parseInt(h, 16)
    }))
    
    return typedArray.buffer
}


module.exports={
    ab2hex,
    hex2ab
}

參考

細說CryptoJs使用(微信小程序加密解密)-7855145-51CTO博客
https://blog.51cto.com/7865145/2090041

https://code.google.com/archive/p/crypto-js/downloads

brix/crypto-js: JavaScript library of crypto standards.
https://github.com/brix/crypto-js

JS中ArrayBuffer轉字符串,字符串轉ArrayBuffer,字符與字節橋轉換 - xyzdwf的博客 - CSDN博客
https://blog.csdn.net/xyzdwf/article/details/82220987

標準crc16,通用javascript,java,c語言 - 簡書
https://www.jianshu.com/p/0d810302ddff

js-crc - npm
https://www.npmjs.com/package/js-crc
crc在線計算
http://www.ip33.com/crc.html

java 3des加密問題記錄 - 無所事事O_o - 博客園
https://www.cnblogs.com/wsss/p/6925090.html

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