AES - 对称加密算法简要介绍与JAVA实现

简介: AES - 对称加密算法简要介绍与JAVA实现

【1】AES简介

高级加密标准(AES,Advanced Encryption Standard)为最常见的对称加密算法(微信小程序加密传输就是用这个加密算法的)。

对称加密算法还有:DES算法,3DES算法,TDEA算法,Blowfish算法,RC5算法,IDEA算法。

随着对称密码的发展,DES数据加密标准算法由于密钥长度较小(56位),已经不适应当今分布式开放网络对数据加密安全性的要求,因此1997年NIST公开征集新的数据加密标准,即AES[1]。

经过三轮的筛选,比利时Joan Daeman和Vincent Rijmen提交的Rijndael算法被提议为AES的最终算法。此算法将成为美国新的数据加密标准而被广泛应用在各个领域中。尽管人们对AES还有不同的看法,但总体来说,AES作为新一代的数据加密标准汇聚了强安全性、高性能、高效率、易用和灵活等优点。

AES设计有三个密钥长度:128,192,256位,相对而言,AES的128密钥比DES的56密钥强1021倍[2]。由于历史原因,JDK默认只支持不大于128 bits的密钥,而128 bits的key已能够满足商用安全需求。


AES属于块加密(Block Cipher),块加密中有CBC、ECB、CTR、OFB、CFB等几种工作模式。

AES算法主要包括三个方面:轮变化、圈数和密钥扩展。

对称加密算法也就是加密和解密用相同的密钥,具体的加密流程如下图:

下面简单介绍下各个部分的作用与意义:

  • 明文P

没有经过加密的数据。

密钥K

用来加密明文的密码,在对称加密算法中,加密与解密的密钥是相同的。密钥为接收方与发送方协商产生,但不可以直接在网络上传输,否则会导致密钥泄漏,通常是通过非对称加密算法加密密钥,然后再通过网络传输给对方,或者直接面对面商量密钥。密钥是绝对不可以泄漏的,否则会被攻击者还原密文,窃取机密数据。

  • AES加密函数

设AES加密函数为E,则 C = E(K, P),其中P为明文,K为密钥,C为密文。也就是说,把明文P和密钥K作为加密函数的参数输入,则加密函数E会输出密文C。

  • 密文C

经加密函数处理后的数据

  • AES解密函数

设AES解密函数为D,则 P = D(K, C),其中C为密文,K为密钥,P为明文。也就是说,把密文C和密钥K作为解密函数的参数输入,则解密函数会输出明文P。


在这里简单介绍下对称加密算法与非对称加密算法的区别。

  • 对称加密算法

加密和解密用到的密钥是相同的,这种加密方式加密速度非常快,适合经常发送数据的场合。

缺点是密钥的传输比较麻烦。

  • 非对称加密算法

加密和解密用的密钥是不同的,这种加密方式是用数学上的难解问题构造的,通常加密解密的速度比较慢,适合偶尔发送数据的场合。

优点是密钥传输方便。常见的非对称加密算法为RSA、ECC和EIGamal。


【2】Cipher

Cipher的java doc 如下:

Open Declaration   javax.crypto.Cipher
This class provides the functionality of a cryptographic cipher for encryption and decryption.
//该类提供了密码功能以实现加密和解密
It forms the core of the Java Cryptographic Extension (JCE) framework. 
//它是JCE框架的核心(Java 加密 扩展)
In order to create a Cipher object, the application calls the Cipher's getInstance method, and passes the name of the requested transformation to it. Optionally, the name of a provider may be specified. 
// 为了创建一个密码对象,你需要调用Cipher类的getInstance()方法,并传想要使用的转换名字作为参数。当然了,你可以随意指定这个算法名字,比如 AES/DES/CBC/PKCS5Padding.
A transformation is a string that describes the operation (or set of operations) to be performed on the given input, to produce some output. 
//一个转换过程是描述要在给定输入上执行的操作(或一组操作)的字符串,以产生一些输出。
A transformation always includes the name of a cryptographic algorithm (e.g., DES), and may be followed by a feedback mode and padding scheme. 
//转换总是包含加密算法(例如DES)的名称,并且随后可以使用反馈模式和填充方案。
A transformation is of the form: 
•"algorithm/mode/padding" or 
•"algorithm" 
(in the latter case, provider-specific default values for the mode and padding scheme are used). 
// 转换的表现形式如下:① 算法名/模式/填充;② 算法名;。后一种情况下,模式和填充方案使用供应商指定的默认值。
For example, the following is a valid transformation: 
     Cipher c = Cipher.getInstance("DES/CBC/PKCS5Padding");
