Java實現AES加密(window機器和linux機器) 注意window機器 和linux機器算法稍有不同


一)什么是AES?

高級加密標准(英語:Advanced Encryption Standard,縮寫:AES),是一種區塊加密標准。這個標准用來替代原先的DES,已經被多方分析且廣為全世界所使用。

那么為什么原來的DES會被取代呢,,原因就在於其使用56位密鑰,比較容易被破解。而AES可以使用128、192、和256位密鑰,並且用128位分組加密和解密數據,相對來說安全很多。完善的加密算法在理論上是無法破解的,除非使用窮盡法。使用窮盡法破解密鑰長度在128位以上的加密數據是不現實的,僅存在理論上的可能性。統計顯示,即使使用目前世界上運算速度最快的計算機,窮盡128位密鑰也要花上幾十億年的時間,更不用說去破解采用256位密鑰長度的AES算法了。

目前世界上還有組織在研究如何攻破AES這堵堅厚的牆,但是因為破解時間太長,AES得到保障,但是所用的時間不斷縮小。隨着計算機計算速度的增快,新算法的出現,AES遭到的攻擊只會越來越猛烈,不會停止的。

AES現在廣泛用於金融財務、在線交易、無線通信、數字存儲等領域,經受了最嚴格的考驗,但說不定哪天就會步DES的后塵。

二)AES加密解密 以下為window下的源碼:(注意 如果在linux或者mac電腦上執行 ,是不對的)

package demo.security;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Base64;
import java.util.Scanner;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.KeyGenerator;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;

import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;

/*
 * AES對稱加密和解密
 */
