Java 常用加密算法(二)---对称加密(DES/3DES(TripleDES)/AES)

基于“对称密钥”的加密算法主要有DES、3DES(TripleDES)、AES、RC2、RC4、RC5和Blowfish等。

代码中用到的Base64与BouncyCastleProvider的jar包可在本文末尾下载源码,从源码获取jar包。

  1. DES
    DES算法全称为Data Encryption Standard,即数据加密算法,它是IBM公司于1975年研究成功并公开发表的。DES算法的入口参数有三个:Key、Data、Mode。其中Key为8个字节共64位,是DES算法的工作密钥;Data也为8个字节64位,是要被加密或被解密的数据;Mode为DES的工作方式,有两种:加密或解密。
    五种分组模式:EBC模式、CBC模式、CFB模式、OFB模式、CTR模式。
    其中CTR 模式被广泛用于 ATM 网络安全和 IPSec应用中,相对于其它模式而言,CRT模式具有如下特点:
    ■硬件效率:允许同时处理多块明文 / 密文。
    ■ 软件效率:允许并行计算,可以很好地利用 CPU 流水等并行技术。
    ■ 预处理:算法和加密盒的输出不依靠明文和密文的输入,因此如果有足够的保证安全的存储器,加密算法将仅仅是一系列异或运算,这将极大地提高吞吐量。
    ■ 随机访问:第 i 块密文的解密不依赖于第 i-1 块密文,提供很高的随机访问能力
    ■ 可证明的安全性:能够证明 CTR 至少和其他模式一样安全(CBC, CFB, OFB, ...)
    ■ 简单性:与其它模式不同,CTR模式仅要求实现加密算法,但不要求实现解密算法。对于 AES 等加/解密本质上不同的算法来说,这种简化是巨大的。
    ■ 无填充,可以高效地作为流式加密使用。
    在Java进行DES、3DES和AES三种对称加密算法时,常采用的是NoPadding(不填充)、Zeros填充(0填充)、PKCS5Padding填充。
    示例:

/**
 * DES加密工具类
 * @author mazaiting
 */
public class DESUtil {
    /**
     * 算法名称
     */
    public static final String KEY_ALGORITHM = "DES";
    /**
     * 算法名称/加密模式/填充方式
     * DES的四种工作模式:ECB(电子密码本)、CBC(加密分组链接)、
     * CFB(加密反馈模式)、OFB(输出反馈)
     * 当前无填充的情况:
     *      加密数据必须为8的倍数,密钥输入必须为16的倍数
     *  使用
        String source = "abcdefgh";// 为8位的倍数
        String key = "A1B2C3D4E5F60708";// 为16位的倍数
        
        String encryptData = DESUtil.encrypt(source, key);
        System.out.println("加密后: " + encryptData);
        
        String decryptData = DESUtil.decrypt(encryptData, key);
        System.out.println("解密后: " + decryptData);
     */
    public static final String CIPHER_ALGORITHM_DES_ECB_NOPADDING = "DES/ECB/NoPadding";
    /**
     * DES/CBC/PKCS5Padding
     * PKCS5Padding填充时:
     *      加密数据无位数控制,密钥输入必须为16的倍数
     *  使用
        String source = "abcdefgh";// 无需控制位数
        String key = "A1B2C3D4E5F60708";// 为16位的倍数
        
        String encryptData = DESUtil.encrypt(source, key);
        System.out.println("加密后: " + encryptData);
        
        String decryptData = DESUtil.decrypt(encryptData, key);
        System.out.println("解密后: " + decryptData);
        
     */
    public static final String CIPHER_ALGORITHM_DES_ECB_PKCS5_PADDING = "DES/ECB/PKCS5Padding";
    
