前言
今天我们来聊聊数据加密与隐私相关话题。本人开发的加密工具,欢迎体验!https://www.pgyer.com/cryptotools 。AES加密,通俗的话来讲,就是用一个key把原数据变成一个新数据,也通过这个key还原成原数据。所以,它是一种对称的加密方式。只要别人不知道这个key,就无法解开数据的内容。它是DES加密的升级版本,由于AES的密钥长度更长,所以更安全。但是它的效率略有降低,不过这对于现代的计算机设备来说可以忽略不计。数字加密在当今数字时代的重要性不容忽视。
数字加密的重要性
数字加密的重要性,有以下几个主要方面:
- 数据保护:加密技术将信息转化为无法读取的格式,只有授权方可以解密。这是防止数据泄露的第一道防线,尤其在传输敏感信息(如个人身份信息、金融数据等)时尤为重要。
- 隐私保障:加密技术能够保护用户的隐私,确保通信双方之外的任何人无法窃听或截取对话内容。这在个人通信、电子邮件、社交媒体等领域非常关键。
- 安全通信:在互联网的应用中,许多协议(如HTTPS)依赖于加密来确保数据传输的安全性,防止中间人攻击和数据篡改。
- 身份验证:数字加密还用于验证身份,例如数字签名,确保发件人或设备的真实身份,防止身份冒用。
- 保护区块链和数字货币:加密技术是区块链和加密货币(如比特币)的核心,它确保了交易的安全性、不可篡改性和用户的匿名性。
- 防止数据篡改:加密不仅保护数据的机密性,还能通过散列函数(如SHA-256)等加密算法检测数据是否被篡改。这在文件验证、软件更新等场景中非常有用。
通过这些方式,数字加密在保障网络安全、数据隐私以及构建安全数字基础设施中起到了至关重要的作用。
AES加密介绍
AES(Advanced Encryption Standard,高级加密标准)是一种对称加密算法,用于加密和解密数据。AES 由美国国家标准与技术研究院(NIST)于2001年发布,成为取代DES(数据加密标准)的新加密标准。它广泛应用于政府、企业和个人数据保护中,特别是在金融、通信和网络安全领域。
AES的主要特点
-
对称加密:
- 同一密钥:AES是一种对称加密算法,这意味着加密和解密都使用相同的密钥。也就是说,发件人和接收人必须共享同一个密钥才能进行加密和解密。
-
加密块大小:
- 固定的块大小:AES处理的数据块大小是128位(16字节)。如果明文长度不足128位,AES使用填充方式(如PKCS7)补齐。
-
密钥长度:
-
AES支持三种不同的密钥长度:128位、192位和256位。
- AES-128:使用128位密钥,安全性高且速度较快。
- AES-192:使用192位密钥。
- AES-256:使用256位密钥,提供最高的安全性,但速度稍慢。
-
-
加密模式: AES加密常与不同的操作模式结合使用,常见的模式有:
- ECB模式(电子密码本模式):每个块独立加密,不推荐使用,容易导致模式被破解。
- CBC模式(密码分组链接模式):每个块的加密依赖于前一个块,常用于网络通信,确保数据的随机性。
- CFB、OFB模式:这些模式常用于流加密或特殊场景。
-
安全性:
- AES算法基于复杂的代数结构,当前没有已知的高效攻击方法,即使使用现代计算机也无法在合理时间内破解。AES-256被认为是最安全的算法之一,适用于高安全性需求场景。
AES的应用
- 数据加密:用于加密文件、数据库和存储设备,以保护敏感信息不被泄露。
- 网络通信:广泛应用于TLS/SSL等加密协议,确保互联网通信的安全性(如HTTPS)。
- 无线通信:Wi-Fi安全协议(如WPA2)使用AES加密,以确保无线网络传输数据的安全。
- 区块链:加密货币和区块链技术中,也使用AES确保钱包和交易数据的安全。
AES加密因其高效、安全和标准化而成为当今广泛使用的加密技术,是保护个人隐私、企业数据和国家安全的基石。
AES加密中的重要概念
初始化向量(偏移量) / IV
加密方式
电子密码本 / ECB
ECB不需要偏移量iv
密码块连接 / CBC
在CBC中,每个明文块要先与前一个密文块进行异或后再加密,每个密文块都依赖于前面的所有明文块。
密文反馈 / CFB
CFB的加密跟解密过程几乎完全相同,注意它在解密过程中使用的是AES加密而不是AES解密。
输出反馈 / OFB
这个很简单,跟CFB128很相似,不同的是它是直接把输出块作为下一个块加密的输入块。
计数器模式 / CTR
COUNTER
是整个CTR模式的核心所在。它是由IV经过一定的规则之后生成的一段数据,长度与数据块的长度相等。接着我们要选定一个数m,这个m是用于确定计数器中累加部分的大小的,通常取块大小的一半,块大小是奇数就四舍五入(当然对于AES并没有这个问题)。初始的计数器COUNTER1
是长度固定的任意一个随机字节序列,而不是像想象中那样一段随机数后面跟着一段0。
填充
NoPadding
顾名思义,就是不填充。缺点就是只能加密长为128bits倍数的信息,一般不会使用。
ZeroPadding
全部填充0x00
,无论缺多少全部填充0x00
,已经是128bits倍数仍要填充。
… | DD DD DD DD DD DD DD DD | DD DD DD DD 00 00 00 00 |
PKCS5和PKCS7
对于AES来说PKCS5Padding和PKCS7Padding是完全一样的,不同在于PKCS5限定了块大小为8bytes而PKCS7没有限定。因此对于AES来说两者完全相同,但是对于Rijndael就不一样了。AES是Rijndael在块大小为8bytes时的特例,对于使用其他信息块大小的Rijndael算法只能使用PKCS7。
在AES加密当中严格来说是不能使用pkcs5的,因为AES的块大小是16bytes而pkcs5只能用于8bytes,通常我们在AES加密中所说的pkcs5指的就是pkcs7。
… | DD DD DD DD DD DD DD DD | DD DD DD DD 04 04 04 04 |
ISO 10126
最后一个字节是填充的字节数(包括最后一字节),其他全部填随机数
… | DD DD DD DD DD DD DD DD | DD DD DD DD 81 A6 23 04 |
ANSI X9.23
跟ISO 10126很像,只不过ANSI X9.23其他字节填的都是0而不是随机数
… | DD DD DD DD DD DD DD DD | DD DD DD DD 00 00 00 04
AES加密的代码实现
Android&Java
/**
* AES encryption.
* 简体中文:AES加密。
*
* @param secretKey Key
* @param transformation In the field of encryption, it usually refers to the combination
* of encryption algorithms, modes, and padding.
* @param iv Offset
* @param data Data to be encrypted
*/
public static String encryptAES(String secretKey, String transformation, IvParameterSpec iv, String data) {
try {
Cipher cipher = Cipher.getInstance(transformation);
cipher.init(Cipher.ENCRYPT_MODE, getSecretKey(secretKey, AES), iv);
byte[] encryptByte = cipher.doFinal(data.getBytes(StandardCharsets.UTF_8));
return base64Encode(encryptByte);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* Encrypting with AES, using the default mode.
* 简体中文:AES加密,使用默认的方式。
*
* @param secretKey Key
* @param data Data to be encrypted
*/
public static String encryptAES(String secretKey, String data) {
try {
Cipher cipher = Cipher.getInstance(AES_ECB_PKCS5);
cipher.init(Cipher.ENCRYPT_MODE, getSecretKey(secretKey, AES));
byte[] encryptByte = cipher.doFinal(data.getBytes(StandardCharsets.UTF_8));
return base64Encode(encryptByte);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* Decrypting with AES.
* 简体中文:AES解密。
*
* @param secretKey Key
* @param transformation In the field of encryption, it usually refers to the combination
* of encryption algorithms, modes, and padding.
* @param iv Offset
* @param base64Data Base64 data to be decrypted
*/
public static String decryptAES(String secretKey, String transformation, IvParameterSpec iv, String base64Data) {
try {
byte[] data = base64Decode(base64Data);
Cipher cipher = Cipher.getInstance(transformation);
cipher.init(Cipher.DECRYPT_MODE, getSecretKey(secretKey, AES), iv);
byte[] result = cipher.doFinal(data);
return new String(result, StandardCharsets.UTF_8);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* Decrypting using AES, using the default mode.
* 简体中文:AES解密,使用默认的方式。
*
* @param secretKey Key
* @param base64Data Base64 data to be decrypted
*/
public static String decryptAES(String secretKey, String base64Data) {
try {
byte[] data = base64Decode(base64Data);
Cipher cipher = Cipher.getInstance(AES_ECB_PKCS5);
cipher.init(Cipher.DECRYPT_MODE, getSecretKey(secretKey, AES));
byte[] result = cipher.doFinal(data);
return new String(result, StandardCharsets.UTF_8);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* Encrypting a file using AES.
* 简体中文:对文件进行AES加密。
*
* @param srcFile Source encrypted file
* @param dir Storage path of the encrypted file
* @param dstName Encrypted file name
* @param secretKey Key
* @param transformation In the field of encryption, it usually refers to the combination
* of encryption algorithms, modes, and padding.
* @return Encrypted file
*/
public static File encryptFileAES(File srcFile, String dir, String dstName, String secretKey, String transformation) {
try {
File encryptFile = new File(dir, dstName);
FileOutputStream outputStream = new FileOutputStream(encryptFile);
Cipher cipher = initFileAESCipher(secretKey, transformation, Cipher.ENCRYPT_MODE);
CipherInputStream cipherInputStream = new CipherInputStream(
new FileInputStream(srcFile), cipher);
byte[] buffer = new byte[1024 * 2];
int len;
while ((len = cipherInputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, len);
outputStream.flush();
}
cipherInputStream.close();
IoUtils.close(outputStream);
return encryptFile;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* Encrypting a file using AES, with the default mode.
* 简体中文:AES加密文件,默认方式。
*
* @param srcFile Source encrypted file
* @param dir Storage path of the encrypted file
* @param dstName Encrypted file name
* @param secretKey Key
*/
public static File encryptFileAES(File srcFile, String dir, String dstName, String secretKey) {
return encryptFileAES(srcFile, dir, dstName, secretKey, AES_CFB_PKCS5);
}
/**
* Encrypting a file using AES.
* 简体中文:AES解密文件。
*
* @param srcFile Source encrypted file
* @param dir Storage path of the decrypted file
* @param dstName Decrypted file name
* @param secretKey Key
* @param transformation In the field of encryption, it usually refers to the combination
* of encryption algorithms, modes, and padding.
*/
public static File decryptFileAES(File srcFile, String dir, String dstName, String secretKey, String transformation) {
try {
File decryptFile = new File(dir, dstName);
Cipher cipher = initFileAESCipher(secretKey, transformation, Cipher.DECRYPT_MODE);
FileInputStream inputStream = new FileInputStream(srcFile);
CipherOutputStream cipherOutputStream = new CipherOutputStream(
new FileOutputStream(decryptFile), cipher);
byte[] buffer = new byte[1024 * 2];
int len;
while ((len = inputStream.read(buffer)) >= 0) {
cipherOutputStream.write(buffer, 0, len);
cipherOutputStream.flush();
}
IoUtils.close(cipherOutputStream, inputStream);
return decryptFile;
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
/**
* Decrypting a file using AES, with the default mode.
* 简体中文:AES解密文件,默认方式。
*
* @param srcFile Source encrypted file
* @param dir Storage path of the decrypted file
* @param dstName Decrypted file name
* @param secretKey Key
*/
public static File decryptFileAES(File srcFile, String dir, String dstName, String secretKey) {
return decryptFileAES(srcFile, dir, dstName, secretKey, AES_CFB_PKCS5);
}
/**
* Initialize AES Cipher.
* 简体中文:初始化AES Cipher。
*
* @param secretKey Key
* @param transformation In the field of encryption, it usually refers to the combination
* of encryption algorithms, modes, and padding.
* @param cipherMode Encryption mode
* @return Cryptographic Algorithm
*/
private static Cipher initFileAESCipher(String secretKey, String transformation, int cipherMode) {
try {
SecretKeySpec secretKeySpec = getSecretKey(secretKey, AES);
Cipher cipher = Cipher.getInstance(transformation);
cipher.init(cipherMode, secretKeySpec, new IvParameterSpec(new byte[cipher.getBlockSize()]));
return cipher;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
Java的AES加解密是JDK的内置模块,以上是我Java代码的封装,支持加密文本和文件,工具类在https://github.com/dora4/dora 也可以找到。
iOS
#import <CommonCrypto/CommonCryptor.h>
#import <Foundation/Foundation.h>
@interface AESCrypto : NSObject
+ (NSData *)AES256EncryptWithKey:(NSString *)key data:(NSData *)data;
+ (NSData *)AES256DecryptWithKey:(NSString *)key data:(NSData *)data;
@end
@implementation AESCrypto
+ (NSData *)AES256EncryptWithKey:(NSString *)key data:(NSData *)data {
return [self AES256Operation:kCCEncrypt key:key data:data];
}
+ (NSData *)AES256DecryptWithKey:(NSString *)key data:(NSData *)data {
return [self AES256Operation:kCCDecrypt key:key data:data];
}
+ (NSData *)AES256Operation:(CCOperation)operation key:(NSString *)key data:(NSData *)data {
char keyPtr[kCCKeySizeAES256+1]; // 密钥长度(256位 = 32字节)
bzero(keyPtr, sizeof(keyPtr)); // 初始化keyPtr
[key getCString:keyPtr maxLength:sizeof(keyPtr) encoding:NSUTF8StringEncoding]; // 将密钥转换为C字符串
size_t dataOutAvailable = data.length + kCCBlockSizeAES128; // 输出缓冲区大小
void *dataOut = malloc(dataOutAvailable); // 分配内存
size_t dataOutMoved = 0; // 实际加密数据的大小
// 执行AES加密/解密
CCCryptorStatus status = CCCrypt(operation, // 加密或解密操作
kCCAlgorithmAES, // 加密算法
kCCOptionPKCS7Padding, // 填充方式
keyPtr, // 密钥
kCCKeySizeAES256, // 密钥长度
NULL, // 初始向量(此处为nil)
data.bytes, // 输入数据
data.length, // 输入数据长度
dataOut, // 输出缓冲区
dataOutAvailable, // 输出缓冲区大小
&dataOutMoved); // 输出大小
if (status == kCCSuccess) {
// 成功,返回加密后的数据
return [NSData dataWithBytesNoCopy:dataOut length:dataOutMoved];
}
// 失败,释放内存并返回nil
free(dataOut);
return nil;
}
@end
iOS一般使用CommonCrypto进行AES的加解密。我们同样也定义成工具类,以AES256为例,定义两个类方法AES256EncryptWithKey和AES256DecryptWithKey。底层最终调用CCCrypt来实现加解密。
前端
import CryptoJS from "crypto-js";
static encryptAES(plainText) {
const key = "12345678abcdefgh";
const iv = '0000000000000000';
const encodedContent = CryptoJS.enc.Utf8.parse(plainText);
const keyUtf8 = CryptoJS.enc.Utf8.parse(key);
const ivUtf8 = CryptoJS.enc.Utf8.parse(iv);
const encrypted = CryptoJS.AES.encrypt(encodedContent, keyUtf8, {
iv: ivUtf8,
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
});
const cipherText = encrypted.toString();
console.log(cipherText);
return cipherText;
}
static decryptAES(cipherText) {
const key = "12345678abcdefgh";
const iv = '0000000000000000';
const keyUtf8 = CryptoJS.enc.Utf8.parse(key);
const ivUtf8 = CryptoJS.enc.Utf8.parse(iv);
const decrypted = CryptoJS.AES.decrypt(cipherText, keyUtf8, {
iv: ivUtf8, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7
});
const decryptedText = decrypted.toString(CryptoJS.enc.Utf8);
console.log(decryptedText);
return decryptedText;
}
前端通常使用CryptoJS库进行AES的加解密。人家都给你封装好了,直接调用即可。
注意事项
由于各个版本库的加密实现方式可能会不一样,所以加密出来的结果一般不会一样,但是解密出来都是相同的内容。如果你要加密出来也要一样,那我建议你用底层语言C/C++去写,然后打成动态链接库,供上层语言调用。或者使用诸如Flutter等跨平台/混合开发技术。另外推荐一个网页版的加解密工具 http://tool.chacuo.net/cryptaes ,支持的加解密方式贼多。
标签:AES,加密,String,DD,平台,param,data From: https://blog.csdn.net/a_lwh____/article/details/142864172