編碼、摘要和加密(三)——數據加密

0. 前言

關於加密,此處沒有更加通俗易懂的解釋。與同是對於字節類型數據處理的編碼和摘要對比:git

  1. 編碼是可逆的,任何人只要知道編碼規則,就可以進行解碼。
  2. 摘要是不可逆的,即便知道只要算法的實現原理,也很難還原出原數據。

加密是可逆的,但只知道加密算法並不能解密,還須要知道加密密鑰。github

接下來,將針對幾個常見的加密算法:DES3DESAESRSA 的 Java 實現及其相關進行介紹,因爲以前實現過 DESAES 算法,所以具體算法說明,後續有空會寫到。算法

1. 相關概念

1.1 密鑰

在代碼實現的過程當中,密鑰便是 key 。使用對稱加密算法時,加密和解密是同一個密鑰。使用非對稱加密算法時,加密和解密密鑰不相同,區分爲公鑰(public key)和私鑰(private key)。編碼

見過把 base64 當作對稱加密, md5 當作非對稱加密,所以下面劃重點:加密

  1. 判別加密算法最直接的方式,是否須要密鑰。
  2. 對稱加密和非對稱加密區別在於,加解密是不是同一個key。
  3. 對稱加密算法效率優於非對稱加密算法,建議用對稱加密算法加密長數據,非對稱加密算法加密端數據。

1.2 加密模式

加密模式主要體如今對稱加密算法中。以前提到過,對稱加密算法效率優,適合加密長數據。實際加密過程當中,是將長數據劃分紅固定長度的若干塊短數據進行加密操做。爲防止暴力破解得出明文,所以衍生了四種加密模式。spa

1.2.1 電子密碼本模式

英譯 Electronic Code Book ,簡稱 ECB 模式,最簡單的加密模式。code

  1. 將長數據分割成固定長度的若干塊。
  2. 分別對每塊數據用同一個密鑰進行加密。
  3. 將每塊加密出來的密文合併拼接成最終的完整密文。

上述步驟存在一個嚴重的問題,若是有重複的明文塊,那麼加密出來的密文也重複。對象

1.2.2 加密塊鏈模式

英譯 Cipher Block Chaining ,簡稱 CBC,基於 ECB 模式的改進版。 此處引入一個概念:初始化向量 Initialization Vector 簡稱 IVip

  1. 將長數據分割成固定長度的若干塊。
  2. 將前一塊的密文與後一塊明文進行異或,再用密鑰進行加密。
  3. 將每塊加密出來的密文合併拼接成最終的完整密文。
  4. 第一塊明文沒有密文與其異或,所以須要 IV 對其異或再用密鑰加密。

1.2.3 加密反饋模式

英譯 Cipher Feedback Mode ,簡稱 CFBmd5

  1. 將長數據分割成固定長度的若干塊。
  2. 將前一塊密文使用密鑰進行加密,再與後一塊明文進行異或。
  3. 將每塊異或後的密文合併拼接成最終的完整密文。
  4. 第一塊明文須要與用密鑰加密後的 IV 進行異或。

1.2.4 輸出反饋模式

英譯 Output Feedback Mode , 簡稱 OFB ,與 CFB 模式有些細小的區別。

  1. 將長數據分割成固定長度的若干塊。
  2. 將前一塊中間密文使用密鑰進行加密得中間密文。中間密文與明文進行異或得密文。
  3. 將每一箇中間密文與明文塊異或後的密文進行合併拼接成最終的完整密文。
  4. 第一塊明文須要的中間密文是用密鑰加密後的 IV 。

CFBOFB 的區別在於中間密文和密文塊的用法 。

  1. CFB 使用前一塊的密文進行加密。
  2. OFB 使用前一塊的中間密文進行加密。

1.3 填充模式

分塊加密的過程當中,遇到不夠整分的塊。如,將 16 字節做爲一個明文塊。當加密 17 字節時,不夠分紅兩塊。此時須要對第二塊明文進行填充。填充後的兩個明文塊各 16 字節共 32 字節後再進行加密操做。