    /**
     * 加密数据
     * @param data 待加密的数据--8位
     * @param key 密钥--16位
     * @return 加密后的数据
     */
    public static String encrypt(String data, String key) {
        try {
            // 获得一个密钥
            Key deskey = keyGenerator(key);
            // 实例化一个Cipher(密码)对象,用于完成加密操作
            Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM_DES_ECB_PKCS5_PADDING);
            SecureRandom random = new SecureRandom();
            // 初始化Cipher对象,设置为加密模式
            cipher.init(Cipher.ENCRYPT_MODE, deskey, random);
            byte[] bytes = data.getBytes();
            // 执行加密操作
            byte[] results = cipher.doFinal(bytes);
            return Base64.encodeBase64String(results);
        } catch (InvalidKeyException e) {
            return "无效KEY"; // 无效KEY
        } catch (NoSuchAlgorithmException e) {
            return "无效算法名称"; // 无效算法名称
        } catch (InvalidKeySpecException e) {
            return "无效KeySpec"; // 无效KeySpec
        } catch (NoSuchPaddingException e) {
            return "无效算法名称"; // 无效算法名称
        } catch (IllegalBlockSizeException e) {
            return "无效字节"; // 无效字节
        } catch (BadPaddingException e) {
            return "解析异常"; // 解析异常
        }
    }

    /**
     * 解密数据
     * @param data 待解密数据
     * @param key 密钥
     * @return 解密后的数据
     */
    public static String decrypt(String data, String key) {
        try {
            Key desKey = keyGenerator(key);
            Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM_DES_ECB_PKCS5_PADDING);
            // 初始化Cipher对象,设置为解密模式
            cipher.init(Cipher.DECRYPT_MODE, desKey);
            // 执行解密操作
            byte[] decBytes = cipher.doFinal(Base64.decodeBase64(data));
            return new String(decBytes,"UTF-8");
        } catch (InvalidKeyException e) {
            return "无效KEY"; // 无效KEY
        } catch (NoSuchAlgorithmException e) {
            return "无效算法名称"; // 无效算法名称
        } catch (InvalidKeySpecException e) {
            return "无效KeySpec"; // 无效KeySpec
        } catch (NoSuchPaddingException e) {
            return "无效算法名称"; // 无效算法名称
        } catch (IllegalBlockSizeException e) {
            return "无效字节"; // 无效字节
        } catch (BadPaddingException e) {
            return "解析异常"; // 解析异常
        } catch (UnsupportedEncodingException e) {
            return "编码异常"; // 编码异常
        }
    }
    
    /**
     * 生成密钥key对象
     * @param key 密钥字符串
     * @return 密钥对象
     * @throws InvalidKeyException 无效的key
     * @throws NoSuchAlgorithmException 算法名称未发现
     * @throws InvalidKeySpecException 无效的KeySpec
     */
    private static Key keyGenerator(String key) throws InvalidKeyException, NoSuchAlgorithmException, InvalidKeySpecException {
        byte[] input = hexString2Bytes(key);
        DESKeySpec desKey = new DESKeySpec(input);
        // 创建一个密钥工厂,然后用它把DESKeySpec转化
        SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(KEY_ALGORITHM);
        // 获得一个密钥
        SecretKey secretKey = keyFactory.generateSecret(desKey);
        return secretKey;
    }

    /**
     * 从十六进制字符串到字节数组转化
     * @param key 密钥
     */
    private static byte[] hexString2Bytes(String key) {
        byte[] b = new byte[key.length()/2];
        int j = 0;
        for (int i = 0; i < b.length; i++) {
            char c0 = key.charAt(j++);
            char c1 = key.charAt(j++);
            // c0做b[i]的高字节,c1做低字节
            b[i] = (byte) ((parse(c0)<<4)|parse(c1));
        }
        return b;
    }

    /**
     * 将字符转换为int值
     * @param c 要转化的字符
     * @return ASCII码值
     */
    private static int parse(char c) {
        if (c >= 'a') {
            return (c - 'a' + 10) & 0x0f;
        }
        if (c >= 'A') {
            return (c - 'A' + 10) & 0x0f;
        }
        return (c - '0') & 0x0f;
    }   
}

  1. 3-DES
    3DES(或称为Triple DES)是三重数据加密算法(TDEA,Triple Data Encryption Algorithm)块密码的通称。它相当于是对每个数据块应用三次DES加密算法。由于计算机运算能力的增强,原版DES密码的密钥长度变得容易被暴力破解;3DES即是设计用来提供一种相对简单的方法,即通过增加DES的密钥长度来避免类似的攻击,而不是设计一种全新的块密码算法
    示例:
