淺析Java中使用AES對稱加密步驟解析、SpringBoot如何實現AES加解密(秘鑰、偏移量)、Java AES加解密工具類參考示例


一、Java 使用 AES 步驟解析

  AES是一種對稱的加密算法,可基於相同的密鑰進行加密和解密。Java采用AES算法進行加解密的邏輯大致如下:(1)生成/獲取密鑰;(2)加/解密。

(一)關於密鑰步驟

1、生成密鑰

  密鑰的生成是通過KeyGenerator來生成的。通過獲取一個KeyGenerator實例,然后調用其generateKey()方法即可生成一個SecretKey對象。大致邏輯一般如下:

private SecretKey geneKey() throws Exception {   //獲取一個密鑰生成器實例 
  KeyGenerator keyGenerator = KeyGenerator.getInstance(ALGORITHM);   SecureRandom random = new SecureRandom();   random.setSeed("123456".getBytes());//設置加密用的種子,密鑰 
  keyGenerator.init(random);   SecretKey secretKey = keyGenerator.generateKey();   return secretKey; }

  上述生成密鑰的過程中指定了固定的種子,每次生成出來的密鑰都是一樣的。

  還有一種形式,我們可以通過不指定SecureRandom對象的種子,即不調用其setSeed方法,這樣每次生成出來的密鑰都可能是不一樣的。

random.setSeed("123456".getBytes());//設置加密用的種子,密鑰 // 即把這段代碼去掉即可

  通過KeyGenerator的init(keySize)方法進行初始化,而不是通過傳遞SecureRandom對象進行初始化也可以達到上面的效果,每次生成的密鑰都可能是不一樣的。但是對應的keySize的指定一定要正確,AES算法的keySize是128。

private SecretKey geneKey() throws Exception {   //獲取一個密鑰生成器實例 
  KeyGenerator keyGenerator = KeyGenerator.getInstance(ALGORITHM);   keyGenerator.init(128);   SecretKey secretKey = keyGenerator.generateKey();   return secretKey; } 

  但是這種每次生成出來的密鑰都是不同的情況下,我們需要把加密用的密鑰存儲起來,以供解密的時候使用,不然就沒法進行解密了。

2、密鑰的存儲

  密鑰SecretKey里面最核心的內容就是其中的密鑰對應的字節數組,可以通過SecretKey的getEncoded()方法獲取。然后把它存儲起來即可。最簡單的方式就是直接寫入一個文件中。

//把上面的密鑰存起來 
Path keyPath = Paths.get("D:/aes.key"); Files.write(keyPath, secretKey.getEncoded()); 

3、獲取存儲的密鑰

  獲取存儲的密鑰的核心是把密鑰的字節數組轉換為對應的SecretKey。這可以通過SecretKeySpec來獲取,其實現了SecretKey接口,然后構造參數里面將接收密鑰的字節數組。

private SecretKey readKey(Path keyPath) throws Exception {   //讀取存起來的密鑰 
  byte[] keyBytes = Files.readAllBytes(keyPath);   SecretKeySpec keySpec = new SecretKeySpec(keyBytes, ALGORITHM);   return keySpec; } 

(二)關於加解密步驟

  Java采用AES算法進行加解密的過程是類似的,具體如下:

1、指定算法,獲取一個Cipher實例對象

Cipher cipher = Cipher.getInstance(ALGORITHM);//算法是AES 

2、生成 / 讀取用於加解密的密鑰

SecretKey secretKey = this.geneKey();

3、用指定的密鑰初始化Cipher對象,同時指定加解密模式,是加密模式還是解密模式。

cipher.init(Cipher.ENCRYPT_MODE, secretKey);  

4、通過update指定需要加密的內容,不可多次調用。

cipher.update(content.getBytes());  

5、通過Cipher的dofinal()進行最終的加解密操作。

byte[] result = cipher.doFinal();//加密后的字節數組 

  通過以上幾步就完成了使用AES算法進行加解密的操作了。其實第4、5步是可以合在一起的,即在進行doFinal的時候傳遞需要進行加解密的內容,如下:

byte[] result = cipher.doFinal(content.getBytes());  // 4、5合並就這樣寫即可

  但是如果update指定了加密的內容,而doFinal的時候也指定了加密的內容,那最終加密出來的結果將是兩次指定的加密內容的和對應的加密結果

二、使用對稱加密方式(AES)實踐

  AES 加解密工具類:ECB 模式,不支持使用偏移向量

