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

本文涉及的产品
密钥管理服务KMS,1000个密钥,100个凭据,1个月
简介: 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月前
|
存储 人工智能 算法
数据结构与算法细节篇之最短路径问题:Dijkstra和Floyd算法详细描述,java语言实现。
这篇文章详细介绍了Dijkstra和Floyd算法,这两种算法分别用于解决单源和多源最短路径问题,并且提供了Java语言的实现代码。
105 3
数据结构与算法细节篇之最短路径问题:Dijkstra和Floyd算法详细描述,java语言实现。
|
4月前
|
存储 安全 数据安全/隐私保护
打造安全防线!Python AES&RSA加密工具,黑客绕道走的秘籍
【9月更文挑战第9天】随着数字化时代的到来,信息安全问题日益凸显。本文将介绍如何使用Python结合AES与RSA两种加密算法,构建强大的加密工具。AES以其高效性和强安全性著称,适用于大量数据的快速加密;RSA作为非对称加密算法,在加密小量数据及实现数字签名方面表现卓越。通过整合两者,可以构建既安全又灵活的加密系统。首先,需要安装pycryptodome库。接着,实现AES加密与解密功能,最后利用RSA加密AES密钥,确保其安全传输。这种设计不仅提高了数据传输效率,还增强了密钥交换的安全性,为敏感数据提供坚实保护。
249 43
|
4月前
|
存储 安全 数据安全/隐私保护
浅谈对称加密(AES与DES)
浅谈对称加密(AES与DES)
88 1
|
1天前
|
存储 算法 安全
探究‘公司禁用 U 盘’背后的哈希表算法与 Java 实现
在数字化办公时代,信息安全至关重要。许多公司采取“禁用U盘”策略,利用哈希表算法高效管理外接设备的接入权限。哈希表通过哈希函数将设备标识映射到数组索引,快速判断U盘是否授权。例如,公司预先将允许的U盘标识存入哈希表,新设备接入时迅速验证,未授权则禁止传输并报警。这有效防止恶意软件和数据泄露,保障企业信息安全。 代码示例展示了如何用Java实现简单的哈希表,模拟公司U盘管控场景。哈希表不仅用于设备管理,还在文件索引、用户权限等多方面助力信息安全防线的构建,为企业数字化进程保驾护航。
|
2月前
|
存储 安全 算法
AES算法
【10月更文挑战第30天】AES算法
132 2
|
3月前
|
Java Maven 数据安全/隐私保护
如何实现Java打包程序的加密代码混淆,避免被反编译?
【10月更文挑战第15天】如何实现Java打包程序的加密代码混淆,避免被反编译?
429 2
|
3月前
|
算法 Java 数据中心
探讨面试常见问题雪花算法、时钟回拨问题,java中优雅的实现方式
【10月更文挑战第2天】在大数据量系统中,分布式ID生成是一个关键问题。为了保证在分布式环境下生成的ID唯一、有序且高效,业界提出了多种解决方案,其中雪花算法(Snowflake Algorithm)是一种广泛应用的分布式ID生成算法。本文将详细介绍雪花算法的原理、实现及其处理时钟回拨问题的方法,并提供Java代码示例。
104 2
|
3月前
|
安全 算法 Java
数据库信息/密码加盐加密 —— Java代码手写+集成两种方式,手把手教学!保证能用!
本文提供了在数据库中对密码等敏感信息进行加盐加密的详细教程,包括手写MD5加密算法和使用Spring Security的BCryptPasswordEncoder进行加密,并强调了使用BCryptPasswordEncoder时需要注意的Spring Security配置问题。
232 0
数据库信息/密码加盐加密 —— Java代码手写+集成两种方式,手把手教学!保证能用!
|
4月前
|
数据安全/隐私保护
aes之ecb模式的加密解密
aes之ecb模式的加密解密
|
4月前
|
存储 安全 数据安全/隐私保护
安全升级!Python AES加密实战,为你的代码加上一层神秘保护罩
【9月更文挑战第12天】在软件开发中,数据安全至关重要。本文将深入探讨如何使用Python中的AES加密技术保护代码免受非法访问和篡改。AES(高级加密标准)因其高效性和灵活性,已成为全球最广泛使用的对称加密算法之一。通过实战演练,我们将展示如何利用pycryptodome库实现AES加密,包括生成密钥、初始化向量(IV)、加密和解密文本数据等步骤。此外,还将介绍密钥管理和IV随机性等安全注意事项。通过本文的学习,你将掌握使用AES加密保护敏感数据的方法,为代码增添坚实的安全屏障。
164 8
下一篇
开通oss服务