java加密算法之AES小記


jce中提供了加解密的api:

1、首先應該明白AES是基於數據塊的加密方式,也就是說,每次處理的數據是一塊(16字節),當數據不是16字節的倍數時填充,這就是所謂的分組密碼(區別於基於比特位的流密碼),16字節是分組長度

AES支持五種模式:CBC,CFB,ECB,OFB,PCBC,

jce中實現了三種補碼方式:NoPadding,PKCS5Padding,ISO10126Padding;不支持SSL3Padding,不支持“NONE”模式。

ECB:是一種基礎的加密方式,密文被分割成分組長度相等的塊(不足補齊),然后單獨一個個加密,一個個輸出組成密文。
CBC:是一種循環模式,前一個分組的密文和當前分組的明文異或操作后再加密,這樣做的目的是增強破解難度。
CFB/OFB實際上是一種反饋模式,目的也是增強破解的難度。
ECB和CBC的加密結果是不一樣的,兩者的模式不同,而且CBC會在第一個密碼塊運算時加入一個初始化向量。

算法/模式/填充                    16字節加密后數據長度         不滿16字節加密后長度

AES/CBC/NoPadding                  16                         不支持
AES/CBC/PKCS5Padding             32                         16
AES/CBC/ISO10126Padding        32                          16
AES/CFB/NoPadding                    16                          原始數據長度
AES/CFB/PKCS5Padding              32                          16
AES/CFB/ISO10126Padding         32                          16
AES/ECB/NoPadding                    16                          不支持
AES/ECB/PKCS5Padding              32                          16
AES/ECB/ISO10126Padding         32                          16
AES/OFB/NoPadding                     16                          原始數據長度
AES/OFB/PKCS5Padding               32                          16
AES/OFB/ISO10126Padding          32                          16
AES/PCBC/NoPadding                   16                          不支持
AES/PCBC/PKCS5Padding             32                          16
AES/PCBC/ISO10126Padding        32                          16
可 以看到,在原始數據長度為16的整數倍時,假如原始數據長度等於16*n,則使用NoPadding時加密后數據長度等於16*n,其它情況下加密數據長 度等於16*(n+1)。在不足16的整數倍的情況下,假如原始數據長度等於16*n+m[其中m小於16],除了NoPadding填充之外的任何方 式,加密數據長度都等於16*(n+1);NoPadding填充情況下,CBC、ECB和PCBC三種模式是不支持的,CFB、OFB兩種模式下則加密 數據長度等於原始數據長度。 

 

Demo:

 

import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class AESCoder {
    
    private static final Logger log=LoggerFactory.getLogger(AESCoder.class);
    
    /**
     * 加密
     * hexStr和hexKey都須為16進制表示的字符串
     * 加密后返回16進制表示的字符串*/
    public static String ecbEnc(String hexStr, String hexKey){
        String rs=null;
        try {
            byte[] inBytes = HexUtil.hexToBytes(hexStr);            
            byte[] keyBytes = HexUtil.hexToBytes(hexKey);
            SecretKeySpec skeySpec = new SecretKeySpec(keyBytes, "AES");
            Cipher cipher = Cipher.getInstance("AES/ECB/NoPadding");// "算法/模式/補碼方式"
            cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
            byte[] encrypted = cipher.doFinal(inBytes);            
            rs=HexUtil.bytesToHex(encrypted);
        } catch (Exception e) {
            log.error("加密異常",e);
            log.error("輸入參數為hexStr:{},hexKey:{}",hexStr,hexKey);
        }
        return rs;
    }
    
    /**
     * 解密
     * hexStr和hexKey都須為16進制
     * 加密后返回16進制的字符串*/
    public static String ecbDec(String hexStr,String hexKey){
        String rs=null;
        try {
            byte[] outBytes = HexUtil.hexToBytes(hexStr);
            byte[] keyBytes = HexUtil.hexToBytes(hexKey);
            SecretKeySpec skeySpec = new SecretKeySpec(keyBytes, "AES");
            Cipher cipher = Cipher.getInstance("AES/ECB/NoPadding");// "算法/模式/補碼方式"
            cipher.init(Cipher.DECRYPT_MODE, skeySpec);
            byte[] decBytes = cipher.doFinal(outBytes);
            rs=HexUtil.bytesToHex(decBytes);
        } catch (Exception e) {
            log.error("解密異常",e);
            log.error("輸入參數為hexStr:{},hexKey:{}",hexStr,hexKey);
        }
        return rs;
    }

}

 

public class HexUtil {
    