Using modes such as CFB and OFB, block ciphers can encrypt data in units smaller than the cipher's actual block size. 
When requesting such a mode, you may optionally specify the number of bits to be processed at a time by appending this number to the mode name as shown in the "DES/CFB8/NoPadding" and "DES/OFB32/PKCS5Padding" transformations.
If no such number is specified, a provider-specific default is used. (For example, the SunJCE provider uses a default of 64 bits for DES.) Thus, block ciphers can be turned into byte-oriented stream ciphers by using an 8 bit mode such as CFB8 or OFB8. 
Modes such as Authenticated Encryption with Associated Data (AEAD) provide authenticity assurances for both confidential data and Additional Associated Data (AAD) that is not encrypted. 
//模式如认证加密相关的数据(AEAD)对机密数据和额外的相关数据(AAD,是不加密的)提供真实性的保证。
(Please see  RFC 5116  for more information on AEAD and AEAD algorithms such as GCM/CCM.) 
//(请参阅RFC 5116对失效和失效算法如GCM和CCM的更多信息。)
Both confidential and AAD data can be used when calculating the authentication tag (similar to a Mac). This tag is appended to the ciphertext during encryption, and is verified on decryption. 
AEAD modes such as GCM/CCM perform all AAD authenticity calculations before starting the ciphertext authenticity calculations. 
To avoid implementations having to internally buffer ciphertext, all AAD data must be supplied to GCM/CCM implementations (via the updateAAD methods) before the ciphertext is processed (via the update and doFinal methods). 
Note that GCM mode has a uniqueness requirement on IVs used in encryption with a given key. 
//注意,当使用给定key进行加密是,GCM模式还对IVS有唯一性要求
When IVs are repeated for GCM encryption, such usages are subject to forgery attacks. 
//当IVs在GCM加密过程中重复时,很可能会受伪造攻击。
Thus, after each encryption operation using GCM mode, callers should re-initialize the cipher objects with GCM parameters which has a different IV value. 
//因此,在使用GCM模式进行每次加密操作之后,调用者应该重新初始化具有不同IV值的GCM参数的密码对象。
     GCMParameterSpec s = ...;
     cipher.init(..., s);
     // If the GCM parameters were generated by the provider, it can
     // be retrieved by:
     // cipher.getParameters().getParameterSpec(GCMParameterSpec.class);
     cipher.updateAAD(...);  // AAD
     cipher.update(...);     // Multi-part update
     cipher.doFinal(...);    // conclusion of operation
     // Use a different IV value for every encryption
     byte[] newIv = ...;
     s = new GCMParameterSpec(s.getTLen(), newIv);
     cipher.init(..., s);
     ...
Every implementation of the Java platform is required to support the following standard Cipher transformations with the keysizes in parentheses: 
//java平台的每一个实现都要求支持一下标准密码转换(使用括号内的keysize)。
•AES/CBC/NoPadding (128)
•AES/CBC/PKCS5Padding (128)
•AES/ECB/NoPadding (128)
•AES/ECB/PKCS5Padding (128)
•DES/CBC/NoPadding (56)
•DES/CBC/PKCS5Padding (56)
•DES/ECB/NoPadding (56)
•DES/ECB/PKCS5Padding (56)
•DESede/CBC/NoPadding (168)
•DESede/CBC/PKCS5Padding (168)
•DESede/ECB/NoPadding (168)
•DESede/ECB/PKCS5Padding (168)
•RSA/ECB/PKCS1Padding (1024, 2048)
•RSA/ECB/OAEPWithSHA-1AndMGF1Padding (1024, 2048)
•RSA/ECB/OAEPWithSHA-256AndMGF1Padding (1024, 2048)
These transformations are described in the  Cipher section of the Java Cryptography Architecture Standard Algorithm Name Documentation. 
Consult the release documentation for your implementation to see if any other transformations are supported.

