對稱加密算法-詳解測試DEMO

一,概述

        前面講解了大體通用的對稱,非對稱,MD系列等加密算法。

       見前面 我的博客文:  https://blog.csdn.net/Mynah886/article/details/82787683

但感覺還是不能直觀的讓開發者來直接爽起來。最近又看到了其他人分享的,自己項目正好又要涉及使用了。故此重新細化下。供使用時候直接拿來!

         對稱加密算法就是傳統的用一個密碼進行加密和解密。就像我們常用的WinZIP && WinRAR 對壓縮包的加密和解密,就是使用對稱加密算法。

        代表算法/實現方式:

              1) DES(Data Encryption standard)數據加密標準   3DES, 密鑰長度一般 56/64.  DES算法由於密鑰過短,可以在短時間內被暴力破解,所以現在已經不安全了

              2) AES高級DES替代者(工作模式:ECB, CBC, PCBC, CTR, CFB8~128... 填充方式:NoPadding, PKCS5Padding, ISO10126Padding...), 密鑰長度一般 128/192/256.

              3) RC4/5/6算法: 是參數可變的分組密碼算法,三個可變的參數是:分組大小、密鑰大小和加密輪數; 加密時使用了2r+2個密鑰相關的的32位字: 這裏r表示加密的輪數。

               4) Blowfish算法: 每次加密一個64位分組,使用32位~448位的可變長度密鑰,應用於內部加密,輸入64位明文,輸出64位密文。加密過程分爲兩個階段:密鑰預處理和信息加密。 自從32位處理器誕生後,blowfish加密算法在加密速度上就超越了DES加密算法,引起了人們的關注。blowfish加密算法沒有註冊專利,不需要授權,可以免費使用

               5) PBE(Password Based Encryption)基於口令加密 = 口令+鹽

               6)IDEA,早於AES替代出來取代DES. 密鑰長度128 . 工作模式:ECB, 填充方式:PKCS5Padding/PKCS7Padding/... 。實現方式有Bouncy Castle

              注意: 密鑰長度直接決定加密強度,而工作模式和填充模式可以看成是對稱加密算法的參數和格式選擇。Java標準庫提供的算法實現並不包括所有的工作模式和所有填充模式,但是通常我們只需要挑選常用的使用就可以了。

 

          從程序的角度看,所謂加密,就是這樣一個函數,它接收密碼和明文,然後輸出密文:

secret = encrypt(key, message);

          而解密則相反,它接收密碼和密文,然後輸出明文:

plain = decrypt(key, secret);

 

二, 開發DEMO

2.1 我們先用AES加密算法 && ECB模式加密並解密。

    Java標準庫提供的對稱加密接口非常簡單,使用時按以下步驟編寫代碼:

  1. 根據算法名稱/工作模式/填充模式獲取Cipher實例;

  2. 根據算法名稱初始化一個SecretKey實例,密鑰必須是指定長度;

  3. 使用SerectKey初始化Cipher實例,並設置加密或解密模式;

  4. 傳入明文或密文,獲得密文或明文。

/**
 * desc- 對稱加密,ECB模式加密並解密
 * 加密算法  AES, 填充方式 ECB
 * auth  xupengfei
 *
 * @param key 加密key
 * @param input 需要加密的字節數組
 * @return 加密後的字節數組
 */
public static byte[] encrypt( byte[] key,  byte[] input) {
        byte[] result = null;
        try {
            Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
            SecretKey keySpec = new SecretKeySpec(key, "AES");
            cipher.init(Cipher.ENCRYPT_MODE, keySpec);
            result = cipher.doFinal(input);
        }catch (Exception ex){
            log.error("EX-------- {}", ex);
        }
        return result;
}
/**
 * desc 對稱 解密,ECB模式加密並解密
 * 加密算法  AES, 填充方式 ECB
 * auth  xupengfei
 *
 * @param key 加密key
 * @param input 需要解密的字節數組
 * @return 解密後的字節數組
 * @throws GeneralSecurityException
 */
public static byte[] decrypt(byte[] key, byte[] input) throws GeneralSecurityException {
        Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
        SecretKey keySpec = new SecretKeySpec(key, "AES");
        cipher.init(Cipher.DECRYPT_MODE, keySpec);
        return cipher.doFinal(input);
}
/**
 *  測試類
 * @param args
 * @throws Exception
 */