後 15 字節的填充內容,須要取決於具體的填充模式。見後續 Java 代碼實現中介紹。

2. 代碼實現

Java 對加密部分作了比較完整的封裝—— Cipher 類。

如下列舉幾個主要方法:

  1. getInstance 獲取 Cipher 對象,主要接收轉換類型參數對象。轉換類型參數分爲 算法算法/模式/填充
  2. init 初始化加密參數。包括指定加解密模式、密鑰和初始化向量-IV
  3. doFinal 結束加密和解密操做,有多個重載方法,主要接收須要加密或解密的數據。

關於填充,以前簡要介紹過。在 Java 代碼中常見的填充模式是 PKCS5Padding 。還有一種模式 NoPadding 因爲對明文長度有要求,不建議使用。其餘填充模式未深刻了解,暫不誤導。

PKCS5Padding 指,須要填充多少字節,就填充多少個字節的數字。如 DES 算法要求每一個明文塊 8 字節,那麼,加密 1 字節數據,須要填充 7 個字節,此時填充 7 個 7 。加密 7 字節數據,須要填充 1 個 1 。加密 8 字節數據時,爲方便校驗解密後的明文正確性,須要擴展成 16 字節數據,此時第二個明文塊填充 8 個 8 。

2.1 DES

DES 全稱 Data Encryption Standard ,數據加密標準算法。固定密鑰 8 字節,64 位。每一個明文塊長度 8 字節。

getInstance 接收參數:DES/ECB/PKCS5Padding ,其中 ECB 表示加密模式,能夠用上述的其餘三個模式替換以及更多 JDK 支持的模式。PKCS5Padding 表示一種填充模式。

在使用 CBCCFBOFB 時,須要在 init 方法中指定 IV

private static final String KEY_ALGORITHM = "DES";
private static final String DEFAULT_CIPHER_ALGORITHM = "DES/ECB/PKCS5Padding";

public static final byte[] encrypt(byte[] data, byte[] key) {
    try {
        Cipher cipher = Cipher.getInstance(DEFAULT_CIPHER_ALGORITHM);
        // 加密模式
        cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key, KEY_ALGORITHM));
        return cipher.doFinal(data);
    } catch (Exception e) {
        e.printStackTrace();
    }
    return null;
}

public static final byte[] decrypt(byte[] data, byte[] key) {
    try {
        Cipher cipher = Cipher.getInstance(DEFAULT_CIPHER_ALGORITHM);
        // 解密模式
        cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(key, KEY_ALGORITHM));
        return cipher.doFinal(data);
    } catch (Exception e) {
        e.printStackTrace();
    }
    return null;
}
複製代碼

2.2 3DES

3DES 即便用 3 次 DES 算法。因爲每一個 DES 算法處理須要 8 字節密鑰,所以 3DES 算法須要 24 字節密鑰。

須要注意,3DES 算法名稱使用 DESedeTripleDESe 表示作 DES 加密操做,d 表示作 DES 解密操做。前者表示用 DES 連續作加密 、解密、加密操做,後者表示連續作三次加密操做。每次使用的密鑰,分別是 24 字節密鑰中不一樣的三段(前、中、後各8字節)。

若使用 DESede 算法時 24 字節密鑰中的前兩段同樣,該算法等同於 DES 算法使用第三段的 8 字節密鑰。

填充相關同 DES 算法同樣。

private static final String KEY_ALGORITHM = "DESede";
private static final String DEFAULT_CIPHER_ALGORITHM = "DESede/CBC/PKCS5Padding";
// 初始化向量
private static final String IV = "12345678";

public static final byte[] encrypt(byte[] data, byte[] key) {
    try {
        Cipher cipher = Cipher.getInstance(DEFAULT_CIPHER_ALGORITHM);
        cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key, KEY_ALGORITHM),new IvParameterSpec(IV.getBytes()));
        return cipher.doFinal(data);
    } catch (Exception e) {
        e.printStackTrace();
    }
    return null;
}

