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


目录
相关文章
|
2月前
|
搜索推荐 算法 Java
手写快排:教你用Java写出高效排序算法!
快速排序(QuickSort)是经典的排序算法之一,基于分治思想,平均时间复杂度为O(n log n),广泛应用于各种场合。在这篇文章中,我们将手写一个Java版本的快速排序,从基础实现到优化策略,并逐步解析代码背后的逻辑。
80 1
|
27天前
|
安全 Java 数据安全/隐私保护
- 代码加密混淆工具-Java 编程安全性
在Java编程领域,保护代码安全与知识产权至关重要。本文探讨了代码加密混淆工具的重要性,并介绍了五款流行工具:ProGuard、DexGuard、Jscrambler、DashO 和 Ipa Guard。这些工具通过压缩、优化、混淆和加密等手段,提升代码安全性,保护知识产权。ProGuard 是开源工具,用于压缩和混淆Java代码;DexGuard 专为Android应用程序设计,提供强大加密功能;Jscrambler 基于云,保护Web和移动应用的JavaScript及HTML5代码;DashO 支持多种Java平台和
54 1
|
2月前
|
设计模式 缓存 算法
揭秘策略模式:如何用Java设计模式轻松切换算法?
【8月更文挑战第30天】设计模式是解决软件开发中特定问题的可重用方案。其中,策略模式是一种常用的行为型模式,允许在运行时选择算法行为。它通过定义一系列可互换的算法来封装具体的实现,使算法的变化与客户端分离。例如,在电商系统中,可以通过定义 `DiscountStrategy` 接口和多种折扣策略类(如 `FidelityDiscount`、`BulkDiscount` 和 `NoDiscount`),在运行时动态切换不同的折扣逻辑。这样,`ShoppingCart` 类无需关心具体折扣计算细节,只需设置不同的策略即可实现灵活的价格计算,符合开闭原则并提高代码的可维护性和扩展性。
41 2
|
2月前
|
安全 Java 应用服务中间件
网络安全的护城河:漏洞防御与加密技术深入浅出Java并发编程
【8月更文挑战第31天】在数字世界的棋盘上,每一次点击都可能是一步棋。网络安全的战场无声却激烈,漏洞如同裂缝中的风,悄无声息地侵袭着数据的堡垒。本文将揭示网络漏洞的隐蔽角落,探讨如何通过加密技术筑起防线,同时提升个人和组织的安全意识,共同守护我们的数字家园。
|
2月前
|
安全 算法 Java
java系列之~~网络通信安全 非对称加密算法的介绍说明
这篇文章介绍了非对称加密算法,包括其定义、加密解密过程、数字签名功能,以及与对称加密算法的比较,并解释了非对称加密在网络安全中的应用,特别是在公钥基础设施和信任网络中的重要性。
|
2月前
|
存储 算法 Java
在Java中使用MD5对用户输入密码进行加密存储、同时登录验证。
这篇文章详细介绍了在Java项目中如何使用MD5算法对用户密码进行加密存储和登录验证,包括加入依赖、编写MD5工具类、注册时的密码加密和登录时的密码验证等步骤,并通过示例代码和数据库存储信息展示了测试效果。
在Java中使用MD5对用户输入密码进行加密存储、同时登录验证。
|
2月前
|
数据采集 搜索推荐 算法
【高手进阶】Java排序算法:从零到精通——揭秘冒泡、快速、归并排序的原理与实战应用,让你的代码效率飙升!
【8月更文挑战第21天】Java排序算法是编程基础的重要部分,在算法设计与分析及实际开发中不可或缺。本文介绍内部排序算法,包括简单的冒泡排序及其逐步优化至高效的快速排序和稳定的归并排序,并提供了每种算法的Java实现示例。此外,还探讨了排序算法在电子商务、搜索引擎和数据分析等领域的广泛应用,帮助读者更好地理解和应用这些算法。
25 0
|
2月前
|
算法 Java
HanLP — HMM隐马尔可夫模型 -- 维特比(Viterbi)算法 --示例代码 - Java
HanLP — HMM隐马尔可夫模型 -- 维特比(Viterbi)算法 --示例代码 - Java
33 0
|
2月前
|
Java C# 数据安全/隐私保护
如何 使 Java、C# md5 加密的值保持一致
如何 使 Java、C# md5 加密的值保持一致
27 0
|
3天前
|
安全 网络协议 网络安全
网络安全与信息安全:漏洞、加密与意识的三重奏
【9月更文挑战第32天】在数字世界的交响乐中,网络安全是那不可或缺的乐章。本文将带您深入探索网络安全的三大主题:网络漏洞的识别与防范、加密技术的奥秘以及安全意识的重要性。通过深入浅出的方式,我们将一起揭开这些概念的神秘面纱,并学习如何在实际生活中应用它们来保护自己的数字足迹。让我们开始这场既刺激又富有教育意义的旅程,提升个人和组织的网络安全防御能力。
下一篇
无影云桌面