import javax.crypto.Cipher; import javax.crypto.KeyGenerator; import javax.crypto.spec.SecretKeySpec; import com.alibaba.fastjson.JSONObject; import com.fasterxml.jackson.databind.util.JSONPObject; import org.apache.commons.codec.binary.Base64; import java.util.HashMap; import java.util.Map; // 前后端數據傳輸加密工具類
public class AesEncryptUtils { //可配置到Constant中,並讀取配置文件注入,16位,自己定義
    private static final String KEY = "xxxxxxxxxxxxxxxx"; //參數分別代表 算法名稱/加密模式/數據填充方式
    private static final String ALGORITHMSTR = "AES/ECB/PKCS5Padding"; /* 加密 * @param content 加密的字符串 * @param encryptKey key值*/
    public static String encrypt(String content, String encryptKey) throws Exception { KeyGenerator kgen = KeyGenerator.getInstance("AES"); kgen.init(128); Cipher cipher = Cipher.getInstance(ALGORITHMSTR); cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(encryptKey.getBytes(), "AES")); byte[] b = cipher.doFinal(content.getBytes("utf-8")); // 采用base64算法進行轉碼,避免出現中文亂碼
        return Base64.encodeBase64String(b); } /* 解密 * @param encryptStr 解密的字符串 * @param decryptKey 解密的key值*/
    public static String decrypt(String encryptStr, String decryptKey) throws Exception { KeyGenerator kgen = KeyGenerator.getInstance("AES"); kgen.init(128); Cipher cipher = Cipher.getInstance(ALGORITHMSTR); cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(decryptKey.getBytes(), "AES")); // 采用base64算法進行轉碼,避免出現中文亂碼
        byte[] encryptBytes = Base64.decodeBase64(encryptStr); byte[] decryptBytes = cipher.doFinal(encryptBytes); return new String(decryptBytes); } public static String encrypt(String content) throws Exception { return encrypt(content, KEY); } public static String decrypt(String encryptStr) throws Exception { return decrypt(encryptStr, KEY); } }

三、Java實現AES加密(秘鑰、偏移量)

  AES 加解密工具類:CBC 模式,使用偏移向量

package com.unicom.atlas.statistic.abnormal.table.aes; import org.apache.tomcat.util.codec.binary.Base64; import javax.crypto.Cipher; import javax.crypto.KeyGenerator; import javax.crypto.SecretKey; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; import java.security.SecureRandom; import java.util.logging.Level; import java.util.logging.Logger; public class AESUtil { private static final String KEY = "******"; //AES 16位
    public static final String VIPARA = "1234567890abcdef";   //AES 128位數據塊對應偏移量為16位 //AES:加密方式 CBC:工作模式 PKCS5Padding:填充模式
    private static final String CBC_PKCS5_PADDING = "AES/CBC/PKCS5Padding"; private static final String AES = "AES";
    public static final String CODE_TYPE = "UTF-8"; // 編碼方式 /* AES 加密操作 * @param content 待加密內容 * @param key 加密密鑰 * @return 返回Base64轉碼后的加密數據 */
    public static String encrypt(String content, String key) { if (content == null || "".equals(content)) { return content; } try { /* * 新建一個密碼編譯器的實例,由三部分構成,用"/"分隔,分別代表如下 * 1. 加密的類型(如AES,DES,RC2等) * 2. 模式(AES中包含ECB,CBC,CFB,CTR,CTS等) * 3. 補碼方式(包含nopadding/PKCS5Padding等等) * 依據這三個參數可以創建很多種加密方式 */ Cipher cipher = Cipher.getInstance(CBC_PKCS5_PADDING); //偏移量
            IvParameterSpec zeroIv = new IvParameterSpec(VIPARA.getBytes(CODE_TYPE)); byte[] byteContent = content.getBytes(CODE_TYPE); //使用加密秘鑰
            SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes(CODE_TYPE), AES); //SecretKeySpec skeySpec = getSecretKey(key); cipher.init(Cipher.ENCRYPT_MODE, skeySpec, zeroIv);// 初始化為加密模式的密碼器
            byte[] result = cipher.doFinal(byteContent);// 加密
            return Base64.encodeBase64String(result);//通過Base64轉碼返回
        } catch (Exception ex) { Logger.getLogger(AESUtil.class.getName()).log(Level.SEVERE, null, ex); } return null; } /* AES 解密操作 * @param content * @param key
     */
    public static String decrypt(String content, String key) { if (content == null || "".equals(content)) { return content; } try { //實例化
            Cipher cipher = Cipher.getInstance(CBC_PKCS5_PADDING); IvParameterSpec zeroIv = new IvParameterSpec(VIPARA.getBytes(CODE_TYPE)); SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes(CODE_TYPE), AES); //SecretKeySpec skeySpec = getSecretKey(key);
 cipher.init(Cipher.DECRYPT_MODE, skeySpec, zeroIv); byte[] result = cipher.doFinal(Base64.decodeBase64(content)); return new String(result, CODE_TYPE); } catch (Exception ex) { Logger.getLogger(AESUtil.class.getName()).log(Level.SEVERE, null, ex); } return null; } // 生成加密秘鑰
    private static SecretKeySpec getSecretKey(final String key) { //返回生成指定算法密鑰生成器的 KeyGenerator 對象
        KeyGenerator kg = null; try { kg = KeyGenerator.getInstance(AES); //AES 要求密鑰長度為 128
            kg.init(128, new SecureRandom(key.getBytes())); //生成一個密鑰
            SecretKey secretKey = kg.generateKey(); return new SecretKeySpec(secretKey.getEncoded(), AES);// 轉換為AES專用密鑰
        } catch (Exception ex) { Logger.getLogger(AESUtil.class.getName()).log(Level.SEVERE, null, ex); } return null; } } 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM