1. 程式人生 > >Java程式設計師從笨鳥到菜鳥(六十九)常用加密方法

Java程式設計師從笨鳥到菜鳥(六十九)常用加密方法

前言

在這個資訊共享的時代,資訊保安無論是對於開發還是使用者來說都是重點關注的問題,例如在表單提交時,採用密文的方式來代替明文,可以相對有效避免重要資訊外洩,文中闡述了幾種比較常用的加密方法

加密方式

1、BASE64

嚴格來說是編碼格式,而非加密演算法;特點是加密解密是雙向的,可以求反解;主要是 BASE64Encoder、BASE64Decoder 兩個類;常見用於郵件、http 加密

測試程式碼實現:

package encoder;

import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;

/**
 * create by 
[email protected]
on 2018/12/13 9:22 * BASE64 嚴格來說是編碼格式,而非加密演算法 * 加密解密是雙向的,可以求反解 * 主要是 BASE64Encoder、BASE64Decoder 兩個類 * 常見於郵件、http 加密 **/ public class BASE64 { /** * BASE64 解密 * @param key 祕鑰 * @throws Exception 異常 * */ private static byte[] decryptBASE64(String key) throws Exception { return (new BASE64Decoder()).decodeBuffer(key); } /** * BASE64 加密 * @param key 祕鑰 * @throws Exception 異常 * */ private static String encrypyBASE64(byte[] key) throws Exception { return (new BASE64Encoder()).encodeBuffer(key); } public static void main(String[] args) { String str = "12345678"; try { System.out.println("str====加密前====" + str); String encodeData = BASE64.encrypyBASE64(str.getBytes()); System.out.println("encodeData====加密資料=====" + encodeData); byte[] decodeData = BASE64.decryptBASE64(encodeData); System.out.println("decodeData====解密資料=====" + new String(decodeData)); } catch (Exception e) { e.printStackTrace(); } } }

執行結果:

str====加密前====12345678
encodeData====加密資料=====MTIzNDU2Nzg=

decodeData====解密資料=====12345678

Process finished with exit code 0

2、MD5

MD5(Message Digest Algorithm 5) 資訊摘要演算法,用於確保資訊傳輸完整一致,是計算機廣泛使用的雜湊演算法之一(又稱摘要演算法、雜湊演算法),將資料(如漢字)運算為另一固定長度值,常用語檔案校驗;通常是不直接使用上述 MD5 加密,通常將 MD5 產生的位元組陣列交給 BASE64 再加密一把,得到相應的字串
具有以下特點:

  • 壓縮性:任意長度的資料,算出的 MD5 值長度是固定的
  • 容易計算:從原資料計算出 MD5 值很容易
  • 抗修改性:對於原資料進行任何更改,所得到的 MD5 值都有很大的區別
  • 弱抗碰撞:已知原資料和其 MD5 值,想找到一個具有相同 MD5 值的資料非常困難
  • 強抗碰撞:想找到兩個不同的資料,使他們具有相同的 MD5 值,非常困難
    測試程式碼實現:
package encoder;

import java.math.BigInteger;
import java.security.MessageDigest;

/**
 * create by [email protected] on 2018/12/13 10:02
 * MD5(Message Digest Algorithm 5) 資訊摘要演算法 用於確保資訊傳輸完整一致
 * 是計算機廣泛使用的雜湊演算法之一(又譯摘要演算法、雜湊演算法)
 * 將資料(如漢字)運算為另一固定長度值,是雜湊演算法的基礎原理,常用語檔案校驗
 * 通常不直接使用上述 MD5 加密,通常將 MD5 產生的位元組陣列交給 BASE64 再加密一把,得到相應的字串
 * 具有以下特點:
 * 1、壓縮性:任意長度的資料,算出的 MD5 值長度是固定的
 * 2、容易計算:從原資料計算出 MD5 值很容易
 * 3、抗修改性:對原資料進行任何更改,所得到的 MD5 值都有很大區別
 * 4、弱抗碰撞:已知原資料和其 MD5 值,想找到一個具有相同 MD5 值得資料時非常困難的
 * 5、強抗碰撞:想找到兩個不同的資料,使他們具有相同的 MD5 值,非常困難
 **/
public class MD5 {
    private static final String KEY_MD5 = "MD5";

    private static String getResult(String inputStr) {
        System.out.println("=====加密前的資料:" + inputStr);
        BigInteger bigInteger = null;

        try {
            MessageDigest md = MessageDigest.getInstance(KEY_MD5);
            byte[] inputData = inputStr.getBytes();
            md.update(inputData);
            bigInteger = new BigInteger(md.digest());
        } catch (Exception e) {
            e.printStackTrace();
        }
        return bigInteger.toString(16);
    }
    public static void main(String[] args) {
        try {
            String inputStr = "簡單加密8888888888";
            System.out.println("MD5 加密後:" + getResult(inputStr));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

執行結果:

=====加密前的資料:簡單加密8888888888
MD5 加密後:-60398b41d73fd45b9f90ce6a612ada8c

Process finished with exit code 0

3、SHA

SHA(Secure Hash Algorithm 安全雜湊演算法)主要適用於數字簽名標準(Digest Signature Standard DSS)裡面定義的數字簽名演算法
演算法思想:接收一段明文,然後以一種不可逆的方式將它轉換成一段(通常更小)密文
較之 MD5 更為安全
SHA-1 與 MD5 比較;因為二者均由 MD4 匯出,SHA-1 和 MD5 彼此很相似,但還有以下幾點不同:

  • 對強行攻擊的安全性:最顯著和最重要的區別是 SHA-1 摘要要比 MD5 摘要長 32 位
  • 對密碼分析的安全性:由於 MD5 的設計,易受密碼分析攻擊,SHA-1 不易受攻擊
  • 速度:在相同的硬體基礎上,SHA-1 的執行速度比 MD5 慢
    測試程式碼實現:
package encoder;

import java.math.BigInteger;
import java.security.MessageDigest;

/**
 * create by [email protected] on 2018/12/13 10:36
 * SHA (Secure Hash Algorithm 安全雜湊演算法)主要適用於數字簽名標準(Digest Signature Standard DSS)裡面定義的數字簽名演算法
 * (Digest Signature Algorithm DSA) 對於長度小於 2^64 位的訊息,SHA1 會產生一個 160 位的訊息摘要
 * 演算法思想:接收一段明文,然後以一種不可逆的方式將它轉換成一段(通常更小)密文
 * 較之 MD5 更為安全
 * SHA-1 與 MD5 的比較
 * 因為二者均由 MD4 匯出,SHA-1 和 MD5 彼此很相似,但還有以下幾點不同
 * 1、對強行攻擊的安全性:最顯著和最重要的區別是 SHA-1 摘要要比 MD5 摘要長 32 位
 * 2、對密碼分析的安全性:由於 MD5 的設計,易受密碼分析攻擊,SHA-1 不易受攻擊
 * 3、速度:在相同的硬體基礎上,SHA-1 的執行速度比 MD5 慢
 **/
public class SHA {
    private static final String KEY_SHA = "SHA";
    private static String getResult(String inputStr) {
        BigInteger bigInteger = null;
        System.out.println("=======加密前的資料:" + inputStr);
        byte[] inputData = inputStr.getBytes();
        try {
            MessageDigest messageDigest = MessageDigest.getInstance(KEY_SHA);
            messageDigest.update(inputData);
            bigInteger = new BigInteger(messageDigest.digest());
        } catch (Exception e) {
            e.printStackTrace();
        }
        return bigInteger.toString(32);
    }
    public static void main(String[] args) {
        try {
            String inputStr = "簡單加密";
            System.out.println("=======加密後的資料:" + getResult(inputStr));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

執行結果:

=======加密前的資料:簡單加密
=======加密後的資料:91k9vo7p400cjkgfhjh0ia9qthsjagfn

Process finished with exit code 0

4、AES

AES(Advanced Encryption Standard 高階加密標準)是現在對稱加密演算法中最流行的演算法之一
AES 可以使用 128、192 和 256 位金鑰,並且用 128 位分組加密和解密資料,相對來說安全
測試程式碼實現:

public class AES {
    private static String src = "TestAES";

    /**
     * AES 加密字串
     * @param content 需要被加密的字串
     * @param password 加密需要的密碼
     * @return 密文
     * */
    public static byte[] encrypt(String content, String password) {
        try {
            KeyGenerator keyGenerator = KeyGenerator.getInstance("AES"); // 建立 AES 的 key 生產者
            keyGenerator.init(128, new SecureRandom(password.getBytes())); // 利用使用者密碼作為隨機初始化出
            // 128 位的 key 生產者,SecureRandom 是生成安全隨機序列,password.getBytes() 是種子,只要種子相同,序列就一樣
            // 所以解密只要有 password 就行
            SecretKey secretKey = keyGenerator.generateKey(); // 根據使用者密碼,生成一個金鑰
            byte[] enCodeFormate = secretKey.getEncoded(); // 返回基本編碼格式的金鑰,如果此金鑰不支援編碼,則返回 null
            SecretKeySpec secretKeySpec = new SecretKeySpec(enCodeFormate, "AES"); // 轉換為 AES 專用金鑰
            Cipher cipher = Cipher.getInstance("AES"); // 建立密碼器
            byte[] byteContent = content.getBytes("utf-8");
            cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec); // 初始化為加密模式的密碼器
            byte[] result = cipher.doFinal(byteContent); // 加密
            return result;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * AES 解密 解密AES 加密的字串
     * @param content AES 加密過的內容
     * @param password 加密時的密碼
     * @return 明文
     * */
    public static byte[] decrypt(byte[] content, String password) {
        try {
            KeyGenerator keyGenerator = KeyGenerator.getInstance("AES"); // 建立 AES 的 key 生產者
            keyGenerator.init(128, new SecureRandom(password.getBytes()));
            SecretKey secretKey = keyGenerator.generateKey(); // 根據使用者密碼,生成一個金鑰
            byte[] enCodeFormat = secretKey.getEncoded(); // 返回基本編碼格式的金鑰
            SecretKeySpec key = new SecretKeySpec(enCodeFormat, "AES"); // 轉為 AES 專用金鑰
            Cipher cipher = Cipher.getInstance("AES"); // 建立密碼器
            cipher.init(Cipher.DECRYPT_MODE, key); // 初始化為解密模式的密碼器
            byte[] result = cipher.doFinal(content);
            return result; // 明文
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public static void main(String[] args) {
        String content = "美女,約嗎?";
        String password = "123";
        System.out.println("====加密前的內容:" + content);

        // 加密
        byte[] encrypt = encrypt(content, password);
        System.out.println("====加密後的內容,轉換前:" + new String(encrypt));
        System.out.println("====加密後的內容,轉換後:" + ParseSystemUtil.parseByte2HexStr(encrypt));

        // 解密
        byte[] decrypt = decrypt(encrypt, password);
        System.out.println("====解密後的內容:" + new String(decrypt));
    }
}

執行結果:

====加密前的內容:美女,約嗎?
====加密後的內容,轉換前:P�d�g�K�3�g�����,Ꝏ?U納�_
====加密後的內容,轉換後:50FE6401E867A34BD533FE67BB85EDABFED62CEA9D8E3F5516E7B48D01F21A5F
====解密後的內容:美女,約嗎?

Process finished with exit code 0

編碼轉換工具:
ParseSystemUtil 類實現:

package encoder;

/**
 * create by [email protected] on 2018/12/13 13:53
 * 位元組陣列和十六進位制之間的相互轉換
 **/
public class ParseSystemUtil {

    /**
     * 將位元組陣列轉換成十六進位制
     * @param buf 位元組陣列
     * @return 十六進位制字串
     * */
    public static 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();
    }

    /**
     * 將十六進位制轉換成位元組陣列
     * @param hex 十六進位制的字串
     * @return 位元組陣列
     * */
    public static byte[] parseHexStr2Byte(String hex) {
        if (hex.length() < 1) {
            return null;
        }
        byte[] result = new byte[hex.length() / 2];
        for (int i = 0; i < hex.length() / 2; i++) {
            int high = Integer.parseInt(hex.substring(i * 2, i * 2 + 1), 16);
            int low = Integer.parseInt(hex.substring(i * 2 + 1, i * 2 + 2), 16);
            result[i] = (byte)(high * 16 + low);
        }
        return result;
    }

    // 字串轉字元陣列
    public static char[] change(String s) {
        char[] result = new char[s.length()/2]; // 定義陣列
        for (int i = 0; i < result.length; i++)
            result[i] = (char)(Integer.parseInt(s.substring(2*i, 2*i + 2),16 & 0xFF));
        return result;
    }
}