- 参考文章:https://blog.csdn.net/lifeng_2009/article/details/5338687?spm=a2c6h.13066369.0.0.506b2945OZpllp
目录
简介
目前主流的加密算法分为两种:对称秘钥加密和非对称秘钥加密。
- 对称秘钥加密主要包括:DES、 3DES、 AES
- 非对称秘钥加密又称公开秘钥加密主要包括:RSA
该篇主要讲解对称秘钥加密,非对称秘钥加密在下篇讲解
对称秘钥加密的特点是:
加密/解密使用相同的秘钥。
是可逆的。
DES
IBM公司研制1972年,美国国家标准局NBS (National Bureau of Standards)开始实施计算机数据保护标准的开发计划。1973年5月13日,NBS征集在传输和存贮数据中保护计算机数据的密码算法。
1975年3月17日,首次公布DES算法描述。
1977年1月15日,正式批准为加密标准(FIPS-46),当年7 月1日正式生效。
1994年1月的评估后决定1998年12月以后不再将DES作为数据加密标准。
DES算法结构如下图:
DES是16轮的Feistel结构密码
DES是一个包含16个阶段的“替代–置换”的分组加密算法。
DES的分组长度是64位的分组明文序列作为加密算法的输入,经过16轮加密得到64位的密文序列
DES使用56位的密钥,每一轮使用48位的子密钥,每个子密钥是56位密钥的子集构成
一、IP置换
把输入的64位数据的排列顺序打乱,每位数据按照下面规则重新组合
二、一轮DES加密过程
该过程主要包括两部分:
子秘钥的生成
f函数的操作部分:扩展变换、与子秘钥的异或运算、S盒替换、P盒置换
-
扩展变换:扩展变换(Expansion Permutation,也被称 为E-盒)将64位输入序列的右半部分从32位扩展到48位。确保最终的密文与所有的明文位都有关。
-
S-盒替代(S-boxes Substitution)
-
P-盒置换(P-boxes Permutation)
三、逆初始置换
初始置换和对应的逆初始置换操作并不会增强DES算法的安全性。主要目的是为了更容易地将明文和密文数据以字节大小放入DES芯片中
DES算法的安全性
DES的安全性:
- DES的56位密钥可能太小,1998年7月,EFE(电子前哨基金会)宣布攻破了DES算法,他们使用的是不到25万美元的特殊的“DES破译机”,这种攻击只需要不到3天的时间。
- DES的迭代次数可能太少,16次恰巧能抵抗差分分析。
- S盒(即替代函数S)中可能有不安全因素。
- DES的一些关键部分不应当保密。
- DES存在弱密钥和半弱密钥
针对DES的攻击方法:
- 差分分析方法(Difference Analysis Method)
- 线性分析方法(Linear Analysis Method)
- 旁路攻击法(Side-Channel Attack)
关于DES算法56位秘钥
我们在实际使用中秘钥的长度往往是64位,为什么这里说是56位呢,因为64位秘钥中的 8 16 24 32 40 48 56 64这8位是校验位,不参与实际的加密运算,所以实际使用的是56位秘钥。那么这里就要注意一个问题,当我们使用 8个0x30 做为秘钥时与使用8个0x31做为秘钥时,虽然秘钥不同,但加密结果一致。因为这两个秘钥除了校验位不同外其余56位相同。
echo -n 520it123 | openssl enc -des-ecb -K 3030303030303030 -nosalt | base64
IaWvZKpZ0GWtaoi0+jeDPQ==
echo -n 520it123 | openssl enc -des-ecb -K 3131313131313131 -nosalt | base64
IaWvZKpZ0GWtaoi0+jeDPQ==
DES算法的ECB模式和CBC模式
- ECB模式(Electronic Codebook,电码本)
echo -n 520it123abcdefgh | openssl enc -des-ecb -K 3131313131313131 -nosalt | base64
IaWvZKpZ0GV4c+2odsoP6q1qiLT6N4M9
echo -n 520it123bbcdefgh | openssl enc -des-ecb -K 3131313131313131 -nosalt | base64
IaWvZKpZ0GVMlsPGsiDhma1qiLT6N4M9
对比一下只有中间部分不一致:
IaWvZKpZ0GV 4c+2odsoP6q 1qiLT6N4M9
IaWvZKpZ0GV MlsPGsiDhma 1qiLT6N4M9
- CBC模式 (Cipher Block Chaining 密文分组链接模式)
echo -n 520it123abcdefgh | openssl enc -des-cbc -iv 0102030405060708 -K 3131313131313131 -nosalt | base64
nfGGM1uAJDJtt9A+kfKbEVjgJiJztSL/
echo -n 520it123bbcdefgh | openssl enc -des-cbc -iv 0102030405060708 -K 3131313131313131 -nosalt | base64
nfGGM1uAJDKSvPgJXXCRNgjet4iD1wlQ
对比一下 从某一位置起后边都不一致:
nfGGM1uAJD Jtt9A+kfKbEVjgJiJztSL/
nfGGM1uAJD KSvPgJXXCRNgjet4iD1wlQ
3DES
DES密钥过短(56bits)→多重DES
3DES使用3个密钥,执行3次DES算法,加密过程:
加密-解密-加密(EDE),即: C=EK3(DK2(EK1(M)))
为了避免3DES使用3个密钥进行三阶段加密带来的密钥过长的缺点(168bit),Tuchman提出使用两个密钥的三重加密方法,这个方法只要求112bit密钥,即令其K1=K3:C=EK1(DK2(EK1(M)))
3DES的第二阶段的解密并没有密码编码学上的意义,唯一优点是可以使用3DES解密原来的单次DES加密的数据,即K1=K2=K3 C=EK1(DK1(EK1(M)))=EK1(M)
AES
AES: Advanced Encryption Standard
NIST(美国国家标准技术研究所)对称密钥加密标准,取代DES(2001年12月)1997年NIST宣布征集AES算法,要求:
- 可公开加密方法
- 分组加密,分组长度为128位
- 至少像3DES一样安全
- 更加高效、快
- 可提供128/192/256位密钥
比利时学者Joan Daemen和Vincent Rijmen提出的 Rijndael加密算法最终被选为AES算法。
NIST在2001年12月正式颁布了基于Rijndael算法AES标准
不属于Feistel结构
- 加密、解密相似但不完全对称
- 支持128/192/256密钥长度
- 有较好的数学理论作为基础
- 结构简单、速度快
Rijndael算法特点:
- 分组长度和密钥长度均可变(128/192/256bits)
- 循环次数允许在一定范围内根据安全要求进行修正
- 汇聚了安全、效率、易用、灵活等优点
- 抗线性攻击和抗差分攻击的能力大大增强
- 如果1秒暴力破解DES,则需要149万亿年破解AES
需要注意的是:
AES的块大小始终是128位(16字节),这是AES标准的一部分。虽然AES支持不同长度的密钥(128位、192位和256位),但块大小始终是128位。
填充模式
因为DES与AES都是块加密,当最后一组无法对齐时就需要进行填充
填充方式有
ZeroPadding
PKCS7Padding PKCS5Padding
一. ZeroPadding
最后一组没有对齐时填充:0
最后一组对齐时,不填充
使用ZeroPadding填充时,没办法区分真实数据与填充数据,所以只适合以\0结尾的字符串加解密
二. PKCS7Padding
PKCS7Padding,假设数据长度需要填充n(n>0)个字节才对齐,那么填充n个字节,每个字节都是n;如果数据本身就已经对齐了,则填充一块长度为块大小的数据,每个字节都是块大小。
比如我们采用DES进行加密,DES块大小为64bit,8字节,
假如最后一组为abcde,填充后为:abcde333
假如最后组已经对齐,那么填充应该为:88888888
三. PKCS5Padding
PKCS5Padding,PKCS7Padding的子集,块大小固定为8字节。
也就是PKCS5Padding只能用于块大小是64位的加密方式,比如DES或3DES.并不能应用于块大小为128位的AES.但PKCS7Padding就可以
由于使用PKCS7Padding/PKCS5Padding填充时,最后一个字节肯定为填充数据的长度,所以在解密后可以准确删除填充的数据
代码实现
DES加密解密实现
/**
* DES加密NSData 默认使用 kCCOptionPKCS7Padding
* @param data 需加密数据
* @param key 密钥
* @param iv 有iv为CBC模式 nil为ECB模式
* @return 加密后的NSData
*/
+(NSData *)encryptWithData:(NSData *)data key:(NSString *)key iv:(NSData*)iv{
if (!data || !key) { return nil;}
//设置模式
/**
0 没有Padding的 CBC 的加密
kCCOptionECBMode 没有Padding的 ECB 的加密
kCCOptionPKCS7Padding 有Padding的 CBC 的加密
kCCOptionPKCS7Padding | kCCOptionECBMode 有Padding的 ECB 的加密
*/
//要加密的数据
const void * dataBytes = [data bytes];
size_t dataLength = [data length];
// 设置秘钥
NSData *keyData = [key dataUsingEncoding:NSUTF8StringEncoding];
uint8_t cKey[kCCKeySizeDES];
bzero(cKey, sizeof(cKey));
[keyData getBytes:cKey length:sizeof(cKey)];
int cKeyLength = sizeof(cKey);
// 设置iv
uint8_t cIv[kCCBlockSizeDES];
bzero(cIv, sizeof(cIv));
int option = 0;
if (iv) {
[iv getBytes:cIv length:sizeof(cIv)];
option = kCCOptionPKCS7Padding;
} else {
option = kCCOptionPKCS7Padding | kCCOptionECBMode;
}
// 设置输出缓冲区
size_t bufferSize = dataLength + kCCBlockSizeDES;
void *buffer = malloc(bufferSize);
// 开始加密
size_t encryptedSize = 0;
/*
CCCrypt 对称加密算法的核心函数(加密/解密)
第一个参数:kCCEncrypt 加密/ kCCDecrypt 解密
第二个参数:加密算法,默认使用的是 AES/DES
第三个参数:加密选项 ECB/CBC
kCCOptionPKCS7Padding CBC 的加密
kCCOptionPKCS7Padding | kCCOptionECBMode ECB 的加密
第四个参数:加密密钥
第五个参数:密钥的长度
第六个参数:初始向量
第七个参数:加密的数据
第八个参数:加密的数据长度
第九个参数:密文的内存地址
第十个参数:密文缓冲区的大小
第十一个参数:加密结果的大小
*/
CCCryptorStatus cryptStatus = CCCrypt(kCCEncrypt,
kCCAlgorithmDES,
option,
cKey,
cKeyLength,
cIv,
dataBytes,
dataLength,
buffer,
bufferSize,
&encryptedSize);
NSData *result = nil;
if (cryptStatus == kCCSuccess) {
result = [NSData dataWithBytesNoCopy:buffer length:encryptedSize];
} else {
free(buffer);
NSLog(@"[错误] 加密失败|状态编码: %d", cryptStatus);
}
return result;
}
/**
* DES解密NSData 默认使用 kCCOptionPKCS7Padding
* @param data 需解密数据
* @param key 密钥
* @param iv 有iv为CBC模式 nil为ECB模式
* @return 解密后的NSData
*/
+ (NSData *)decryptWithData:(NSData *)data key:(NSString *)key iv:(NSData*)iv{
if (!data || !key) { return nil;}
//要加密的数据
const void * dataBytes = [data bytes];
size_t dataLength = [data length];
// 设置秘钥
NSData *keyData = [key dataUsingEncoding:NSUTF8StringEncoding];
uint8_t cKey[kCCKeySizeDES];
bzero(cKey, sizeof(cKey));
[keyData getBytes:cKey length:sizeof(cKey)];
int cKeyLength = sizeof(cKey);
// 设置iv
uint8_t cIv[kCCBlockSizeDES];
bzero(cIv, sizeof(cIv));
int option = 0;
if (iv) {
[iv getBytes:cIv length:sizeof(cIv)];
option = kCCOptionPKCS7Padding;
} else {
option = kCCOptionPKCS7Padding | kCCOptionECBMode;
}
// 设置输出缓冲区
size_t bufferSize = dataLength + kCCBlockSizeDES;
void *buffer = malloc(bufferSize);
// 开始解密
size_t decryptedSize = 0;
CCCryptorStatus cryptStatus = CCCrypt(kCCDecrypt,
kCCAlgorithmDES,
option,
cKey,
cKeyLength,
cIv,
dataBytes,
dataLength,
buffer,
bufferSize,
&decryptedSize);
NSData *result = nil;
if (cryptStatus == kCCSuccess) {
result = [NSData dataWithBytesNoCopy:buffer length:decryptedSize];
} else {
free(buffer);
NSLog(@"[错误] 解密失败|状态编码: %d", cryptStatus);
}
return result;
}
/**
没有padding ECB 模式 kCCOptionECBMode nil
key : 12345678
plaintext : abcdefgh12345678abcdefgh //24字节
ciphertext: lNRDa8O1tpOW0AKIeNWMiZTUQ2vDtbaT //32字节
key : 12345678
plaintext : abcdefgh22345678abcdefgh //24字节
ciphertext: lNRDa8O1tpNnEnvqkI6CyZTUQ2vDtbaT //32字节
对比:
lNRDa8O1tp OW0AKIeNWMi ZTUQ2vDtbaT
lNRDa8O1tp NnEnvqkI6Cy ZTUQ2vDtbaT
原文长度为24个字节,加密后密文长度也应为24个字节,24个字节转为base64所以长度变为32.
*/
/**
有padding ECB 模式 kCCOptionPKCS7Padding | kCCOptionECBMode nil
key : 12345678
plaintext : abcdefgh12345678abcdefgh //24字节
ciphertext: lNRDa8O1tpOW0AKIeNWMiZTUQ2vDtbaT/rlZt9RkL8s= //44字节
key : 12345678
plaintext : abcdefgh22345678abcdefgh //24字节
ciphertext:lNRDa8O1tpNnEnvqkI6CyZTUQ2vDtbaT/rlZt9RkL8s= //44字节
对比:与没有padding的对比 多出了/rlZt9RkL8s=
lNRDa8O1tp OW0AKIeNWMi ZTUQ2vDtbaT /rlZt9RkL8s=
lNRDa8O1tp NnEnvqkI6Cy ZTUQ2vDtbaT /rlZt9RkL8s=
lNRDa8O1tp NnEnvqkI6Cy ZTUQ2vDtbaT /rlZt9RkL8s=
原文长度为24个字节,加上padding为7个字节,共计31个字节,加密后密文长度也应为31个字节,31个字节转为base64所以长度变为44字节.
*/
/**
没有padding CBC 模式 0 nil
key : 12345678
plaintext : abcdefgh12345678abcdefgh //24字节
ciphertext: lNRDa8O1tpPgovcvKLH6LuuSPI02Ej5z //32字节
key : 12345678
plaintext : abcdefgh22345678abcdefgh //24字节
ciphertext: lNRDa8O1tpNxVn7tzKLgZ6YoQTS8UwnO //32字节
对比:
lNRDa8O1tp PgovcvKLH6LuuSPI02Ej5z
lNRDa8O1tp NxVn7tzKLgZ6YoQTS8UwnO
*/
3DES加密解密实现
//加密
+(NSData *)encryptWithData:(NSData *)data key:(NSString*)key iv:(NSData*)iv{
if (!data || !key) { return nil;}
//要加密的数据
const void * dataBytes = [data bytes];
size_t dataLength = [data length];
// 设置秘钥
NSData *keyData = [key dataUsingEncoding:NSUTF8StringEncoding];
uint8_t cKey[kCCKeySize3DES];
bzero(cKey, sizeof(cKey));
[keyData getBytes:cKey length:sizeof(cKey)];
int cKeyLength = sizeof(cKey);
// 设置iv
uint8_t cIv[kCCBlockSize3DES];
bzero(cIv, sizeof(cIv));
int option = 0;
if (iv) {
[iv getBytes:cIv length:sizeof(cIv)];
option = kCCOptionPKCS7Padding;
} else {
option = kCCOptionPKCS7Padding | kCCOptionECBMode;
}
// 设置输出缓冲区
size_t bufferSize = dataLength + kCCBlockSize3DES;
void *buffer = malloc(bufferSize);
// 开始解密
size_t encryptedSize = 0;
//配置CCCrypt
CCCryptorStatus cryptStatus = CCCrypt(kCCEncrypt,
kCCAlgorithm3DES,
option,
cKey,
cKeyLength,
cIv,
dataBytes,
dataLength,
buffer,
bufferSize,
&encryptedSize);
NSData *result = nil;
if (cryptStatus == kCCSuccess) {
result = [NSData dataWithBytesNoCopy:buffer length:encryptedSize];
} else {
free(buffer);
NSLog(@"[错误] 加密失败|状态编码: %d", cryptStatus);
}
return result;
}
//解密
+(NSData*)decryptWithData:(NSData *)data key:(NSString*)key iv:(NSData*)iv{
if (!data || !key) { return nil;}
//要加密的数据
const void * dataBytes = [data bytes];
size_t dataLength = [data length];
// 设置秘钥
NSData *keyData = [key dataUsingEncoding:NSUTF8StringEncoding];
uint8_t cKey[kCCKeySize3DES];
bzero(cKey, sizeof(cKey));
[keyData getBytes:cKey length:sizeof(cKey)];
int cKeyLength = sizeof(cKey);
// 设置iv
uint8_t cIv[kCCBlockSize3DES];
bzero(cIv, sizeof(cIv));
int option = 0;
if (iv) {
[iv getBytes:cIv length:sizeof(cIv)];
option = kCCOptionPKCS7Padding;
} else {
option = kCCOptionPKCS7Padding | kCCOptionECBMode;
}
// 设置输出缓冲区
size_t bufferSize = dataLength + kCCBlockSize3DES;
void *buffer = malloc(bufferSize);
// 开始解密
size_t decryptedSize = 0;
CCCryptorStatus cryptStatus = CCCrypt(kCCDecrypt,
kCCAlgorithm3DES,
option,
cKey,
cKeyLength,
cIv,
dataBytes,
dataLength,
buffer,
bufferSize,
&decryptedSize);
NSData *result = nil;
if (cryptStatus == kCCSuccess) {
result = [NSData dataWithBytesNoCopy:buffer length:decryptedSize];
} else {
free(buffer);
NSLog(@"[错误] 解密失败|状态编码: %d", cryptStatus);
}
return result;
}
AES加密解密实现
//加密
+ (NSData *)encryptWithData:(NSData *)data key:(NSString *)key iv:(NSData*)iv{
if (!data || !key) { return nil;}
//要加密的数据
const void * dataBytes = [data bytes];
size_t dataLength = [data length];
// 设置秘钥
NSData *keyData = [key dataUsingEncoding:NSUTF8StringEncoding];
uint8_t cKey[kCCKeySizeAES128];
bzero(cKey, sizeof(cKey));
[keyData getBytes:cKey length:sizeof(cKey)];
int cKeyLength = sizeof(cKey);
// 设置iv
uint8_t cIv[kCCBlockSizeAES128];
bzero(cIv, sizeof(cIv));
int option = 0;
if (iv) {
[iv getBytes:cIv length:sizeof(cIv)];
option = kCCOptionPKCS7Padding;
} else {
option = kCCOptionPKCS7Padding | kCCOptionECBMode;
}
// 设置输出缓冲区
size_t bufferSize = dataLength + kCCBlockSizeAES128;
void *buffer = malloc(bufferSize);
// 开始加密
size_t encryptedSize = 0;
CCCryptorStatus cryptStatus = CCCrypt(kCCEncrypt,
kCCAlgorithmAES,
option,
cKey,
cKeyLength,
cIv,
dataBytes,
dataLength,
buffer,
bufferSize,
&encryptedSize);
NSData *result = nil;
if (cryptStatus == kCCSuccess) {
result = [NSData dataWithBytesNoCopy:buffer length:encryptedSize];
} else {
free(buffer);
NSLog(@"[错误] 加密失败|状态编码: %d", cryptStatus);
}
return result;
}
//解密
+ (NSData *)decryptWithData:(NSData *)data key:(NSString *)key iv:(NSData*)iv{
if (!data || !key) { return nil;}
//要加密的数据
const void * dataBytes = [data bytes];
size_t dataLength = [data length];
// 设置秘钥
NSData *keyData = [key dataUsingEncoding:NSUTF8StringEncoding];
uint8_t cKey[kCCKeySizeAES128];
bzero(cKey, sizeof(cKey));
[keyData getBytes:cKey length:sizeof(cKey)];
int cKeyLength = sizeof(cKey);
// 设置iv
uint8_t cIv[kCCBlockSizeAES128];
bzero(cIv, sizeof(cIv));
int option = 0;
if (iv) {
[iv getBytes:cIv length:sizeof(cIv)];
option = kCCOptionPKCS7Padding;
} else {
option = kCCOptionPKCS7Padding | kCCOptionECBMode;
}
// 设置输出缓冲区
size_t bufferSize = dataLength + kCCBlockSizeAES128;
void *buffer = malloc(bufferSize);
// 开始解密
size_t decryptedSize = 0;
CCCryptorStatus cryptStatus = CCCrypt(kCCDecrypt,
kCCAlgorithmAES128,
option,
cKey,
cKeyLength,
cIv,
dataBytes,
dataLength,
buffer,
bufferSize,
&decryptedSize);
NSData *result = nil;
if (cryptStatus == kCCSuccess) {
result = [NSData dataWithBytesNoCopy:buffer length:decryptedSize];
} else {
free(buffer);
NSLog(@"[错误] 解密失败|状态编码: %d", cryptStatus);
}
return result;
}
行者常至,为者常成!