/**
 * 3-DES加密工具类
 * @author mazaiting
 */
public class ThreeDESUtil {
    /**
     * 算法名称
     */
    public static final String KEY_ALGORITHM = "desede";
    /**
     * 算法名称/加密模式/填充方式
     * 
        String source = "abcdefgh"; // 无填充情况下,长度必须为8的倍数
        String key = "6C4E60E55552386C759569836DC0F83869836DC0F838C0F7";// 长度必须大于等于48
        System.out.println(key.length());
        String encryptData = ThreeDESUtil.tDesEncryptCBC(source, key);
        System.out.println("加密后: " + encryptData);
        
        String decryptData = ThreeDESUtil.tDesDecryptCBC(encryptData, key);
        System.out.println("解密后: " + decryptData);  
     */
    public static final String CIPHER_ALGORITHM = "desede/CBC/NoPadding";
    /**
     * IvParameterSpec参数
     */
    public static final byte[] keyiv = { 1, 2, 3, 4, 5, 6, 7, 8 };
    
    /**
     * CBC加密
     * @param data 明文
     * @param key 密钥
     * @return Base64编码的密文
     */
    public static String tDesEncryptCBC(String data, String key){
        try {
            // 添加一个安全提供者
            Security.addProvider(new BouncyCastleProvider());
            // 获得密钥
            Key desKey = keyGenerator(key);
            // 获取密码实例
            Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
            IvParameterSpec ips = new IvParameterSpec(keyiv);
            // 初始化密码
            cipher.init(Cipher.ENCRYPT_MODE, desKey, ips);
            // 执行加密
            byte[] bytes = cipher.doFinal(data.getBytes("UTF-8"));
            return Base64.encodeBase64String(bytes);
        } catch (InvalidKeyException e) {
            return "无效KEY"; // 无效KEY
        } catch (NoSuchAlgorithmException e) {
            return "无效算法名称"; // 无效算法名称
        } catch (InvalidKeySpecException e) {
            return "无效KeySpec"; // 无效KeySpec
        } catch (NoSuchPaddingException e) {
            return "无效算法名称"; // 无效算法名称
        } catch (IllegalBlockSizeException e) {
            return "无效字节"; // 无效字节
        } catch (BadPaddingException e) {
            return "解析异常"; // 解析异常
        } catch (UnsupportedEncodingException e) {
            return "编码异常"; // 编码异常
        } catch (InvalidAlgorithmParameterException e) {
            return "摘要参数异常"; // 摘要参数异常
        }
    }
    