这里需要注意几个关键词:

密钥长度(Key Size)

加密模式(Cipher Mode)

填充方式(Padding)

初始向量(Initialization Vector)


【3】Java实现

第一种:

  • 不指定mode/padding和IV。
package com.hh.common.encrypt;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
/**
 * AES对称加密和解密
 */
public class AesUtil {
    /** 
     * 加密 
     *  aes(src.getBytes,key)
     * @param encryptStr 
     * @return 
     */  
    public static byte[] encrypt(byte[] src, String key) throws Exception {
      // 返回实现指定算法的密码对象实例
        Cipher cipher = Cipher.getInstance("AES");  
        // 根据key产生指定算法的秘钥
        SecretKeySpec securekey = new SecretKeySpec(key.getBytes(),"AES");  
        //设置密钥和加密形式  
        cipher.init(Cipher.ENCRYPT_MODE, securekey);
        // 依据init,执行具体操作
        byte[] doFinal = cipher.doFinal(src); 
        return doFinal;  
    } 
    /** 
     * 加密 
     *  base64(aes(src,key))
     * @param encryptStr 
     * @return 
     */  
    public static String encryptBase64(String src, String key) throws Exception {  
      Cipher cipher = Cipher.getInstance("AES");  
      SecretKeySpec securekey = new SecretKeySpec(key.getBytes(),"AES");  
      cipher.init(Cipher.ENCRYPT_MODE, securekey);//设置密钥和加密形式  
      byte[] doFinal = cipher.doFinal(src.getBytes());
      String encode = Base64Util.encode(doFinal);
      return encode;  
    } 
    /** 
     * 解密 
     *  aes(src.getBytes,key)
     * @param decryptStr 
     * @return 
     * @throws Exception 
     */  
    public static byte[] decrypt(byte[] src, String key)  throws Exception  {  
        Cipher cipher = Cipher.getInstance("AES");  
        SecretKeySpec securekey = new SecretKeySpec(key.getBytes(), "AES");//设置加密Key  
        cipher.init(Cipher.DECRYPT_MODE, securekey);//设置密钥和解密形式  
        return cipher.doFinal(src);  
    } 
    /** 
     * 解密 
     *  decryptBase64--->decryptAes
     * @param decryptStr 
     * @return 
     * @throws Exception 
     */  
    public static String decryptBase64(String src, String key)  throws Exception  {  
      byte[] decode = Base64Util.decode(src);
      Cipher cipher = Cipher.getInstance("AES");  
      SecretKeySpec securekey = new SecretKeySpec(key.getBytes(), "AES");//设置加密Key  
      cipher.init(Cipher.DECRYPT_MODE, securekey);//设置密钥和解密形式  
      byte[] doFinal = cipher.doFinal(decode);
      return new String(doFinal);  
    }    
}

第二种:

  • 指定mode/padding和IV。
