【數據加解密】四種加解密工具類的實現(Java)

       最近在項目中,接觸到了數據加解密的業務。數據加密技術是網絡中最基本的安全技術,主要是通過對網絡中傳輸的信息進行數據加密來保障其安全性,這是一種主動安全防禦策略,用很小的代價即可爲信息提供相當大的安全保護。在介紹如何實現之前,需要先來了解一下什麼是加密解密。
       

1、加密的基本概念 

        "加密",是一種限制對網絡上傳輸數據的訪問權的技術。原始數據(也稱爲明文,plaintext)被加密設備(硬件或軟件)和密鑰加密而產生的經過編碼的數據稱爲密文(ciphertext)將密文還原爲原始明文的過程稱爲解密,它是加密的反向處理,但解密者必須利用相同類型的加密設備和密鑰對密文進行解密。

加密的基本功能包括:

1. 防止不速之客查看機密的數據文件;

2. 防止機密數據被泄露或篡改;

3. 防止特權用戶(如系統管理員)查看私人數據文件;

4. 使入侵者不能輕易地查找一個系統的文件。

 

2、加解密工具類的實現

       在項目中我主要探究了4種加密技術的實現,分別是:Base64加解密、MD5加解密、AES加解密、DES加解密

2.1  Base64(可逆)  

         Base64是一種根據ASCII碼來進行加密的可逆算法,這類算法簡單來說,就等於將每個字符對應一個特定的字符(常常是根據進制進行匹配),一一對應,特別像電臺密碼本,因爲每個字符都必須要加密,加密比較笨重。

 Base64加密算法

1、獲取字符串中每個字符的ASCII碼

2、按照每3個8bit的字符爲一組來分組,即每組24bit;

3、將這24bit劃分成4個6bit的4個單位,每個單位前面添加2個0,得到4個8bit的單位;

4、將每個8bit的單位轉換成十進制數字,對照Base64編碼表找到對應的字符進行拼接,得到最終的加密後的字符串。

 Base64解密算法

1、讀入4個字符,對照Base64編碼表找到字符對應的索引,生成4個6爲的值;

2、將這4個6爲的值拼接起來,形成一個24爲的值;

3、將這個24位的值按照8位一組截斷成3個8位的值;

4、對照ASCII表找到這三個8位的值對應的字符,即解碼後的字符。

/** 工具類實現代碼  **/
public class BASE64Util {
    private static final Map<Integer, Character> base64CharMap = new HashMap<>();
    private static final String base64CharString = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

    private static BASE64Util instance;

    private BASE64Util() {
        for (int i = 0; i < base64CharString.length(); i++) {
            char c = base64CharString.charAt(i);
            base64CharMap.put(new Integer(i), new Character(c));
        }
    }

    public static BASE64Util getInstance() {
        if (instance == null) {
            synchronized (BASE64Util.class) {
                if (instance == null) {
                    instance = new BASE64Util();
                }
            }
        }
        return instance;
    }

    /**
     * This method is used to encode a normal string to base64 string @param
     * origin The String to be encoded @return The String after encoded.
     */
    public String encode(String origin) {
        if (origin == null) {
            return null;
        }
        if (origin.length() == 0) {
            return "";
        }
        int length = origin.length();
        String binaryString = "";
        // to binary String
        for (int i = 0; i < length; i++) {
            int ascii = origin.charAt(i);
            String binaryCharString = Integer.toBinaryString(ascii);
            while (binaryCharString.length() < 8) {
                binaryCharString = "0" + binaryCharString;
            }
            binaryString += binaryCharString;
        }

        // to base64 index
        int beginIndex = 0;
        int endIndex = beginIndex + 6;
        String base64BinaryString = "";
        String charString = "";
        while ((base64BinaryString = binaryString.substring(beginIndex, endIndex)).length() > 0) {
            // if length is less than 6, add "0".
            while (base64BinaryString.length() < 6) {
                base64BinaryString += "0";
            }
            int index = Integer.parseInt(base64BinaryString, 2);
            char base64Char = base64CharMap.get(index);
            charString = charString + base64Char;
            beginIndex += 6;
            endIndex += 6;
            if (endIndex >= binaryString.length()) {
                endIndex = binaryString.length();
            }
            if (endIndex < beginIndex) {
                break;
            }
        }
        if (length % 3 == 2) {
            charString += "=";
        }
        if (length % 3 == 1) {
            charString += "==";
        }
        return charString;
    }