public class SymmetricEncoder {
  /*
   * 加密
   * 1.構造密鑰生成器
   * 2.根據ecnodeRules規則初始化密鑰生成器
   * 3.產生密鑰
   * 4.創建和初始化密碼器
   * 5.內容加密
   * 6.返回字符串
   */
    public static String AESEncode(String encodeRules,String content){
        try {
            //1.構造密鑰生成器,指定為AES算法,不區分大小寫
            KeyGenerator keygen=KeyGenerator.getInstance("AES");
            //2.根據ecnodeRules規則初始化密鑰生成器
            //生成一個128位的隨機源,根據傳入的字節數組
            keygen.init(128, new SecureRandom(encodeRules.getBytes()));
              //3.產生原始對稱密鑰
            SecretKey original_key=keygen.generateKey();
              //4.獲得原始對稱密鑰的字節數組
            byte [] raw=original_key.getEncoded();
            //5.根據字節數組生成AES密鑰
            SecretKey key=new SecretKeySpec(raw, "AES");
              //6.根據指定算法AES自成密碼器
            Cipher cipher=Cipher.getInstance("AES");
              //7.初始化密碼器,第一個參數為加密(Encrypt_mode)或者解密解密(Decrypt_mode)操作,第二個參數為使用的KEY
            cipher.init(Cipher.ENCRYPT_MODE, key);
            //8.獲取加密內容的字節數組(這里要設置為utf-8)不然內容中如果有中文和英文混合中文就會解密為亂碼
            byte [] byte_encode=content.getBytes("utf-8");
            //9.根據密碼器的初始化方式--加密:將數據加密
            byte [] byte_AES=cipher.doFinal(byte_encode);
          //10.將加密后的數據轉換為字符串
            //這里用Base64Encoder中會找不到包
            //解決辦法:
            //在項目的Build path中先移除JRE System Library,再添加庫JRE System Library,重新編譯后就一切正常了。
            String AES_encode=new String(new BASE64Encoder().encode(byte_AES));
          //11.將字符串返回
            return AES_encode;
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (NoSuchPaddingException e) {
            e.printStackTrace();
        } catch (InvalidKeyException e) {
            e.printStackTrace();
        } catch (IllegalBlockSizeException e) {
            e.printStackTrace();
        } catch (BadPaddingException e) {
            e.printStackTrace();
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        
        //如果有錯就返加nulll
        return null;         
    }
    /*
     * 解密
     * 解密過程:
     * 1.同加密1-4步
     * 2.將加密后的字符串反紡成byte[]數組
     * 3.將加密內容解密
     */
    public static String AESDncode(String encodeRules,String content){
        try {
            //1.構造密鑰生成器,指定為AES算法,不區分大小寫
            KeyGenerator keygen=KeyGenerator.getInstance("AES");
            //2.根據ecnodeRules規則初始化密鑰生成器
            //生成一個128位的隨機源,根據傳入的字節數組
            keygen.init(128, new SecureRandom(encodeRules.getBytes()));
              //3.產生原始對稱密鑰
            SecretKey original_key=keygen.generateKey();
              //4.獲得原始對稱密鑰的字節數組
            byte [] raw=original_key.getEncoded();
            //5.根據字節數組生成AES密鑰
            SecretKey key=new SecretKeySpec(raw, "AES");
              //6.根據指定算法AES自成密碼器
            Cipher cipher=Cipher.getInstance("AES");
              //7.初始化密碼器,第一個參數為加密(Encrypt_mode)或者解密(Decrypt_mode)操作,第二個參數為使用的KEY
            cipher.init(Cipher.DECRYPT_MODE, key);
            //8.將加密並編碼后的內容解碼成字節數組
            byte [] byte_content= new BASE64Decoder().decodeBuffer(content);
            /*
             * 解密
             */
            byte [] byte_decode=cipher.doFinal(byte_content);
            String AES_decode=new String(byte_decode,"utf-8");
            return AES_decode;
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (NoSuchPaddingException e) {
            e.printStackTrace();
        } catch (InvalidKeyException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (IllegalBlockSizeException e) {
            e.printStackTrace();
        } catch (BadPaddingException e) {
            e.printStackTrace();
        }
        
        //如果有錯就返加nulll
        return null;         
    }
    
    public static void main(String[] args) {
        SymmetricEncoder se=new SymmetricEncoder();
        Scanner scanner=new Scanner(System.in);
        /*
         * 加密
         */
        System.out.println("使用AES對稱加密,請輸入加密的規則");
        String encodeRules=scanner.next();
        System.out.println("請輸入要加密的內容:");
        String content = scanner.next();
        System.out.println("根據輸入的規則"+encodeRules+"加密后的密文是:"+se.AESEncode(encodeRules, content));
       
        /*
         * 解密
         */
        System.out.println("使用AES對稱解密,請輸入加密的規則:(須與加密相同)");
         encodeRules=scanner.next();
        System.out.println("請輸入要解密的內容(密文):");
         content = scanner.next();
        System.out.println("根據輸入的規則"+encodeRules+"解密后的明文是:"+se.AESDncode(encodeRules, content));
    }

}

三)測試結果

使用AES對稱加密,請輸入加密的規則
使用AES對稱加密
請輸入要加密的內容:
使用AES對稱加密
根據輸入的規則使用AES對稱加密加密后的密文是:Z0NwrNPHghgXHN0CqjLS58YCjhMcBfeR33RWs7Lw+AY=
使用AES對稱解密,請輸入加密的規則:(須與加密相同)
使用AES對稱加密
請輸入要解密的內容(密文):
Z0NwrNPHghgXHN0CqjLS58YCjhMcBfeR33RWs7Lw+AY=
根據輸入的規則使用AES對稱加密解密后的明文是:使用AES對稱加密

 

二、如果在window上面執行上面的代碼是正確的,但是如果在linux或者mac上執行,就會報錯:

javax.crypto.BadPaddingException: Given final block not properly padded

經過一番調試,找到真正原因:javax.crypto.BadPaddingException: Given final block not properly padded

package com.daredo.utils;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.security.SecureRandom;

/**
 * Created by IntelliJ IDEA
 * Author: d-arlin@qq.com
 * Date: 2018/3/14
 * Time: 15:38
 */
public class SecurityUtils {

    /**
     * 編碼格式
     */
    private static String ENCODING = "UTF-8";
    /**
     * 加密算法
     */
    public static final String KEY_ALGORITHM = "AES";

    /**
     * 加密
     *
     * @param content 待加密內容
     * @param key     加密的密鑰
     * @return
     */
    public static String encrypt(String content, String key) {
        try {
            KeyGenerator kgen = KeyGenerator.getInstance(KEY_ALGORITHM);
            kgen.init(128, new SecureRandom(key.getBytes(ENCODING)));
            SecretKey secretKey = kgen.generateKey();
            byte[] enCodeFormat = secretKey.getEncoded();
            SecretKeySpec secretKeySpec = new SecretKeySpec(enCodeFormat, KEY_ALGORITHM);
            Cipher cipher = Cipher.getInstance(KEY_ALGORITHM);
            byte[] byteContent = content.getBytes(ENCODING);
            cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);
            byte[] byteRresult = cipher.doFinal(byteContent);
            StringBuffer sb = new StringBuffer();
            for (int i = 0; i < byteRresult.length; i++) {
                String hex = Integer.toHexString(byteRresult[i] & 0xFF);
                if (hex.length() == 1) hex = '0' + hex;
                sb.append(hex.toUpperCase());
            }
            return sb.toString();
        } catch (Exception e) {
            e.toString();
        }
        return null;
    }

    /**
     * 解密
     *
     * @param content 待解密內容
     * @param key     解密的密鑰
     * @return
     */
    public static String decrypt(String content, String key) {
        if (content.length() < 1) return null;
        byte[] byteRresult = new byte[content.length() / 2];
        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);
            byteRresult[i] = (byte) (high * 16 + low);
        }
        try {
            KeyGenerator kgen = KeyGenerator.getInstance(KEY_ALGORITHM);
            kgen.init(128, new SecureRandom(key.getBytes(ENCODING)));
            SecretKey secretKey = kgen.generateKey();
            byte[] enCodeFormat = secretKey.getEncoded();
            SecretKeySpec secretKeySpec = new SecretKeySpec(enCodeFormat, KEY_ALGORITHM);
            Cipher cipher = Cipher.getInstance(KEY_ALGORITHM);
            cipher.init(Cipher.DECRYPT_MODE, secretKeySpec);
            byte[] result = cipher.doFinal(byteRresult);
            return new String(result, ENCODING);
        } catch (Exception e) {
            e.toString();
        }
        return null;
    }

}

