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编码!


目录
相关文章
|
3月前
|
存储 监控 算法
企业上网监控场景下布隆过滤器的 Java 算法构建及其性能优化研究
布隆过滤器是一种高效的数据结构,广泛应用于企业上网监控系统中,用于快速判断员工访问的网址是否为违规站点。相比传统哈希表,它具有更低的内存占用和更快的查询速度,支持实时拦截、动态更新和资源压缩,有效提升系统性能并降低成本。
73 0
|
3月前
|
存储 负载均衡 算法
我们来说一说 Java 的一致性 Hash 算法
我是小假 期待与你的下一次相遇 ~
109 1
|
4月前
|
存储 算法 安全
Java中的对称加密算法的原理与实现
本文详细解析了Java中三种常用对称加密算法(AES、DES、3DES)的实现原理及应用。对称加密使用相同密钥进行加解密,适合数据安全传输与存储。AES作为现代标准,支持128/192/256位密钥,安全性高;DES采用56位密钥,现已不够安全;3DES通过三重加密增强安全性,但性能较低。文章提供了各算法的具体Java代码示例,便于快速上手实现加密解密操作,帮助用户根据需求选择合适的加密方案保护数据安全。
346 58
|
4月前
|
存储 Java 数据安全/隐私保护
Java技术栈揭秘:Base64加密和解密文件的实战案例
以上就是我们今天关于Java实现Base64编码和解码的实战案例介绍。希望能对你有所帮助。还有更多知识等待你去探索和学习,让我们一同努力,继续前行!
327 5
|
4月前
|
算法 安全 数据安全/隐私保护
基于AES的图像加解密算法matlab仿真,带GUI界面
本程序基于AES算法实现图像的加解密功能,并提供MATLAB GUI界面操作,支持加密与解密。运行环境为MATLAB 2022A,测试结果无水印。核心代码通过按钮回调函数完成AES加密与解密流程,包括字节替换、行移位、列混淆及密钥加等步骤。解密过程为加密逆向操作,确保数据安全性与完整性。完整程序结合128位块加密与可选密钥长度,适用于图像信息安全场景。
|
5月前
|
人工智能 算法 NoSQL
LRU算法的Java实现
LRU(Least Recently Used)算法用于淘汰最近最少使用的数据,常应用于内存管理策略中。在Redis中,通过`maxmemory-policy`配置实现不同淘汰策略,如`allkeys-lru`和`volatile-lru`等,采用采样方式近似LRU以优化性能。Java中可通过`LinkedHashMap`轻松实现LRUCache,利用其`accessOrder`特性和`removeEldestEntry`方法完成缓存淘汰逻辑,代码简洁高效。
198 0
|
5月前
|
存储 缓存 监控
上网行为监控系统剖析:基于 Java LinkedHashMap 算法的时间序列追踪机制探究
数字化办公蓬勃发展的背景下,上网行为监控系统已成为企业维护信息安全、提升工作效能的关键手段。该系统需实时记录并深入分析员工的网络访问行为,如何高效存储和管理这些处于动态变化中的数据,便成为亟待解决的核心问题。Java 语言中的LinkedHashMap数据结构,凭借其独有的有序性特征以及可灵活配置的淘汰策略,为上网行为监控系统提供了一种兼顾性能与功能需求的数据管理方案。本文将对LinkedHashMap在上网行为监控系统中的应用原理、实现路径及其应用价值展开深入探究。
106 3
|
5月前
|
负载均衡 算法 关系型数据库
大数据大厂之MySQL数据库课程设计:揭秘MySQL集群架构负载均衡核心算法:从理论到Java代码实战,让你的数据库性能飙升!
本文聚焦 MySQL 集群架构中的负载均衡算法,阐述其重要性。详细介绍轮询、加权轮询、最少连接、加权最少连接、随机、源地址哈希等常用算法,分析各自优缺点及适用场景。并提供 Java 语言代码实现示例,助力直观理解。文章结构清晰,语言通俗易懂,对理解和应用负载均衡算法具有实用价值和参考价值。
大数据大厂之MySQL数据库课程设计:揭秘MySQL集群架构负载均衡核心算法:从理论到Java代码实战,让你的数据库性能飙升!
|
6月前
|
存储 机器学习/深度学习 监控
如何监控员工的电脑——基于滑动时间窗口的Java事件聚合算法实现探析​
在企业管理场景中,如何监控员工的电脑操作行为是一个涉及效率与合规性的重要课题。传统方法依赖日志采集或屏幕截图,但数据量庞大且实时性不足。本文提出一种基于滑动时间窗口的事件聚合算法,通过Java语言实现高效、低资源占用的监控逻辑,为如何监控员工的电脑提供一种轻量化解决方案。
135 3
|
6月前
|
算法 安全 数据安全/隐私保护
基于AES的遥感图像加密算法matlab仿真
本程序基于MATLAB 2022a实现,采用AES算法对遥感图像进行加密与解密。主要步骤包括:将彩色图像灰度化并重置大小为256×256像素,通过AES的字节替换、行移位、列混合及轮密钥加等操作完成加密,随后进行解密并验证图像质量(如PSNR值)。实验结果展示了原图、加密图和解密图,分析了图像直方图、相关性及熵的变化,确保加密安全性与解密后图像质量。该方法适用于保护遥感图像中的敏感信息,在军事、环境监测等领域具有重要应用价值。
208 35