加密算法加密算法通常分为对称加密算法和非对称加密算法:对称加密算法(symmetric-keycryptography):使用相同的密钥进行加密和解密。常用的对称加密算法有DES和AES。非对称密钥密码术:不同的密钥用于加密和解密。例如,用公钥加密的内容只能用私钥解密,所以也叫公钥密码。使用最广泛的非对称加密算法是RSA算法。两者的使用场景不同,经常一起使用。例如,SSL/TLS协议结合了对称加密算法和非对称加密算法。本文主要介绍最常用的对称加密算法:AES。AESAES的全称是AdvancedEncryptionStandard,是一种对称加密算法。AES的出现主要是用来替代DES加密算法,因为AES的安全性相对更高。AES被广泛使用。可以说只要上网,无论是手机APP还是网页应用,都离不开AES加密算法。因为大部分网站,包括手机APP的后台接口,都已经使用了HTTPS协议,而HTTPS在数据传输阶段大多采用AES对称加密算法。在学习AES之前,首先要知道以下规则:AES是一种块加密算法。加密时,会将原始数据按大小分成块进行加密。块大小固定为128位(即16字节)AES密钥长度可以是128、192或256位(即16、25或32字节),密钥长度越长,安全性越高,并且性能越低AES工作模式AES加密算法的工作模式有很多种,如:ECB、CBC、OFB、CFB、CTR、XTS、OCB、GCM。不同的模式参数和加密过程不同,但核心仍然是AES算法。本文主要介绍三种模式:ECB、CBC、GCM。AES填充方法由于AES是一种块加密算法,加密时会将原始数据分成128位(即16字节)的块进行加密。如果待加密的原始数据不是16字节的整数倍时,需要对原始数据进行填充,使其成为16字节的整数倍。常用的填充方式有PKCS5Padding、ISO10126Padding等。另外,如果可以保证待加密原始数据的大小为16字节的整数倍,也可以选择不填充,即NoPadding。Java中的AESJava中的javax.crypto.Cipher类提供加密和解密功能。创建密码:Ciphercipher=Cipher.getInstance("AES/CBC/PKCS5Padding");Cipher类的getInstance方法需要传入一个加密算法名作为参数来创建对应的Cipher,其格式为algorithm/mode/padding,即算法名/工作模式/padding方法,比如AES/CBC/PKCS5填充。可选的加密方式可以参考文档:https://docs.oracle.com/javas...ECBECB全称Electroniccodebook(电子密码本),将需要加密的数据拆分成块,以及每个块都是独立加密的。代码:publicstaticbyte[]encryptECB(byte[]data,byte[]key)throwsNoSuchPaddingException,NoSuchAlgorithmException,InvalidKeyException,BadPaddingException,IllegalBlockSizeException{Ciphercipher=Cipher.getInstance("AES/ECB/PKCS5Padding");cipher.init(Cipher.ENCRYPT_MODE,newSecretKeySpec(key,“AES”));byte[]result=cipher.doFinal(数据);返回结果;}publicstaticbyte[]decryptECB(byte[]data,byte[]key)throwsNoSuchPaddingException,NoSuchAlgorithmException,InvalidKeyException,BadPaddingException,IllegalBlockSizeException{Ciphercipher=Cipher.getInstance("AES/ECB/PKCS5Padding");cipher.init(Cipher.DECRYPT_MODE,newSecretKeySpec(key,“AES”));byte[]result=cipher.doFinal(数据);返回结果;}publicstaticvoidmain(String[]args)throwsIllegalBlockSizeException,InvalidKeyException,BadPaddingException,NoSuchAlgorithmException,NoSuchPaddingException{Stringdata="HelloWorld";//待加密的明文字符串键="12345678abcdefgh";//密钥长度只能是16、25或32字节byte[]ciphertext=encryptECB(data.getBytes(),key.getBytes());System.out.println("ECB方式加密结果(Base64):"+Base64.getEncoder().encodeToString(ciphertext));byte[]plaintext=decryptECB(密文,key.getBytes());System.out.println("解密结果:"+newString(plaintext));}由于加密后的密文是二进制格式而不是字符串,所以这里使用Base64编码,将其转换成字符串,方便输出查看输出:ECB模式加密结果(Base64):bB0gie8pCE2RBQoIAAIxeA==解密结果:HelloWorld需要注意的是,AES密钥长度只能是16、25或32字节,如果不满足要求,会出现异常:java.security.InvalidKeyException:InvalidAESkeylengthCBC模式有一个致命的缺点,由于该模式对每个块独立加密,会导致同一块明文被加密成同一块密文,相对不安全。下图就是一个很好的例子:CBCCBC全称为密码块链接(Cipher-blockchaining),它的出现解决了ECB将同一个明文块加密成同一个密文块的问题。CBC引入了初始向量(IV,InitializationVector)的概念。第一个明文块在加密前与IV进行异或,随后的每个明文块在加密前与前一个密文块进行异或。代码:publicstaticbyte[]encryptCBC(byte[]data,byte[]key,byte[]iv)throwsNoSuchPaddingException,NoSuchAlgorithmException,InvalidKeyException,BadPaddingException,IllegalBlockSizeException,InvalidAlgorithmParameterException{Ciphercipher=Cipher.getInstance("AES/CBC/PKCS5Padding");cipher.init(Cipher.ENCRYPT_MODE,newSecretKeySpec(key,“AES”),newIvParameterSpec(iv));byte[]result=cipher.doFinal(数据);返回结果;}publicstaticbyte[]decryptCBC(byte[]data,byte[]key,byte[]iv)throwsNoSuchPaddingException,NoSuchAlgorithmException,InvalidKeyException,BadPaddingException,IllegalBlockSizeException,InvalidAlgorithmParameterException{Ciphercipher=Cipher.getInstance("AES/CBC/PKCS5Padding");cipher.init(Cipher.DECRYPT_MODE,newSecretKeySpec(key,"AES"),newIvParameterSpec(iv));byte[]result=cipher.doFinal(数据);返回结果;}publicstaticvoidmain(String[]args)throwsIllegalBlockSizeException,InvalidKeyException,BadPaddingException,NoSuchAlgorithmException,NoSuchPaddingException,InvalidAlgorithmParameterException{字符串数据="HelloWorld";//待加密的原文Stringkey="12345678abcdefgh";//密钥长度只能是16、25或32字节Stringiv="iviviviviviviviv";//CBC模式需要使用初始向量参数byte[]ciphertext=encryptCBC(data.getBytes(),key.getBytes(),iv.getBytes());System.out.println("CBC方式加密结果(Base64):"+Base64.getEncoder().encodeToString(ciphertext));byte[]plaintext=decryptCBC(ciphertext,key.getBytes(),iv.getBytes());System.out.println("解密结果:"+newString(plaintext));}输出:CBC模式加密结果(Base64):K7bSB51+KxfqaMjJOsPAQg==解密结果:HelloWorld由于CBC的每个明文块加密依赖于前一个块的加密结果,它的主要缺点是加密过程是串行的,不能并行化。GCMGCM全称Galois/CounterMode,是一种认证加密算法。它不仅提供加密和解密,还提供数据完整性验证以防止篡改。AES-GCM模式是目前使用最广泛的模式。可以尝试抓包,看看目前主流的https网站,大部分都是基于GCM模式的。下图是浏览器使用抓包工具Charles访问https网站使用的加密算法:可以看到浏览器一般支持AES-GCM和AES-CBC模式,最终服务器选择使用AES-GCM.AES-GCM认证加密需要以下参数:待加密明文密钥的初始向量IV附加认证数据(AAD)代码:publicstaticbyte[]encryptGCM(byte[]data,byte[]key,byte[]iv,byte[]aad)抛出NoSuchPaddingException,NoSuchAlgorithmException,InvalidKeyException,BadPaddingException,IllegalBlockSizeException,InvalidAlgorithmParameterException{Ciphercipher=Cipher.getInstance("AES/GCM/NoPadding");cipher.init(SpCipher.DEENCRY_Key),newGCMParameterSpec(128,iv));cipher.updateAAD(aad);byte[]result=cipher.doFinal(数据);返回结果;}publicstaticbyte[]decryptGCM(byte[]data,byte[]key,byte[]iv,byte[]aad)throwsNoSuchPaddingException,NoSuchAlgorithmException,InvalidKeyException,BadPaddingException,IllegalBlockSizeException,InvalidAlgorithmParameterException{Ciphercipher=Cipher.getInstance("AES/GCM/NoPadding");cipher.init(Cipher.DECRYPT_MODE,newSecretKeySpec(key,"AES"),newGCMParameterSpec(128,iv));cipher.updateAAD(aad);byte[]result=cipher.doFinal(数据);返回结果;}publicstaticvoidmain(String[]args)throwsIllegalBlockSizeException,InvalidKeyException,BadPaddingException,NoSuchAlgorithmException,NoSuchPaddingException,InvalidString{="HelloWorld";//待加密的原文Stringkey="12345678abcdefgh";//密钥长度只能是16、25或32字节Stringiv="iviviviviviviv";Stringaad="aad";//AAD无限长,可以为空byte[]ciphertext=encryptGCM(data.getBytes(),key.getBytes(),iv.getBytes(),aad.getBytes());System.out.println("GCM模式加密结果(Base64):"+Base64.getEncoder().encodeToString(ciphertext));byte[]plaintext=decryptGCM(ciphertext,key.getBytes(),iv.getBytes(),aad.getBytes());系统输出。println("解密结果:"+newString(plaintext));}输出:GCM模式加密结果(Base64):1UxXmFpdUwMnpI7rh0XfmFqtdZSHTbNC/08g解密结果:HelloWorldAES-GCM是一种流加密(Streamcipher)算法,所以对应paddingmode为NoPadding,即不需要padding参考文档维基百科-AES维基百科-工作模式javax.crypto.CipherGCM跟我学公众号