    public String decode(String encodedString) {
        if (encodedString == null) {
            return null;
        }
        if (encodedString.length() == 0) {
            return "";
        }
        // get origin base64 String
        String origin = encodedString.substring(0, encodedString.indexOf("="));
        String equals = encodedString.substring(encodedString.indexOf("="));

        String binaryString = "";
        // convert base64 string to binary string
        for (int i = 0; i < origin.length(); i++) {
            char c = origin.charAt(i);
            int ascii = base64CharString.indexOf(c);
            String binaryCharString = Integer.toBinaryString(ascii);
            while (binaryCharString.length() < 6) {
                binaryCharString = "0" + binaryCharString;
            }
            binaryString += binaryCharString;
        }
        // the encoded string has 1 "=", means that the binary string has append
        // 2 "0"
        if (equals.length() == 1) {
            binaryString = binaryString.substring(0, binaryString.length() - 2);
        }
        // the encoded string has 2 "=", means that the binary string has append
        // 4 "0"
        if (equals.length() == 2) {
            binaryString = binaryString.substring(0, binaryString.length() - 4);
        }

        // convert to String
        String charString = "";
        String resultString = "";
        int beginIndex = 0;
        int endIndex = beginIndex + 8;
        while ((charString = binaryString.substring(beginIndex, endIndex)).length() == 8) {
            int ascii = Integer.parseInt(charString, 2);
            resultString += (char) ascii;
            beginIndex += 8;
            endIndex += 8;
            if (endIndex > binaryString.length()) {
                break;
            }
        }
        return resultString;
    }
}

2.2 MD5加密算法(不可逆)

         MD5碼加解密算法Message-Digest Algorithm 5(信息-摘要算法),是一種不可逆的算法,具有很高的安全性。它對應任何字符串都可以加密成一段唯一的固定長度的代碼。(小貼士:爲啥MD5加密算法不可逆呢~ 按道理來說有加密方式,就會有解密方式呀?因爲MD5加密是有種有損的加密方式,比如一段數據爲'123',我在加密的時候,遇到1和3都直接當做是a,加密後變成了'a2a',所以解密的時候就出現了4種組合'323''121''123''321',數據一多,自然找不到原始的數據了,當然這種方式加密的密文也不需要解密)

// MD5碼工具類代碼  
public class MD5Util {
    public static final String MD5 = "MD5";
    public static final String HmacMD5 = "HmacMD5";
    public static final String charset = null; // 編碼格式;默認null爲GBK

    private static MD5Util instance;

    private MD5Util() {
    }

    // 單例
    public static MD5Util getInstance() {
        if (instance == null) {
            synchronized (MD5Util.class) {
                if (instance == null) {
                    instance = new MD5Util();
                }
            }
        }
        return instance;
    }