@Test
public void testAesECB() throws Exception {
    // 原文:
    String message = "Hello world,I am DCONE member, please call me mynah!";
    log.debug("Message: " + message);
    // 128位密鑰 = 16 bytes Key:
    byte[] key = "xupengfei1234567".getBytes("UTF-8");
    // 加密:
    byte[] data = message.getBytes("UTF-8");
    byte[] encrypted = encrypt(key, data);
    log.debug("Encrypted: " + Base64.getEncoder().encodeToString(encrypted));
    // 解密:
    byte[] decrypted = decrypt(key, encrypted);
    log.debug("Decrypted: " + new String(decrypted, "UTF-8"));
}

2.1  測試結果如下:

 

2.2  ECB模式是最簡單的AES加密模式,它只需要一個固定長度的密鑰,固定的明文會生成固定的密文,這種一對一的加密方式會導致安全性降低,更好的方式是通過CBC模式,它需要一個隨機數作爲IV參數,這樣對於同一份明文,每次生成的密文都不同。        

        在CBC模式下,需要一個隨機生成的16字節IV參數,必須使用 SecureRandom 生成。因爲多了一個IvParameterSpec實例,因此,初始化方法需要調用Cipher的一個重載方法並傳入IvParameterSpec。 可以發現每次生成的IV不同,密文也不同。

/**
 * desc 對稱加密, CBC 模式加密並解密
 * 加密算法  AES, 填充方式 CBC
 * auth  xupengfei
 *
 * @param key
 * @param input
 * @return
 * @throws GeneralSecurityException
 */
public static byte[] encrypt2(byte[] key, byte[] input) throws GeneralSecurityException {
    Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
    SecretKeySpec keySpec = new SecretKeySpec(key, "AES");
    // CBC模式 要生成一個 16bytes 的initialization vector
    SecureRandom sr = SecureRandom.getInstanceStrong();
    byte[] iv = sr.generateSeed(16);
    IvParameterSpec ivps = new IvParameterSpec(iv);
    cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivps);
    byte[] data = cipher.doFinal(input);
    // IV不需要保密,把 IV && 密文一起返回
    return join(iv, data);
}
/**
 * desc 對稱 解密,CBC模式加密並解密
 * 加密算法  AES, 填充方式 CBC
 * auth  xupengfei
 *
 * @param key
 * @param input
 * @return
 * @throws GeneralSecurityException
 */
public static byte[] decrypt2(byte[] key, byte[] input) throws GeneralSecurityException {
    // 把 入參input 分割成:  IV && 密文
    byte[] iv = new byte[16];
    byte[] data = new byte[input.length - 16];
    System.arraycopy(input, 0, iv, 0, 16);
    System.arraycopy(input, 16, data, 0, data.length);
    // 解密
    Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
    SecretKeySpec keySpec = new SecretKeySpec(key, "AES");
    IvParameterSpec ivps = new IvParameterSpec(iv);
    cipher.init(Cipher.DECRYPT_MODE, keySpec, ivps);
    return cipher.doFinal(data);
}
/**
 *
 *
 * @param bs1
 * @param bs2
 * @return
 */
public static byte[] join(byte[] bs1, byte[] bs2) {
    byte[] r = new byte[bs1.length + bs2.length];
    System.arraycopy(bs1, 0, r, 0, bs1.length);
    System.arraycopy(bs2, 0, r, bs1.length, bs2.length);
    return r;
}
/**
 * 測試類 CBC 模式
 *
 * @param args
 * @throws Exception
 */
@Test
public void testAesCBC() throws Exception {
    // 明文
    String message = "Hello world,I am DCONE member, please call me mynah, ouye!";
    log.debug("Message: " + message);
    // 256位密鑰 = 32 bytes Key:
    byte[] key = "0123456789abcdef0123456789abcdef".getBytes("UTF-8");
    // 加密
    byte[] data = message.getBytes("UTF-8");
    byte[] encrypted = encrypt(key, data);
    log.debug("Encrypted: " + Base64.getEncoder().encodeToString(encrypted));
    // 解密
    byte[] decrypted = decrypt(key, encrypted);
    log.debug("Decrypted: " + new String(decrypted, "UTF-8"));
}

2.2  測試結果如下:

 

 

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