aes是什么?

         aes是对称加密的一种,什么是对称加密呢?就是加密和解密使用相同的秘钥的加密算法,显而易见,aes加密和md5不同,aes是可逆的,aes加密算法用于替代以前的des加密算法。

使用场景:

         工作的过程中,我们有些文件因为需要携带出公司,可能需要加密,最常见的比如源代码加密。如果我想让加密可逆,又能自己指定加密和解密使用的秘钥,那么aes加密算法真的是一个不错的选择哦。因为既然可以自己指定秘钥,我们控制起来就会让aes这个加密算法安全很多。

看下面的例子:

package com.lzzcms.utils;

import java.io.File;
import java.security.SecureRandom;

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

import org.apache.commons.io.FileUtils;
public class CryptoUtilTest {
    public static Cipher getCipher(int type,String seed){
        try {
            // 创建AES的KeyGenerator对象
            KeyGenerator  kgen = KeyGenerator.getInstance("AES");
            // 利用用户传入的字符串作为种子初始化出128位的key生产者不能使用
            //kgen.init(128, new SecureRandom(seed.getBytes()));因为在linux上会导致加解密出错,
            //见代码后边的说明。
              SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG" );
              secureRandom.setSeed(seed.getBytes());
              kgen.init(128, secureRandom);
              //生成秘钥
            SecretKey  secretKey = kgen.generateKey(); 
            //返回基本编码格式的密钥,如果此密钥不支持编码,则返回 null。
            byte[] enCodeFormat = secretKey.getEncoded();
            // 转换为AES专用密钥
            SecretKeySpec  specKey = new SecretKeySpec(enCodeFormat, "AES");
            // 创建密码器
            Cipher cipher = Cipher.getInstance("AES");
            // 初始化为加密模式Cipher.ENCRYPT_MODE或解密模式Cipher.DECRYPT_MODE的密码器
            cipher.init(type, specKey);
            return cipher;
        } catch (Exception e) {
            e.printStackTrace();
        } 
         return null;
    }
    /**
     * 加密
     * @param content :要加密的内容
     * @param seed :种子
     * @return  :加密后的字节数组
     */
    public static byte[] encrypt(String content, String seed) {
        try {
            Cipher cipher = getCipher(Cipher.ENCRYPT_MODE,seed);
            byte[] contentBytes = content.getBytes("utf-8");
            byte[] result = cipher.doFinal(contentBytes);
            return result;
        } catch (Exception e) {
            e.printStackTrace();
        } 
        return null;
    }
    /**
     *  解密AES加密过的字节数组
     * @param contentBytes :AES加密过的字节数组
     * @param seed  :加密时传入的种子
     * @return  :解密后的字节数组
     */
    public static byte[] decrypt(byte[] contentBytes, String seed) {
        try {
            Cipher cipher = getCipher(Cipher.DECRYPT_MODE,seed);
            byte[] result = cipher.doFinal(contentBytes);  
            return result;  
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
    /**将字节数组转换成16进制字符组成的字符串 
     * @param buf :
     * @return 
     */  
    public static String parseByte2HexStr(byte buf[]) {  
        StringBuffer sb = new StringBuffer();  
        int length = buf.length;
        for (int i = 0; i < length; i++) {  
            String hex = Integer.toHexString(buf[i]&0xFF);  
            if (hex.length() == 1) {  
                hex = '0' + hex;  
            }  
            sb.append(hex.toUpperCase());  
        }  
        return sb.toString();  
    } 
    
    /**
     * 把16进制字符组成的字符串转为字节数组并返回
     * 这里传入的十六进制一定是偶数的长度,因为parseByte2HexStr处理了
     * @param hexStr
     * @return
     */
    public static byte[] parseHexStr2Byte(String hexStr) {  
        if (hexStr.length() < 1)  return null;  
        byte[] result = new byte[hexStr.length()/2];  
        for (int i = 0;i< hexStr.length()/2; i++) {  
                //两个十六进制字符构成一个byte.high表示高4位,low表示低四位。Integer.parseInt(s,radix),
            //作用:把s转为10进制,radix指定s是什么进制
                int high = Integer.parseInt(hexStr.substring(i*2, i*2+1), 16);  
                int low = Integer.parseInt(hexStr.substring(i*2+1, i*2+2), 16);  
                result[i] = (byte) (high * 16 + low);  
        }  
        return result;  
    }
    public static void main(String[] args) throws Exception {
         test();        
    }
    private static void test() throws Exception {
        String content = FileUtils.readFileToString(new File("D:\\zhao\\tmp\\app.py"), "utf-8");
        String seed = "123";//秘钥
        System.out.println("加密之前:" + content);
        // 加密
        byte[] encrypt = CryptoUtil.encrypt(content, seed);
        System.out.println("加密后返回的字节数组转为string:" + new String(encrypt,"utf-8"));
        //如果想要加密内容不显示乱码,需要将加密后返回的字节数组转换为16进制
        String hexStrResult = CryptoUtil.parseByte2HexStr(encrypt);
        System.out.println("加密后返回的字节数组转为16进制字符组成的字符串:"+hexStrResult);
        
        
        //把16进制字符组成的字符串还原为字节数组
        byte[] twoBytes = CryptoUtil.parseHexStr2Byte(hexStrResult);
        System.out.println("16进制字符组成的字符串还原的字节数组:"+new String(twoBytes,"utf-8"));
        //解密1
        byte[] decrypt1 = CryptoUtil.decrypt(twoBytes, seed);
        System.out.println("通过16进制字符组成的字符串还原的字节数组解密:"+new String(decrypt1,"utf-8"));  
        /* 解密2:与解密1结果完全一样,之所以通过16进制转换了一下,
            仅仅是为了在存储的时候不想存储乱码二是存储16进制字符组成的字符串而已*/
        byte[] decrypt = CryptoUtil.decrypt(encrypt, seed);
        System.out.println("通过加密后的原始字节数组解密:" + new String(decrypt,"utf-8"));
    }
}

结果截图如下:

AES Crypto工具类 JAVA new java使用aes加密解密_java

代码说明:

    1.功能解释:读入一个python文件,并把源代码的内容进行aes加密,因为转为字符串之后是乱码,需要转为16进制字符组成的字符串,也相当于进行了一次二次加密。

    2.不能使用kgen.init(128, new SecureRandom(seed.getBytes()));因为在linux上会导致加解密出错:

javax.crypto.BadPaddingException: Given final block not properly padded
       at com.sun.crypto.provider.SunJCE_f.b(DashoA13*..)
       at com.sun.crypto.provider.SunJCE_f.b(DashoA13*..)
       at com.sun.crypto.provider.AESCipher.engineDoFinal(DashoA13*..)
       at javax.crypto.Cipher.doFinal(DashoA13*..)
       at chb.test.crypto.AESUtils.crypt(AESUtils.java:386)
       at chb.test.crypto.AESUtils.AesDecrypt(AESUtils.java:254)
       at chb.test.crypto.AESUtils.main(AESUtils.java:40)

虽然kgen.init(128, new SecureRandom(seed.getBytes()));和

SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG" );

              secureRandom.setSeed(seed.getBytes());

              kgen.init(128, secureRandom);

看起来差不多,但是如果使用kgen.init(128, new SecureRandom(seed.getBytes()));就会报上边的错,当初想起来也是让我折腾了好久呀,后来查资料说是SecureRandom 的内部实现与操作系统有关,但是如果在getInstance 方法之后又调用了 setSeed 方法,就可以避免这种因为操作系统的不同而一起的差异,以便确保在 windows 和所有的linux上每次初始化的 key 都相同。

3.上面我只是打印出了各个步骤加密和解密后的结果,实际存储的时候可以把16进制字符组成的字符串存放在文件中作为加密文件的内容,解密的时候读取该加密内容按照上边代码中的方法解密就可以了。