    /**
     * 將普通字符串用16進制描述
     * 如"WAZX-B55SY6-S6DT5" 描述為:"57415a582d4235355359362d5336445435"
     * */
    public static String strToHex(String str){
         byte[] bytes = str.getBytes(); 
         return bytesToHex(bytes);
    }
    
    /**將16進制描述的字符串還原為普通字符串
     * 如"57415a582d4235355359362d5336445435" 還原為:"WAZX-B55SY6-S6DT5"
     * */
    public static String hexToStr(String hex){
        byte[] bytes=hexToBytes(hex);
        return new String(bytes);
    }
    
    
    /**16進制轉byte[]*/
    public static byte[] hexToBytes(String hex){
        int length = hex.length() / 2;
        byte[] bytes=new byte[length];
        for(int i=0;i<length;i++){
            String tempStr=hex.substring(2*i, 2*i+2);//byte:8bit=4bit+4bit=十六進制位+十六進制位
            bytes[i]=(byte) Integer.parseInt(tempStr, 16);
        }
        return bytes;
    }
    
    /**byte[]轉16進制*/
    public static String bytesToHex(byte[] bytes){
        StringBuilder sb=new StringBuilder();
        for(int i=0;i<bytes.length;i++){
            int tempI=bytes[i] & 0xFF;//byte:8bit,int:32bit;高位相與.
            String str = Integer.toHexString(tempI);
            if(str.length()<2){
                sb.append(0).append(str);//長度不足兩位,補齊:如16進制的d,用0d表示。
            }else{
                sb.append(str);
            }
        }
        return sb.toString();
    }

}

 

public class AESTest {
    
    private static String key="2b7e151628aed2a6abf7158809cf4f3c";
    
    @Test
    public void test_all(){
        String enOri="000000000000000WAZX-B55SY6-S6DT5";
        String enHex=HexUtil.strToHex(enOri);
        String enRS=AESCoder.ecbEnc(enHex,key);
        System.out.println("加密結果為:"+enRS);
        
        String deHex="7312560ccb30ad9b445ee94b426c8a2bdf75d11ded50f053568ec08bf3f9be04";
        String deRS=AESCoder.ecbDec(deHex,key);
        String deOri=HexUtil.hexToStr(deRS);
        System.out.println("解密結果為:"+deOri);
    }
    
    
    @Test
    public void test_enc(){
        String enStr ="6bc1bee22e409f96e93d7e117393172a";        
        String enRS=AESCoder.ecbEnc(enStr,key);        
        System.out.println(enRS);
    }
    
    @Test
    public void test_dec(){
        String deStr ="3ad77bb40d7a3660a89ecaf32466ef97";        
        String enRS=AESCoder.ecbDec(deStr,key);        
        System.out.println(enRS);

    }
    
    
    
    /**
     * 和后台聯調時使用
     * 該函數用於講16進制數組轉成String
     * 如密鑰key為
     * uint8_t key[] = 
     *         {0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 
     *          0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c}
     * 則格式化之后為"2b7e151628aed2a6abf7158809cf4f3c"
     * */
    public static String convertStr(String hexStr) {
        String[] kStrs = hexStr.split(",");
        String[] keyStrs = new String[kStrs.length];
        for (int i = 0; i < kStrs.length; i++) {
            String str = kStrs[i].trim().substring(2);
            keyStrs[i] = str;
        }
        StringBuffer sb = new StringBuffer();
        for (String str : keyStrs) {
            sb.append(str);
        }
        return sb.toString().toUpperCase();
    }

}

AESTest運行結果:

加密結果為:7312560ccb30ad9b445ee94b426c8a2bdf75d11ded50f053568ec08bf3f9be04
解密結果為:000000000000000WAZX-B55SY6-S6DT5

 

---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

--------------------------------------------------------------此外,還有另外一個寫法-----------------------------------------------------------------------------------------

---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

import java.security.SecureRandom;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.spec.SecretKeySpec;

public class AESHelper {

