和你一起走進對稱加密算法的世界 密碼的世界 加密的可逆性 DES 算法 3DES AES 算法 SM4 算法 第五大加密算法 小結

密碼的世界

如果你是黑幫老大,平時和手下溝通,如何保證自己的信息安全呢?

在神探夏洛克的第一季中,就講述了一個如何偵破黑幫的加密交流的故事。

這種密碼利用的是密碼字典。

密碼本身可以是一本書,比如常見的《聖經》、《殺死一隻知更鳥》,或者紐約地圖?

這種加密方式的優點就是如果不知道字典本身,基本無法破解。使用起來也非常簡單,甚至你可以定期和手下更換字典。

談到密碼,另一個不得不提的故事就是二戰時期的密碼破譯問題。

二戰時期,德國發明的 ENIGMA 加密機器,讓通訊加密從人工手寫時代跨越到了機器操作時代,也讓人工破譯有些無能爲力。

爲了破譯德國的這套加密機器,從劍橋找來了三位優秀的數學家:傑弗裏期、威爾仕曼、阿蘭.圖靈。

說到圖靈,我想大家一定都知道,如果不知道,建議收藏本篇文章,瞭解之後再繼續閱讀。

常言道,唯有魔法可以打敗魔法。那可以讓奇異博……啊,不好意思,還是讓圖靈來吧。

圖靈認爲運用數學上的 crib 方法來破解 ENIGMA 是可行的,在後期破譯了大部分的德軍情報信息。

以後的時代,我們用機器去打敗機器

加密的可逆性

加密算法我們整體可以分爲:可逆加密和不可逆加密;可逆加密又可以分爲:對稱加密和非對稱加密。

當然一般的通訊中,我們都是需要進行解密的。

本文主要介紹近代最有名的四大加密算法:DES 3DES AES 和 SM4。

DES 算法

簡介

DES 全稱爲 Data Encryption Standard,即數據加密標準,是一種使用密鑰加密的塊算法,1977年被美國聯邦政府的國家標準局確定爲聯邦資料處理標準(FIPS),並授權在非密級政府通信中使用,隨後該算法在國際上廣泛流傳開來。

設計原則

DES設計中使用了分組密碼設計的兩個原則:混淆(confusion)和擴散(diffusion),其目的是抗擊敵手對密碼系統的統計分析。

混淆是使密文的統計特性與密鑰的取值之間的關係儘可能複雜化,以使密鑰和明文以及密文之間的依賴性對密碼分析者來說是無法利用的。

擴散的作用就是將每一位明文的影響儘可能迅速地作用到較多的輸出密文位中,以便在大量的密文中消除明文的統計結構,並且使每一位密鑰的影響儘可能迅速地擴展到較多的密文位中,以防對密鑰進行逐段破譯。

ps: 基本近代的加密算法應該遵循這兩個準則,否則就會被統計攻擊。

入門使用

這裏提供了最簡單的 DES 實現例子。

import javax.crypto.*;
import javax.crypto.spec.DESKeySpec;
import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.spec.InvalidKeySpecException;

/**
 * DES 工具類
 *
 * @author binbin.hou
 * @since 0.0.6
 */
public final class DesUtil {

    private DesUtil() {
    }

    /**
     * des
     *
     * @since 0.0.6
     */
    private static final String DES = "DES";

    /**
     * 加密
     *
     * @param plainText 待加密內容
     * @param password  密碼
     * @return 加密結果
     * @since 0.0.6
     */
    public static byte[] encrypt(String plainText, String password) {
        byte[] bytes = plainText.getBytes();
        return encrypt(bytes, password);
    }

