zoukankan      html  css  js  c++  java
  • 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

  • 相关阅读:
    Angular 组件通信的三种方式
    Vue 之keep-alive的使用,实现页面缓存
    Angular Service设计理念及使用
    Git提交规范
    angular的生命周期
    CPU 是如何认识和执行代码的
    Ubuntu 常用软件
    UltraSQL / sqlserver-kit (SQLServer DBA )高手
    便宜的网站模板下载
    Audio over Bluetooth: most detailed information about profiles, codecs, and devices
  • 原文地址:https://www.cnblogs.com/shoubianxingchen/p/5869373.html
Copyright © 2011-2022 走看看