    public static String encrypt(String content, String pwd,String charSet) {
        try {
            byte[] byteContent = content.getBytes(charSet);

            KeyGenerator kgen = KeyGenerator.getInstance("AES");
            kgen.init(128, new SecureRandom(pwd.getBytes()));

            Cipher cipher = Cipher.getInstance("AES");
            cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(kgen.generateKey().getEncoded(), "AES"));

            byte[] buf = cipher.doFinal(byteContent);

            //byte[]轉16進制
            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();
            
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /***
     * 
     * @param content 16進制的字符串
     * @param pwd
     * @param charSet
     * @return
     */
    public static String decrypt(String content, String pwd,String charSet) {
        try {
            byte[] byteContent = new byte[content.length() / 2];
            if (content.length() < 1){
                return null;
            }
            //將16進制轉換為二進制
            for (int i = 0; i < content.length() / 2; i++) {
                int high = Integer.parseInt(content.substring(i * 2, i * 2 + 1), 16);
                int low = Integer.parseInt(content.substring(i * 2 + 1, i * 2 + 2), 16);
                byteContent[i] = (byte) (high * 16 + low);
            }
            
            KeyGenerator kgen = KeyGenerator.getInstance("AES");
            kgen.init(128, new SecureRandom(pwd.getBytes()));

            Cipher cipher = Cipher.getInstance("AES");
            cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(kgen.generateKey().getEncoded(), "AES"));

            byte[] buf = cipher.doFinal(byteContent);
            return new String(buf,charSet);
            
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public static void main(String[] args) {
        String layer = AESHelper.encrypt("編程ABCDefgh~!@#$%^&*()|'?>.<;", "123","utf-8");
        System.out.println(layer);
        String plain = AESHelper.decrypt(layer,"123","utf-8");
        System.out.println(plain);
    }

}

 據說要加上要兩句話,不然,跨操作系統加解密可能有問題:

SecureRandom secureRandom=SecureRandom.getInstance("SHA1PRNG");
secureRandom.setSeed(pwd.getBytes());

 

 

最終寫法:

import java.security.Key;
import java.security.SecureRandom;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;

public class AESHelper {

    public static String encrypt(String content) {
        return encrypt(content, null, "utf-8");
    }

    /***
     * 加密
     * 
     * @param plain
     * @param keySeed
     * @param charSet
     * @return 輸出密文 16進制
     */
    public static String encrypt(String plain, String keySeed, String charSet) {
        try {
            byte[] byteContent = plain.getBytes(charSet);
            Cipher cipher = Cipher.getInstance("AES");
            cipher.init(Cipher.ENCRYPT_MODE, getKey(keySeed));

            byte[] buf = cipher.doFinal(byteContent);

            // byte[]轉16進制
            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();

        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public static String decrypt(String content) {
        return decrypt(content, null, "utf-8");
    }

    /***
     * 解密 輸入16進制的字符串
     * 
     * @param layer
     * @param keySeed
     * @param charSet
     * @return 原文
     */
    public static String decrypt(String layer, String keySeed, String charSet) {
        try {
            byte[] byteContent = new byte[layer.length() / 2];
            if (layer.length() < 1) {
                return null;
            }
            // 將16進制轉換為二進制
            for (int i = 0; i < layer.length() / 2; i++) {
                int high = Integer.parseInt(layer.substring(i * 2, i * 2 + 1), 16);
                int low = Integer.parseInt(layer.substring(i * 2 + 1, i * 2 + 2), 16);
                byteContent[i] = (byte) (high * 16 + low);
            }

            Cipher cipher = Cipher.getInstance("AES");
            cipher.init(Cipher.DECRYPT_MODE, getKey(keySeed));

            byte[] buf = cipher.doFinal(byteContent);
            return new String(buf, charSet);

        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public static Key getKey(String keySeed) {
        if (keySeed == null) {
            keySeed = System.getenv("AES_SYS_KEY");
        }
        if (keySeed == null) {
            keySeed = System.getProperty("AES_SYS_KEY");
        }
        if (keySeed == null || keySeed.trim().length() == 0) {
            keySeed = "abcd1234!@#$";// 默認種子
        }
        try {
            SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG");
            secureRandom.setSeed(keySeed.getBytes());
            KeyGenerator generator = KeyGenerator.getInstance("AES");
            generator.init(secureRandom);
            return generator.generateKey();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public static void main(String[] args) {
        if(null==args||args.length==0){
            args=new String[1];
            args[0]="編程ABCDefgh~!@#$%^&*()|'?>.<;1234567823401";
        }
        String layer = AESHelper.encrypt(args[0]);
        System.out.println("密文:"+layer);
        String plain = AESHelper.decrypt(layer);
        System.out.println("原文:"+plain);
    }

}

 linux 運行該方法:

  

[zhangbin@WEB01 test]$ java AESHelper "jwe223489430214aefhasduf3423"
密文:49336565BD95CB2E70297F4F89DEE2D37EA6A7C14A320194848CF17E930B1DB5
原文:jwe223489430214aefhasduf3423


免責聲明!

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



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