    /**
     * 加密
     *
     * @param plainText 待加密內容
     * @param password  密碼
     * @return 加密結果
     * @since 0.0.6
     */
    public static byte[] encrypt(byte[] plainText, String password) {
        try {
            SecureRandom random = new SecureRandom();
            DESKeySpec desKey = new DESKeySpec(password.getBytes());
            // 創建一個密匙工廠,然後用它把DESKeySpec轉換成
            SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(DES);
            SecretKey secretKey = keyFactory.generateSecret(desKey);
            // Cipher對象實際完成加密操作
            Cipher cipher = Cipher.getInstance(DES);
            // 用密匙初始化Cipher對象
            cipher.init(Cipher.ENCRYPT_MODE, secretKey, random);
            // 現在,獲取數據並加密
            // 正式執行加密操作
            return cipher.doFinal(plainText);
        } catch (Exception e) {
            throw new SecretRuntimeException(e);
        }
    }

    /**
     * 解密
     *
     * @param src      byte[]
     * @param password String
     * @return 解密結果
     * @since 0.0.6
     */
    public static byte[] decrypt(byte[] src, String password) {
        try {
            // DES算法要求有一個可信任的隨機數源
            SecureRandom random = new SecureRandom();
            // 創建一個DESKeySpec對象
            DESKeySpec desKey = new DESKeySpec(password.getBytes());
            // 創建一個密匙工廠
            SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(DES);
            // 將DESKeySpec對象轉換成SecretKey對象
            SecretKey secretKey = keyFactory.generateSecret(desKey);
            // Cipher對象實際完成解密操作
            Cipher cipher = Cipher.getInstance(DES);
            // 用密匙初始化Cipher對象
            cipher.init(Cipher.DECRYPT_MODE, secretKey, random);
            // 真正開始解密操作
            return cipher.doFinal(src);
        } catch (InvalidKeyException | NoSuchAlgorithmException | InvalidKeySpecException | NoSuchPaddingException | IllegalBlockSizeException | BadPaddingException e) {
            throw new SecretRuntimeException(e);
        }
    }

    /**
     * 解密
     *
     * @param src      byte[]
     * @param password String
     * @return 解密結果
     * @since 0.0.6
     */
    public static String decryptToString(byte[] src, String password,
                                         String charset) {
        try {
            byte[] bytes = decrypt(src, password);

            return new String(bytes, charset);
        } catch (UnsupportedEncodingException e) {
            throw new SecretRuntimeException(e);
        }
    }

    /**
     * 解密
     *
     * @param src      byte[]
     * @param password String
     * @return 解密結果
     * @since 0.0.6
     */
    public static String decryptToString(byte[] src, String password) {
        return decryptToString(src, password, "UTF-8");
    }

}

測試代碼

測試代碼如下:

public static void main(String[] args) {
    // 待加密內容
    String str = "測試內容";
    // 密碼,長度要是8的倍數
    String password = "01234567";
    byte[] result = DesUtil.encrypt(str, password);
    System.out.println("加密後:" + HexUtil.byteToHexString(result));
    // 直接將如上內容解密
    String decryResult = DesUtil.decryptToString(result, password);
    System.out.println("解密後:" + decryResult);
}

日誌如下:

加密後:77C25C0143F544CFFF102E43BDE1ABE1
解密後:測試內容

拓展閱讀

具體算法原理建議閱讀:

DES 加密算法入門及算法原理:http://houbb.github.io/2020/06/17/althgorim-cryptograph-05-des

3DES

算法介紹

3DES(或稱爲Triple DES)是三重數據加密算法(TDEA,Triple Data Encryption Algorithm)塊密碼的通稱。

它相當於是對每個數據塊應用三次 DES 加密算法。

由於計算機運算能力的增強,原版DES密碼的密鑰長度變得容易被暴力破解;

3DES 即是設計用來提供一種相對簡單的方法,即通過增加DES的密鑰長度來避免類似的攻擊,而不是設計一種全新的塊密碼算法

java 入門

3DES 的工具類實現如下:

import com.github.houbb.heaven.constant.CharsetConst;
import com.github.houbb.secret.api.exception.SecretRuntimeException;

import javax.crypto.*;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;