    /**
     * CBC解密
     * @param data Base64编码的密文
     * @param key 密钥
     * @return
     */
    public static String tDesDecryptCBC(String data, String key) {
        try {
            Key desKey = keyGenerator(key);
            Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
            IvParameterSpec ips = new IvParameterSpec(keyiv);
            cipher.init(Cipher.DECRYPT_MODE, desKey, ips);
            byte[] bytes = cipher.doFinal(Base64.decodeBase64(data));
            return new String(bytes, "UTF-8");
        } catch (InvalidKeyException e) {
            return "无效KEY"; // 无效KEY
        } catch (NoSuchAlgorithmException e) {
            return "无效算法名称"; // 无效算法名称
        } catch (InvalidKeySpecException e) {
            return "无效KeySpec"; // 无效KeySpec
        } catch (NoSuchPaddingException e) {
            return "无效算法名称"; // 无效算法名称
        } catch (IllegalBlockSizeException e) {
            return "无效字节"; // 无效字节
        } catch (BadPaddingException e) {
            return "解析异常"; // 解析异常
        } catch (UnsupportedEncodingException e) {
            return "编码异常"; // 编码异常
        } catch (InvalidAlgorithmParameterException e) {
            return "摘要参数异常"; // 摘要参数异常
        }
    }
    
    
    /**
     * 生成密钥key对象
     * @param key 密钥字符串
     * @return 密钥对象
     * @throws InvalidKeyException 无效的key
     * @throws NoSuchAlgorithmException 算法名称未发现
     * @throws InvalidKeySpecException 无效的KeySpec
     */
    private static Key keyGenerator(String key) throws InvalidKeyException, NoSuchAlgorithmException, InvalidKeySpecException {
        byte[] input = hexString2Bytes(key);
        DESedeKeySpec desKey = new DESedeKeySpec(input);
        // 创建一个密钥工厂,然后用它把DESKeySpec转化
        SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(KEY_ALGORITHM);
        // 获得一个密钥
        SecretKey secretKey = keyFactory.generateSecret(desKey);
        return secretKey;
    }

    /**
     * 从十六进制字符串到字节数组转化
     * @param key 密钥
     */
    private static byte[] hexString2Bytes(String key) {
        byte[] b = new byte[key.length()/2];
        int j = 0;
        for (int i = 0; i < b.length; i++) {
            char c0 = key.charAt(j++);
            char c1 = key.charAt(j++);
            // c0做b[i]的高字节,c1做低字节
            b[i] = (byte) ((parse(c0)<<4)|parse(c1));
        }
        return b;
    }

    /**
     * 将字符转换为int值
     * @param c 要转化的字符
     * @return ASCII码值
     */
    private static int parse(char c) {
        if (c >= 'a') {
            return (c - 'a' + 10) & 0x0f;
        }
        if (c >= 'A') {
            return (c - 'A' + 10) & 0x0f;
        }
        return (c - '0') & 0x0f;
    }   
    
}
  1. AES加密
    AES是分组密钥,算法输入128位数据,密钥长度也是128位。

/**
 * AES加密工具类
 * <p>
 * 使用:
 * String source = "mazaiting";
 * String key = "123456";
 * <p>
 * String encryptString = AesUtil.encrypt(source, key);
 * System.out.println("加密后: " + encryptString);
 * <p>
 * String decryptString = AesUtil.decrypt(encryptString, key);
 * System.out.println("解密后: " + decryptString);
 * 需要依赖:compile 'commons-codec:commons-codec:1.11'
 * @author mazaiting
 */
public class AesUtil {
  /**
   * 算法名称
   */
  private static final String KEY_ALGORITHM = "AES";
  /**
   * 算法
   */
  private static final String CIPHER_ALGORITHM = "AES";
  
  /**
   * 加密数据
   *
   * @param data 待加密内容
   * @param key  加密的密钥
   * @return 加密后的数据
   */
  public static String encrypt(String data, String key) {
    try {
      // 获得密钥
      Key deskey = keyGenerator(key);
      // 实例化一个密码对象
      Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
      // 密码初始化
      cipher.init(Cipher.ENCRYPT_MODE, deskey);
      // 执行加密
      byte[] bytes = cipher.doFinal(data.getBytes("UTF-8"));
      // 返回Base64编码后的字符串
      return Base64.encodeBase64String(bytes);
    } catch (NoSuchAlgorithmException e) {
      return "加密名称异常"; // 获取加密名称异常
    } catch (UnsupportedEncodingException e) {
      return "未知编码格式"; // 未知编码格式
    } catch (InvalidKeyException e) {
      return "无效Key"; // 无效Key
    } catch (NoSuchPaddingException e) {
      return "无效密码算法"; // 无效密码算法
    } catch (IllegalBlockSizeException e) {
      return "无效字节"; // 无效字节
    } catch (BadPaddingException e) {
      return "解析异常"; // 解析异常
    }
  }
  