那么為什么在Windows正常,在Linux就出現異常呢?

原因分析
SecureRandom 實現完全隨操作系統本身的內部狀態,除非調用方在調用 getInstance 方法之后又調用了 setSeed 方法;

該實現在 windows 上每次生成的 key 都相同,但是在 solaris 或部分 linux 系統上則不同。

解決方法

package com.daredo.utils;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.security.SecureRandom;

/**
 * Created by IntelliJ IDEA
 * Author: d-arlin@qq.com
 * Date: 2018/3/14
 * Time: 15:38
 */
public class SecurityUtils {

    /**
     * 編碼格式
     */
    private static final String ENCODING = "UTF-8";
    /**
     * 加密算法
     */
    public static final String KEY_ALGORITHM = "AES";
    /**
     * 簽名算法
     */
    public static final String SIGN_ALGORITHMS = "SHA1PRNG";

    /**
     * 加密
     *
     * @param content 待加密內容
     * @param key     加密的密鑰
     * @return
     */
    public static String encrypt(String content, String key) {
        try {
            KeyGenerator kgen = KeyGenerator.getInstance(KEY_ALGORITHM);
           SecureRandom random = SecureRandom.getInstance(SIGN_ALGORITHMS);
            random.setSeed(key.getBytes(ENCODING));
            kgen.init(128, random);
            SecretKey secretKey = kgen.generateKey();
            byte[] enCodeFormat = secretKey.getEncoded();
            SecretKeySpec secretKeySpec = new SecretKeySpec(enCodeFormat, KEY_ALGORITHM);
            Cipher cipher = Cipher.getInstance(KEY_ALGORITHM);
            byte[] byteContent = content.getBytes(ENCODING);
            cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);
            byte[] byteRresult = cipher.doFinal(byteContent);
            StringBuffer sb = new StringBuffer();
            for (int i = 0; i < byteRresult.length; i++) {
                String hex = Integer.toHexString(byteRresult[i] & 0xFF);
                if (hex.length() == 1) hex = '0' + hex;
                sb.append(hex.toUpperCase());
            }
            return sb.toString();
        } catch (Exception e) {
            e.toString();
        }
        return null;
    }

    /**
     * 解密
     *
     * @param content 待解密內容
     * @param key     解密的密鑰
     * @return
     */
    public static String decrypt(String content, String key) {
        if (content.length() < 1) return null;
        byte[] byteRresult = new byte[content.length() / 2];
        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);
            byteRresult[i] = (byte) (high * 16 + low);
        }
        try {
            KeyGenerator kgen = KeyGenerator.getInstance(KEY_ALGORITHM);
         SecureRandom random = SecureRandom.getInstance(SIGN_ALGORITHMS);
            random.setSeed(key.getBytes(ENCODING));
            kgen.init(128, random);
            SecretKey secretKey = kgen.generateKey();
            byte[] enCodeFormat = secretKey.getEncoded();
            SecretKeySpec secretKeySpec = new SecretKeySpec(enCodeFormat, KEY_ALGORITHM);
            Cipher cipher = Cipher.getInstance(KEY_ALGORITHM);
            cipher.init(Cipher.DECRYPT_MODE, secretKeySpec);
            byte[] result = cipher.doFinal(byteRresult);
            return new String(result, ENCODING);
        } catch (Exception e) {
            e.toString();
        }
        return null;
    }

}

改動的地方如代碼中已標紅。

參考:javax.crypto.BadPaddingException: Given final block not properly padded

參考:Java實現AES加密


免責聲明!

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



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