/**
 * 3DES 工具類
 *
 * @author binbin.hou
 * @since 0.0.7
 */
public final class TripleDesUtil {

    private TripleDesUtil() {
    }

    /**
     * 算法名稱
     *
     * @since 0.0.7
     */
    private static final String ALGORITHM = "DESede";

    /**
     * 加密函數
     *
     * @param keyBytes 加密密鑰,長度爲24字節
     * @param plainBytes 被加密的數據緩衝區(源)
     * @return 結果
     * @since 0.0.7
     */
    public static byte[] encrypt(byte[] keyBytes, byte[] plainBytes) {
        try {
            // 生成密鑰
            SecretKey deskey = new SecretKeySpec(keyBytes, ALGORITHM);
            // 加密
            Cipher c1 = Cipher.getInstance(ALGORITHM);
            c1.init(Cipher.ENCRYPT_MODE, deskey);
            return c1.doFinal(plainBytes);
        } catch (Exception e1) {
            throw new SecurityException(e1);
        }
    }

    /**
     * 加密函數
     *
     * @param keyBytes 加密密鑰,長度爲24字節
     * @param plainText 被加密的數據緩衝區(源)
     * @return 結果
     * @since 0.0.7
     */
    public static byte[] encrypt(byte[] keyBytes, String plainText) {
        return encrypt(keyBytes, plainText.getBytes());
    }

    /**
     * 解密函數
     * @param keyBytes 加密密鑰,長度爲24字節
     * @param secretBytes 加密後的緩衝區
     * @return 結果
     * @since 0.0.7
     */
    public static byte[] decrypt(byte[] keyBytes, byte[] secretBytes) {
        try {
            // 生成密鑰
            SecretKey deskey = new SecretKeySpec(keyBytes, ALGORITHM);
            // 解密
            Cipher c1 = Cipher.getInstance(ALGORITHM);
            c1.init(Cipher.DECRYPT_MODE, deskey);
            return c1.doFinal(secretBytes);
        } catch (Exception e1) {
            throw new SecretRuntimeException(e1);
        }
    }

    /**
     * 解密函數
     * @param keyBytes 加密密鑰,長度爲24字節
     * @param secretBytes 加密後的緩衝區
     * @param charsetName 編碼名稱
     *
     * @return 結果
     * @since 0.0.7
     */
    public static String decryptToString(byte[] keyBytes, byte[] secretBytes, String charsetName) {
        try {
            byte[] bytes = decrypt(keyBytes, secretBytes);

            return new String(bytes, charsetName);
        } catch (UnsupportedEncodingException e) {
            throw new SecretRuntimeException(e);
        }
    }

    /**
     * 解密函數
     * @param keyBytes 加密密鑰,長度爲24字節
     * @param secretBytes 加密後的緩衝區
     *
     * @return 結果
     * @since 0.0.7
     */
    public static String decryptToString(byte[] keyBytes, byte[] secretBytes) {
        return decryptToString(keyBytes, secretBytes, CharsetConst.UTF8);
    }

    
}

測試

public static void main(String[] args) {
    String text = "我愛中華!";
    String password = "123456781234567812345678";
    byte[] bytes = encrypt(password.getBytes(), text);
    System.out.println(HexUtil.byteToHexString(bytes));
    String plainText = decryptToString(password.getBytes(), bytes);
    System.out.println(plainText);
}

日誌信息:

A60CBC97EEFF2958DF4384215E0838C0
我愛中華!

拓展閱讀

具體算法原理建議閱讀:

3DES 加密算法入門及算法原理:http://houbb.github.io/2020/06/17/althgorim-cryptograph-06-3des

AES 算法

算法簡介

密碼學中的高級加密標準(Advanced Encryption Standard,AES),又稱Rijndael加密法,是美國聯邦政府採用的一種區塊加密標準。

2006年,高級加密標準已然成爲對稱密鑰加密中最流行的算法之一。

這種算法比 3DES 的安全性更高。

java 入門

java 的工具類實現如下。

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import java.io.UnsupportedEncodingException;
import java.security.SecureRandom;

/**
 * 3DES 工具類
 *
 * @author binbin.hou
 * @since 0.0.7
 */
public final class AesUtil {

    private AesUtil() {
    }

    /**
     * 算法名稱
     *
     * @since 0.0.7
     */
    private static final String ALGORITHM = "AES";

    /**
     * 根據密鑰對指定的明文plainText進行加密.
     *
     * @param plainBytes 明文
     * @param keyBytes   密碼
     * @return 加密後的密文.
     * @since 0.0.8
     */
    public static byte[] encrypt(byte[] plainBytes, byte[] keyBytes) {
        try {
            SecretKey secretKey = getSecretKey(keyBytes);
            Cipher cipher = Cipher.getInstance(ALGORITHM);
            cipher.init(Cipher.ENCRYPT_MODE, secretKey);
            return cipher.doFinal(plainBytes);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 根據密鑰對指定的密文 cipherBytes 進行解密.
     *
     * @param cipherBytes 加密密文
     * @param keyBytes    祕鑰
     * @return 解密後的明文.
     * @since 0.0.8
     */
    public static byte[] decrypt(byte[] cipherBytes, byte[] keyBytes) {
        try {
            SecretKey secretKey = getSecretKey(keyBytes);

            Cipher cipher = Cipher.getInstance(ALGORITHM);
            cipher.init(Cipher.DECRYPT_MODE, secretKey);
            return cipher.doFinal(cipherBytes);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 獲取加密 key
     *
     * @param keySeed seed
     * @return 結果
     * @since 0.0.8
     */
    private static SecretKey getSecretKey(byte[] keySeed) {
        try {
            // 避免 linux 系統出現隨機的問題
            SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG");
            secureRandom.setSeed(keySeed);
            KeyGenerator generator = KeyGenerator.getInstance("AES");
            generator.init(secureRandom);
            return generator.generateKey();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
    
}

測試代碼

public static void main(String[] args) throws UnsupportedEncodingException {
    String text = "我愛中華!";
    // 密鑰, 256位32個字節
    String password = "uBdUx82vPHkDKb284d7NkjFoNcKWBuka";
    byte[] bytes = encrypt(text.getBytes(), password.getBytes());
    String text2 = new String(decrypt(bytes, password.getBytes()), "UTF-8");
    System.out.println(text2);
}

輸出:我愛中華!

拓展閱讀

具體算法原理建議閱讀:

AES 加密算法入門及算法原理:http://houbb.github.io/2020/06/17/althgorim-cryptograph-07-aes

SM4 算法

算法簡介

SM4是一種分組密碼算法,其分組長度爲128位(即16字節,4字),密鑰長度也爲128位(即16字節,4字)。

其加解密過程採用了32輪迭代機制(與DES、AES類似),每一輪需要一個輪密鑰(與DES、AES類似)。

SM4 算法,又稱國密算法。因爲這是屬於中國人自己的加密算法,國內的金融等領域都會使用。

java 入門

maven 依賴

<dependency>
    <groupId>org.bouncycastle</groupId>
    <artifactId>bcprov-jdk15on</artifactId>
    <version>1.59</version>
</dependency>

工具封裝

import com.github.houbb.secret.api.exception.SecretRuntimeException;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.pqc.math.linearalgebra.ByteUtils;

import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.security.Key;
import java.security.Security;
import java.util.Arrays;

/**
 * Sm4 國密算法
 *
 * @author binbin.hou
 * @since 0.0.5
 */
public final class Sm4Util {

    private Sm4Util() {
    }

    static {
        Security.addProvider(new BouncyCastleProvider());
    }

    private static final String ENCODING = "UTF-8";

    private static final String ALGORITHM_NAME = "SM4";

    /**
     * PKCS5Padding  NoPadding 補位規則,PKCS5Padding缺位補0,NoPadding不補
     */
    private static final String ALGORITHM_NAME_ECB_PADDING = "SM4/ECB/PKCS5Padding";

    /**
     * ECB加密模式,無向量
     * @param algorithmName 算法名稱
     * @param mode          模式
     * @param key           key
     * @return 結果
     */
    private static Cipher generateEcbCipher(String algorithmName, int mode, byte[] key) throws Exception {
        Cipher cipher = Cipher.getInstance(algorithmName, BouncyCastleProvider.PROVIDER_NAME);
        Key sm4Key = new SecretKeySpec(key, ALGORITHM_NAME);
        cipher.init(mode, sm4Key);
        return cipher;
    }

    /**
     * sm4加密
     * 加密模式:ECB 密文長度不固定,會隨着被加密字符串長度的變化而變化
     *
     * @param hexKey   16進制密鑰(忽略大小寫)
     * @param plainText 待加密字符串
     * @return 返回16進制的加密字符串
     * @since 0.0.5
     */
    public static String encryptEcb(String hexKey, String plainText) {
        try {
            String cipherText = "";
            // 16進制字符串-->byte[]
            byte[] keyData = ByteUtils.fromHexString(hexKey);
            // String-->byte[]
            //當加密數據爲16進制字符串時使用這行
            byte[] srcData = plainText.getBytes(ENCODING);
            // 加密後的數組
            byte[] cipherArray = encryptEcbPadding(keyData, srcData);
            // byte[]-->hexString
            cipherText = ByteUtils.toHexString(cipherArray);
            return cipherText;
        } catch (Exception exception) {
            throw new SecretRuntimeException(exception);
        }
    }

    /**
     * 加密模式之Ecb
     *
     * @param key 祕鑰
     * @param data 待加密的數據
     * @return 字節數組
     * @since 0.0.5
     */
    public static byte[] encryptEcbPadding(byte[] key, byte[] data) {
        try {
            //聲稱Ecb暗號,通過第二個參數判斷加密還是解密
            Cipher cipher = generateEcbCipher(ALGORITHM_NAME_ECB_PADDING, Cipher.ENCRYPT_MODE, key);
            return cipher.doFinal(data);
        } catch (Exception exception) {
            throw new SecretRuntimeException(exception);
        }
    }

    //解密****************************************

    /**
     * sm4解密
     *
     * 解密模式:採用ECB
     * @param hexKey     16進制密鑰
     * @param cipherText 16進制的加密字符串(忽略大小寫)
     * @return 解密後的字符串
     * @since 0.0.5
     */
    public static String decryptEcb(String hexKey, String cipherText) {
        try {
            // 用於接收解密後的字符串
            String decryptStr = "";
            // hexString-->byte[]
            byte[] keyData = ByteUtils.fromHexString(hexKey);
            // hexString-->byte[]
            byte[] cipherData = ByteUtils.fromHexString(cipherText);
            // 解密
            byte[] srcData = decryptEcbPadding(keyData, cipherData);
            // byte[]-->String
            decryptStr = new String(srcData, ENCODING);
            return decryptStr;
        } catch (Exception exception) {
            throw new SecretRuntimeException(exception);
        }
    }

    /**
     * 解密
     *
     * @param key 祕鑰
     * @param cipherText 密文
     * @return 結果
     * @since 0.0.5
     */
    public static byte[] decryptEcbPadding(byte[] key, byte[] cipherText) {
        try {
            //生成Ecb暗號,通過第二個參數判斷加密還是解密
            Cipher cipher = generateEcbCipher(ALGORITHM_NAME_ECB_PADDING, Cipher.DECRYPT_MODE, key);
            return cipher.doFinal(cipherText);
        } catch (Exception exception) {
            throw new SecretRuntimeException(exception);
        }
    }

    /**
     * 驗證數據
     * @param hexKey key
     * @param cipherText 密文
     * @param plainText 明文
     * @return 結果
     * @since 0.0.5
     */
    public static boolean verifyEcb(String hexKey, String cipherText, String plainText) {
        try {
            // 用於接收校驗結果
            boolean flag = false;
            // hexString-->byte[]
            byte[] keyData = ByteUtils.fromHexString(hexKey);
            // 將16進制字符串轉換成數組
            byte[] cipherData = ByteUtils.fromHexString(cipherText);
            // 解密
            byte[] decryptData = decryptEcbPadding(keyData, cipherData);
            // 將原字符串轉換成byte[]
            byte[] srcData = plainText.getBytes(ENCODING);
            // 判斷2個數組是否一致
            flag = Arrays.equals(decryptData, srcData);
            return flag;
        } catch (Exception exception) {
            throw new SecretRuntimeException(exception);
        }
    }

}

測試代碼

System.out.println("開始****************************");
String plainText = "96C63180C2806ED1F47B859DE501215B";
System.out.println("加密前:" + plainText);
//自定義的32位16進制祕鑰
String key = "86C63180C2806ED1F47B859DE501215B";
String cipher = encryptEcb(key, plainText);//sm4加密
System.out.println("加密後:" + cipher);
//校驗加密前後是否爲同一數據
System.out.println("校驗:" + verifyEcb(key, cipher, plainText));
plainText = decryptEcb(key, cipher);//解密
System.out.println("解密後:" + plainText);
System.out.println("結束****************************");

對應日誌:

開始****************************
加密前:96C63180C2806ED1F47B859DE501215B
加密後:063c352bcec7d360da455ebaab2595347d0aa493d2a80a72396771b5585a49f81642326904c036af50b50f92e86cb274
校驗:true
解密後:96C63180C2806ED1F47B859DE501215B
結束****************************

拓展閱讀

具體算法原理建議閱讀:

SM4 加密算法入門及算法原理:http://houbb.github.io/2020/06/17/althgorim-cryptograph-04-sm4

第五大加密算法

當然,四大加密算法有 5 個,這不是常識嗎?

安德魯:爲什麼,我怎麼不知道?

我們最後再聊一聊另一個常見的算法 Base64。

算法介紹

嚴格地說,Base64 並不是用於加密的,更多的用於編碼,解碼。

Base64是一種能將任意Binary資料用64種字元組合成字串的方法,而這個Binary資料和字串資料彼此之間是可以互相轉換的,十分方便。

在實際應用上,Base64除了能將Binary資料可視化之外,也常用來表示字串加密過後的內容。

java 工具類

import com.github.houbb.heaven.util.lang.StringUtil;
import com.github.houbb.secret.api.exception.SecretRuntimeException;

import java.io.UnsupportedEncodingException;

/**
 * Base64 工具類
 *
 * 轉碼類
 * @author binbin.hou
 * @since 0.0.4
 */
public final class Base64Util {

    private Base64Util() {
    }

    private static final char[] ALPHABET_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".toCharArray();

    private static final byte[] CODES = new byte[256];

    static {
        for (int i = 0; i < 256; i++) {
            CODES[i] = -1;
        }
        for (int i = 'A'; i <= 'Z'; i++) {
            CODES[i] = (byte) (i - 'A');
        }
        for (int i = 'a'; i <= 'z'; i++) {
            CODES[i] = (byte) (26 + i - 'a');
        }
        for (int i = '0'; i <= '9'; i++) {
            CODES[i] = (byte) (52 + i - '0');
        }
        CODES['+'] = 62;
        CODES['/'] = 63;
    }

    /**
     * 將原始數據編碼爲 base64 編碼
     *
     * @param data 數據
     * @since 0.0.4
     */
    public static char[] encode(byte[] data) {
        char[] out = new char[((data.length + 2) / 3) * 4];

        for (int i = 0, index = 0; i < data.length; i += 3, index += 4) {
            boolean quad = false;
            boolean trip = false;
            int val = (0xFF & (int) data[i]);
            val <<= 8;
            if ((i + 1) < data.length) {
                val |= (0xFF & (int) data[i + 1]);
                trip = true;
            }
            val <<= 8;
            if ((i + 2) < data.length) {
                val |= (0xFF & (int) data[i + 2]);
                quad = true;
            }
            out[index + 3] = ALPHABET_CHARS[(quad ? (val & 0x3F) : 64)];
            val >>= 6;
            out[index + 2] = ALPHABET_CHARS[(trip ? (val & 0x3F) : 64)];
            val >>= 6;
            out[index + 1] = ALPHABET_CHARS[val & 0x3F];
            val >>= 6;
            out[index + 0] = ALPHABET_CHARS[val & 0x3F];
        }
        return out;
    }

    /**
     * 將 base64 編碼的數據解碼成原始數據
     *
     * @param data 數組
     * @since 0.0.4
     */
    public static byte[] decode(char[] data) {
        int len = ((data.length + 3) / 4) * 3;
        if (data.length > 0 && data[data.length - 1] == '=') {
            --len;
        }
        if (data.length > 1 && data[data.length - 2] == '=') {
            --len;
        }
        byte[] out = new byte[len];
        int shift = 0;
        int accum = 0;
        int index = 0;
        for (char datum : data) {
            int value = CODES[datum & 0xFF];
            if (value >= 0) {
                accum <<= 6;
                shift += 6;
                accum |= value;
                if (shift >= 8) {
                    shift -= 8;
                    out[index++] = (byte) ((accum >> shift) & 0xff);
                }
            }
        }

        if (index != out.length) {
            throw new SecretRuntimeException("miscalculated data length!");
        }
        return out;
    }

    /**
     * 編碼
     * @param text 文本
     * @return 結果
     * @since 0.0.4
     */
    public static char[] encode(String text) {
        if(StringUtil.isEmpty(text)) {
            return new char[]{};
        }
        byte[] data = text.getBytes();

        return encode(data);
    }

    /**
     * 編碼爲字符串
     *
     * @param text 文本
     * @return 結果
     * @since 0.0.4
     */
    public static String encodeToString(String text) {
        if(StringUtil.isEmpty(text)) {
            return text;
        }
        char[] chars = encode(text);

        return new String(chars);
    }

    /**
     * 將 base64 編碼的數據解碼成原始數據
     *
     * @param text 解碼
     * @since 0.0.4
     */
    public static byte[] decode(String text) {
        if(StringUtil.isEmpty(text)) {
            return new byte[]{};
        }

        char[] chars = text.toCharArray();
        return decode(chars);
    }

    /**
     * 將 base64 編碼的數據解碼成原始數據
     *
     * @param text 解碼
     * @param charset 編碼
     * @since 0.0.4
     */
    public static String decodeToString(String text, String charset) {
        try {
            byte[] bytes = decode(text);

            return new String(bytes, charset);
        } catch (UnsupportedEncodingException e) {
            throw new SecretRuntimeException(e);
        }
    }

}

測試

public static void main(String[] args) {
    String text = "我愛中國!";
    String base64 = encodeToString(text);
    System.out.println(base64);
    String decode64 = decodeToString(base64, "UTF-8");
    System.out.println(decode64);
}

輸出如下:

5oiR54ix5Lit5Zu9IQ==
我愛中國!

拓展閱讀

具體算法原理建議閱讀:

BASE64 加密算法入門及算法原理:http://houbb.github.io/2020/06/17/althgorim-cryptograph-03-base64

小結

信息時代,一直在追求 2 個指標:速度與安全(不是激情)。

安全算法也永遠隨着時代的進步而不斷演變,是一場永不停息的攻防之戰。

針對本文的所有算法,我都做了統一的編碼實現彙總,便於大家使用。

另外,有時間我們可以聊一聊不可逆加密,以及非對稱加密。

我是老馬,期待與你的下次重逢。

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