package com.hh.common.encrypt;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
public class AesUtil2 {
      /**
       * [加密]<BR>
       *base64(aes)
       * @param sSrc 源字符串
       * @param sKey 加密key
       * @return 加密后字符串
       * @throws Exception 异常抛出
       */
      public static String encrypt(String sSrc, String sKey) throws Exception {
          if (sKey == null) {
              return null;
          }
          // 判断Key是否为16位
          if (sKey.length() != 16) {
              return null;
          }
          // 返回实现指定算法的密码对象实例
          Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
          // 根据key产生指定算法的秘钥
          byte[] raw = sKey.getBytes();
          SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
          IvParameterSpec iv = new IvParameterSpec("0102030405060708".getBytes());
          cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv);
          byte[] encrypted = cipher.doFinal(sSrc.getBytes());
          //此处使用BASE64做转码功能,同时能起到2次加密的作用。
          return Base64Util.encode(encrypted);
      }
      /**
       * [解密]<BR>
       *decryptBase64--->decryptAes
       * @param sSrc 源字符串
       * @param sKey 解密key
       * @return 解密后字符串
       * @throws Exception 异常抛出
       */
      public static String decrypt(String sSrc, String sKey) throws Exception {
          try {
              // 判断Key是否正确
              if (sKey == null) {
                  return null;
              }
              // 判断Key是否为16位
              if (sKey.length() != 16) {
                  return null;
              }
              Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
              IvParameterSpec iv = new IvParameterSpec(
                      "0102030405060708".getBytes());
              byte[] raw = sKey.getBytes("ASCII");
              SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
              cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv);
              byte[] encrypted1 = Base64Util.decode(sSrc);
              try {
                  byte[] original = cipher.doFinal(encrypted1);
                  String originalString = new String(original);
                  return originalString;
              } catch (Exception e) {
                  e.printStackTrace();
                  return null;
              }
          } catch (Exception ex) {
              ex.printStackTrace();
              return null;
          }
      }
}

需要注意的时是,此时的key为16bytes!


测试结果如下:

  @Test
  public void testAes(){
    String key = "1020457fddsaf774";
    String src = "今天下雪了";
    try {
      String encryptBase64 = AesUtil.encryptBase64(src, key);
      System.out.println("AesUtil 加密 : "+encryptBase64);
      String decryptBase64 = AesUtil.decryptBase64(encryptBase64,key);
      System.out.println("AesUtil 解密 : "+decryptBase64);
      String encryptBase642 = AesUtil2.encrypt(src, key);
      System.out.println("AesUtil2 加密 : "+encryptBase642);
      String decryptBase642 = AesUtil2.decrypt(encryptBase642,key);
      System.out.println("AesUtil2 解密 : "+decryptBase642);
    } catch (Exception e) {
      e.printStackTrace();
    }
  }

输出如下:

AesUtil 加密 : QluEM37CA+LM1CW7heLhng==

AesUtil 解密 : 今天下雪了

AesUtil2 加密 : dMSibYtBbF1CoPRGw/wzKA==

AesUtil2 解密 : 今天下雪了

【Tips】

需要注意的是,加密后的结果转换为字符串,可能会有"+“存在,在网络传输过程中,有可能被编码为” "!

解决办法:对其进行URL编码!


目录
相关文章
|
2天前
|
存储 算法 安全
如何在Java中实现加密解密
如何在Java中实现加密解密
|
2天前
|
存储 算法 安全
实现Java应用的数据加密与解密技术
实现Java应用的数据加密与解密技术
|
2天前
|
存储 安全 算法
Java中的数据加密与数字签名技术
Java中的数据加密与数字签名技术
|
4天前
|
存储 算法 安全
如何在Java中实现加密解密
如何在Java中实现加密解密
|
2天前
|
机器学习/深度学习 分布式计算 算法
在Java中使用机器学习算法的实际案例
在Java中使用机器学习算法的实际案例
|
2天前
|
存储 安全 Java
Java中的加密与安全传输协议实现
Java中的加密与安全传输协议实现
|
2天前
|
存储 缓存 算法
Java中的数据结构与算法优化实践
Java中的数据结构与算法优化实践
|
3天前
|
搜索推荐 算法 Java
优化Java中大数据量排序算法
优化Java中大数据量排序算法
|
4天前
|
算法 Java 数据安全/隐私保护
Java中的位操作与算法优化
Java中的位操作与算法优化
|
4天前
|
安全 算法 Java
Java中的数据加密与安全传输
Java中的数据加密与安全传输