public static final byte[] decrypt(byte[] data, byte[] key) {
    try {
        Cipher cipher = Cipher.getInstance(DEFAULT_CIPHER_ALGORITHM);
        cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(key, KEY_ALGORITHM));
        return cipher.doFinal(data);
    } catch (Exception e) {
        e.printStackTrace();
    }
    return null;
}
複製代碼

2.3 AES

AES 全稱 Advanced Encryption Standard ,高級加密標準算法,用於替代 DES

DES 只支持 8 字節密鑰,AES 能夠支持 16 字節、24 字節和 32 字節密鑰。明文塊長度也能夠劃分紅 16 字節 、24 字節和 32 字節進行填充。

private static final String KEY_ALGORITHM = "AES";
    private static final String DEFAULT_CIPHER_ALGORITHM = "AES/ECB/PKCS5Padding";

    public static final byte[] encrypt(byte[] data, byte[] key) {
        try {
            Cipher cipher = Cipher.getInstance(DEFAULT_CIPHER_ALGORITHM);
            cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key, KEY_ALGORITHM));
            return cipher.doFinal(data);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public static final byte[] decrypt(byte[] data, byte[] key) {
        try {
            Cipher cipher = Cipher.getInstance(DEFAULT_CIPHER_ALGORITHM);
            cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(key, KEY_ALGORITHM));
            return cipher.doFinal(data);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
複製代碼

以上三種對稱加密算法的 Java 代碼實現基本一致。

2.4 RSA

RSA是1977年由羅納德·李維斯特(Ron Rivest)、阿迪·薩莫爾(Adi Shamir)和倫納德·阿德曼(Leonard Adleman)一塊兒提出的非對稱加密算法,所以使用他們 3 人姓氏首字母命名。

示例代碼中的公鑰和私鑰是隨機生成的密鑰對,密鑰對模長建議是 1024 或 2048,對應密文長度是 128 字節和 256 字節。模長能夠大於 2048 , 越長越難破解,可是效率越低。 實際應用中,建議將密鑰對模長設置爲 2048 並以文件的形式存儲在終端。

private static PrivateKey privateKey;
private static PublicKey publicKey;
private static final String DEFAULT_CIPHER_ALGORITHM = "RSA";

static {

    KeyPairGenerator keyGener = null;
    try {
        keyGener = KeyPairGenerator.getInstance(DEFAULT_CIPHER_ALGORITHM);
        keyGener.initialize(1024);
        KeyPair keyPair = keyGener.generateKeyPair();
        privateKey = keyPair.getPrivate();
        publicKey = keyPair.getPublic();
    } catch (NoSuchAlgorithmException e) {
        e.printStackTrace();
    }
}

public static final byte[] encrypt(byte[] data) {
    try {
        Cipher cipher = Cipher.getInstance(DEFAULT_CIPHER_ALGORITHM);
        cipher.init(Cipher.ENCRYPT_MODE, publicKey);
        return cipher.doFinal(data);

    } catch (NoSuchAlgorithmException e) {
        e.printStackTrace();
    } catch (NoSuchPaddingException e) {
        e.printStackTrace();
    } catch (InvalidKeyException e) {
        e.printStackTrace();
    } catch (BadPaddingException e) {
        e.printStackTrace();
    } catch (IllegalBlockSizeException e) {
        e.printStackTrace();
    }

    return null;
}

public static final byte[] decrypt(byte[] data) {
    try {
        Cipher cipher = Cipher.getInstance(DEFAULT_CIPHER_ALGORITHM);
        cipher.init(Cipher.DECRYPT_MODE, privateKey);
        return cipher.doFinal(data);

    } catch (NoSuchAlgorithmException e) {
        e.printStackTrace();
    } catch (NoSuchPaddingException e) {
        e.printStackTrace();
    } catch (InvalidKeyException e) {
        e.printStackTrace();
    } catch (BadPaddingException e) {
        e.printStackTrace();
    } catch (IllegalBlockSizeException e) {
        e.printStackTrace();
    }

    return null;
}
複製代碼

以爲有用?那打賞一個唄。我要打賞

此處是廣告Flueky的技術小站

相關文章
相關標籤/搜索