  /**
   * 解密数据
   *
   * @param data 待解密的内容
   * @param key  解密的密钥
   * @return 解密后的文字
   */
  public static String decrypt(String data, String key) {
    try {
      // 生成密钥
      Key kGen = keyGenerator(key);
      // 实例化密码对象
      Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
      // 初始化密码对象
      cipher.init(Cipher.DECRYPT_MODE, kGen);
      // 执行解密
      byte[] bytes = cipher.doFinal(Base64.decodeBase64(data));
      // 返回解密后的字符串
      return new String(bytes, "UTF-8");
    } catch (NoSuchAlgorithmException e) {
      return "加密名称异常"; // 获取加密名称异常
    } catch (UnsupportedEncodingException e) {
      return "未知编码格式"; // 未知编码格式
    } catch (InvalidKeyException e) {
      return "无效Key"; // 无效Key
    } catch (NoSuchPaddingException e) {
      return "无效密码算法"; // 无效密码算法
    } catch (IllegalBlockSizeException e) {
      return "无效字节"; // 无效字节
    } catch (BadPaddingException e) {
      return "解析异常"; // 解析异常
    }
  }
  
  /**
   * 获取密钥
   *
   * @param key 密钥字符串
   * @return 返回一个密钥
   * @throws NoSuchAlgorithmException
   * @throws UnsupportedEncodingException
   */
  private static Key keyGenerator(String key) throws NoSuchAlgorithmException, UnsupportedEncodingException {
    KeyGenerator kGen = KeyGenerator.getInstance(KEY_ALGORITHM);
    kGen.init(128, new SecureRandom(hexString2Bytes(key)));
    SecretKey secretKey = kGen.generateKey();
    byte[] encoded = secretKey.getEncoded();
    return new SecretKeySpec(encoded, KEY_ALGORITHM);
  }
  
  
  /**
   * 从十六进制字符串到字节数组转化
   *
   * @param key 密钥
   */
  private static byte[] hexString2Bytes(String key) {
    byte[] b = new byte[key.length() / 2];
    int j = 0;
    for (int i = 0; i < b.length; i++) {
      char c0 = key.charAt(j++);
      char c1 = key.charAt(j++);
      // c0做b[i]的高字节,c1做低字节
      b[i] = (byte) ((parse(c0) << 4) | parse(c1));
    }
    return b;
  }
  
  /**
   * 将字符转换为int值
   *
   * @param c 要转化的字符
   * @return ASCII码值
   */
  private static int parse(char c) {
    if (c >= 'a') {
      return (c - 'a' + 10) & 0x0f;
    }
    if (c >= 'A') {
      return (c - 'A' + 10) & 0x0f;
    }
    return (c - '0') & 0x0f;
  }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,607评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,047评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,496评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,405评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,400评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,479评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,883评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,535评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,743评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,544评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,612评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,309评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,881评论 3 306
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,891评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,136评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,783评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,316评论 2 342

推荐阅读更多精彩内容

  • 这篇文章主要讲述在Mobile BI(移动商务智能)开发过程中,在网络通信、数据存储、登录验证这几个方面涉及的加密...
    雨_树阅读 2,329评论 0 6
  • 本文主要介绍移动端的加解密算法的分类、其优缺点特性及应用,帮助读者由浅入深地了解和选择加解密算法。文中会包含算法的...
    苹果粉阅读 11,449评论 5 29
  • 介绍 加密和解密都使用同一把秘钥,这种加密方法称为对称加密,也称为单密钥加密。简单理解为:加密解密都是同一把钥匙 ...
    Blizzard_liu阅读 793评论 0 1
  • 昨天在回家的车上偶遇了小时候的小伙伴,虽是多时未见但一眼也能认出彼此,相互询问了近来的状况,得知他今年刚考上研究生...
    天颂琳罗阅读 550评论 0 3