    /**
     * 使用 MD5 方法加密(無密碼)
     */
    public String encode(String res) {
        try {
            MessageDigest md = MessageDigest.getInstance(MD5);
            byte[] resBytes = charset == null ? res.getBytes() : res.getBytes(charset);
            return BASE64Util.getInstance().encode(md.digest(resBytes).toString());
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 使用 MD5 方法加密(可以設密碼)
     */
    public String encode(String res, String key) {
        try {
            SecretKey sk = null;
            if (key == null) {
                KeyGenerator kg = KeyGenerator.getInstance(HmacMD5);
                sk = kg.generateKey();
            } else {
                byte[] keyBytes = charset == null ? key.getBytes() : key.getBytes(charset);
                sk = new SecretKeySpec(keyBytes, HmacMD5);
            }
            Mac mac = Mac.getInstance(HmacMD5);
            mac.init(sk);
            byte[] result = mac.doFinal(res.getBytes());
            return BASE64Util.getInstance().encode(result.toString());
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}

2.3 DES加密算法(可逆)

public class DESUtil {
    public static final String DES = "DES";
    public static final String charset = null; // 編碼格式;默認null爲GBK
    public static final int keysizeDES = 0;

    private static DESUtil instance;

    private DESUtil() {
    }

    // 單例
    public static DESUtil getInstance() {
        if (instance == null) {
            synchronized (MD5Util.class) {
                if (instance == null) {
                    instance = new DESUtil();
                }
            }
        }
        return instance;
    }

    /**
     * 使用 DES 進行加密
     */
    public String encode(String res, String key) {
        return keyGeneratorES(res, DES, key, keysizeDES, true);
    }

    /**
     * 使用 DES 進行解密
     */
    public String decode(String res, String key) {
        return keyGeneratorES(res, DES, key, keysizeDES, false);
    }

    // 使用KeyGenerator雙向加密,DES/AES,注意這裏轉化爲字符串的時候是將2進制轉爲16進制格式的字符串,不是直接轉,因爲會出錯
    private String keyGeneratorES(String res, String algorithm, String key, int keysize, boolean isEncode) {
        try {
            KeyGenerator kg = KeyGenerator.getInstance(algorithm);
            if (keysize == 0) {
                byte[] keyBytes = charset == null ? key.getBytes() : key.getBytes(charset);
                kg.init(new SecureRandom(keyBytes));
            } else if (key == null) {
                kg.init(keysize);
            } else {
                byte[] keyBytes = charset == null ? key.getBytes() : key.getBytes(charset);
                kg.init(keysize, new SecureRandom(keyBytes));
            }
            SecretKey sk = kg.generateKey();
            SecretKeySpec sks = new SecretKeySpec(sk.getEncoded(), algorithm);
            Cipher cipher = Cipher.getInstance(algorithm);
            if (isEncode) {
                cipher.init(Cipher.ENCRYPT_MODE, sks);
                byte[] resBytes = charset == null ? res.getBytes() : res.getBytes(charset);
                return parseByte2HexStr(cipher.doFinal(resBytes));
            } else {
                cipher.init(Cipher.DECRYPT_MODE, sks);
                return new String(cipher.doFinal(parseHexStr2Byte(res)));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    // 將二進制轉換成16進制
    private String parseByte2HexStr(byte buf[]) {
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < buf.length; i++) {
            String hex = Integer.toHexString(buf[i] & 0xFF);
            if (hex.length() == 1) {
                hex = '0' + hex;
            }
            sb.append(hex.toUpperCase());
        }
        return sb.toString();
    }

    // 將16進制轉換爲二進制
    private byte[] parseHexStr2Byte(String hexStr) {
        if (hexStr.length() < 1)
            return null;
        byte[] result = new byte[hexStr.length() / 2];
        for (int i = 0; i < hexStr.length() / 2; i++) {
            int high = Integer.parseInt(hexStr.substring(i * 2, i * 2 + 1), 16);
            int low = Integer.parseInt(hexStr.substring(i * 2 + 1, i * 2 + 2), 16);
            result[i] = (byte) (high * 16 + low);
        }
        return result;
    }
}


2.4 AES加密算法(可逆)

         AES(Advanced Encryption Standard,AES),是密碼學中的高級加密標準。這個標準用來替代原先的DES(Data Encryption Standard),已經被多方分析且廣爲全世界所使用。經過五年的甄選流程,高級加密標準由美國國家標準與技術研究院 (NIST)於2001年11月26日發佈於FIPS PUB 197,並在2002年5月26日成爲有效的標準。現在,高級加密標準已然成爲對稱密鑰加密中最流行的算法之一。

         AES也是,採用密鑰,意思是傳入一個需要加密的對象的同時,約定一個key(可以是字符串),然後解密端,通過這個key進行解密獲取對象。AES的實現原理,是先確定密鑰,確定方式:算法/模式/補碼。

public class AESUtil {

    /**
     * 加密
     * @return 加密後的字符串
     */
    public static String Encrypt(String src, String key) throws Exception {
        // 判斷密鑰是否爲空
        if (key == null) {
            System.out.print("密鑰不能爲空");
            return null;
        }

        // 密鑰補位
        int plus= 16-key.length();
        byte[] data = key.getBytes("utf-8");
        byte[] raw = new byte[16];
        byte[] plusbyte={ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f};
        for(int i=0;i<16;i++)
        {
            if (data.length > i)
                raw[i] = data[i];
            else
                raw[i] = plusbyte[0];
        }

        SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
        Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");    // 算法/模式/補碼方式
        cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
        byte[] encrypted = cipher.doFinal(src.getBytes("utf-8"));

        //return new Base64().encodeToString(encrypted);//base64
        return binary(encrypted, 16); //十六進制
    }

    /**
     * 解密
     * @return 解密後的字符串
     */
    public static String Decrypt(String src, String key) throws Exception {
        try {
            // 判斷Key是否正確
            if (key == null) {
                System.out.print("Key爲空null");
                return null;
            }

            // 密鑰補位
            int plus= 16-key.length();
            byte[] data = key.getBytes("utf-8");
            byte[] raw = new byte[16];
            byte[] plusbyte={ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f};
            for(int i=0;i<16;i++)
            {
                if (data.length > i)
                    raw[i] = data[i];
                else
                    raw[i] = plusbyte[0];
            }

            SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
            Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
            cipher.init(Cipher.DECRYPT_MODE, skeySpec);

            //byte[] encrypted1 = new Base64().decode(src);//base64
            byte[] encrypted1 = toByteArray(src);//十六進制

            try {
                byte[] original = cipher.doFinal(encrypted1);
                String originalString = new String(original,"utf-8");
                return originalString;
            } catch (Exception e) {
                System.out.println(e.toString());
                return null;
            }
        } catch (Exception ex) {
            System.out.println(ex.toString());
            return null;
        }
    }

    /**
     * 將byte[]轉爲各種進制的字符串
     * @param bytes byte[]
     * @param radix 可以轉換進制的範圍,從Character.MIN_RADIX到Character.MAX_RADIX,超出範圍後變爲10進制
     * @return 轉換後的字符串
     */
    public static String binary(byte[] bytes, int radix){
        return new BigInteger(1, bytes).toString(radix);   // 這裏的1代表正數
    }

    /**
     * 16進制的字符串表示轉成字節數組
     *
     * @param hexString 16進制格式的字符串
     * @return 轉換後的字節數組
     **/
    public static byte[] toByteArray(String hexString) {
        if (hexString.isEmpty())
            throw new IllegalArgumentException("this hexString must not be empty");

        hexString = hexString.toLowerCase();
        final byte[] byteArray = new byte[hexString.length() / 2];
        int k = 0;
        for (int i = 0; i < byteArray.length; i++) {//因爲是16進制,最多隻會佔用4位,轉換成字節需要兩個16進制的字符,高位在先
            byte high = (byte) (Character.digit(hexString.charAt(k), 16) & 0xff);
            byte low = (byte) (Character.digit(hexString.charAt(k + 1), 16) & 0xff);
            byteArray[i] = (byte) (high << 4 | low);
            k += 2;
        }
        return byteArray;
    }

}

3. 測試結果

 

4. 結論

       最後探究了幾種方法後,我選用了AES加密,因爲我認爲這類委託密鑰的方式,顯然是比較優秀的,而且不像md5碼不可逆,可以解密,比較符合業務需求。

以下是AES得加密解密實例代碼

 try {
          msg = AESUtil.Encrypt("hangli","love");  // 加密
      } catch (Exception e) {
          e.printStackTrace();
      }
/**
 * 進行解密
 */
 String msg2 = null;
 try {
          msg2 = AESUtil.Decrypt(msg,"love");
     } catch (Exception e) {
          e.printStackTrace();
     }
     System.out.println("加密:" + msg);
     System.out.println("解密:" + msg2);

     

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