Base64
base64是一种基于64个可打印字符来表示二进制数据的表示方法.严格来说它只能算作一种编码方式.
Base64编码之所以称为Base64,是因为其使用64个字符来对任意数据进行编码,同理有Base32、Base16编码
作用:
1, 由于某些系统中只能使用ASCII字符。Base64就是用来将非ASCII字符的数据转换成ASCII字符的一种方法.
2, 使用SMTP协议 (Simple Mail Transfer Protocol 简单邮件传输协议)来发送邮件。因为这个协议是基于文本的协议,所以如果邮件中包含一幅图片,我们知道图片的存储格式是二进制数据(binary data),而非文本格式,我们必须将二进制的数据编码成文本格式,这时候Base 64 Encoding就派上用场了.
3, 通过base64将ASCII不可见字符转换为可见字符
使用:
//编码
- (NSString *)base64EncodedString;{
NSData *data = [self dataUsingEncoding:NSUTF8StringEncoding];
return [data base64EncodedStringWithOptions:0];
}
//解码
- (NSString *)base64DecodedString{
NSData *data = [[NSData alloc]initWithBase64EncodedString:self options:0];
return [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding];
}
MD5
MD5(消息摘要算法第五版)为计算机安全领域广泛使用的一种散列函数,用以提供消息的完整性保护.
特点:
1、压缩性:任意长度的数据,算出的MD5值长度都是固定的(32摘要)。
2、容易计算:从原数据计算出MD5值很容易。
3、抗修改性:对原数据进行任何改动,哪怕只修改1个字节,所得到的MD5值都有很大区别。
4、强抗碰撞(难逆向):已知原数据和其MD5值,想找到一个具有相同MD5值的数据(即伪造数据)是非常困难的。
作用
1, 数字签名, 当我们传递敏感信息时,可以为利用MD5+时间戳+盐 为消息添加唯一的数字签名,当服务端获得数据后,用相同算法再次签名.进行比较 若不一致 则数据遭到篡改.
2, 文件验证, 我们在下载文件时,由于复杂的网络环境,我们下载的文件可能会有内容丢失或篡改的可能性.(例如我们从服务器获取的H5文件遭到了JS注入),利用MD5可以有效防止这些事情的发生.
同类算法
SHA-1:
会产生一个160位的消息摘要,SHA-1的安全性在2000年以后已经不被大多数的加密场景所接受。2017年荷兰密码学研究小组CWI和Google正式宣布攻破了SHA-1.
SHA-2:
2001年发布,包括SHA-224、SHA-256、SHA-384、SHA-512、SHA-512/224、SHA-512/256。虽然至今尚未出现对SHA-2有效的攻击,它的算法跟SHA-1基本上仍然相似;因此有些人开始发展其他替代的散列算法。
SHA-3:
2015年正式发布,SHA-3并不是要取代SHA-2,因为SHA-2目前并没有出现明显的弱点。由于对MD5出现成功的破解,以及对SHA-0和SHA-1出现理论上破解的方法,NIST感觉需要一个与之前算法不同的,可替换的加密散列算法,也就是现在的SHA-3。
实现
这里是对字符串的散列计算,若对文件则需要先读取文件流再去散列.
需要: import
MD5
- (NSString *)md5String {
const char *str = self.UTF8String;
uint8_t buffer[CC_MD5_DIGEST_LENGTH];
CC_MD5(str, (CC_LONG)strlen(str), buffer);
return [self stringFromBytes:buffer length:CC_MD5_DIGEST_LENGTH];
}
SHA-1:
- (NSString *)sha1String {
const char *str = self.UTF8String;
uint8_t buffer[CC_SHA1_DIGEST_LENGTH];
CC_SHA1(str, (CC_LONG)strlen(str), buffer);
return [self stringFromBytes:buffer length:CC_SHA1_DIGEST_LENGTH];
}
SHA256:
- (NSString *)sha256String {
const char *str = self.UTF8String;
uint8_t buffer[CC_SHA256_DIGEST_LENGTH];
CC_SHA256(str, (CC_LONG)strlen(str), buffer);
return [self stringFromBytes:buffer length:CC_SHA256_DIGEST_LENGTH];
}
SHA512:
- (NSString *)sha512String {
const char *str = self.UTF8String;
uint8_t buffer[CC_SHA512_DIGEST_LENGTH];
CC_SHA512(str, (CC_LONG)strlen(str), buffer);
return [self stringFromBytes:buffer length:CC_SHA512_DIGEST_LENGTH];
}
SHA3:
需要在github上下载keccak代码包 :https://github.com/gvanas/KeccakCodePackage
大文件的计算:
这里以MD5为例:
#define FileHashDefaultChunkSizeForReadingData 4096
- (NSString *)fileMD5Hash {
//打开一个文件准备读取
NSFileHandle *fp = [NSFileHandle fileHandleForReadingAtPath:self];
if (fp == nil) {
//若路径为文件夹这种的(如:.framework)则会返回Null
return nil;
}
//创建MD5变量
CC_MD5_CTX hashCtx;
//初始化MD5变量
CC_MD5_Init(&hashCtx);
while (YES) {
@autoreleasepool {
//读取文件指定长度数据,循环读取避免一次加载到内存过大
NSData *data = [fp readDataOfLength:FileHashDefaultChunkSizeForReadingData];
//准备MD5加密,将内容上传
CC_MD5_Update(&hashCtx, data.bytes, (CC_LONG)data.length);
if (data.length == 0) {
break;
}
}
}
//关闭文件
[fp closeFile];
//创建MD5结果缓冲区
uint8_t buffer[CC_MD5_DIGEST_LENGTH];
//将MD5结果写进缓冲区
CC_MD5_Final(buffer, &hashCtx);
//原始数据转换为字符串
return [self stringFromBytes:buffer length:CC_MD5_DIGEST_LENGTH];
}
- (NSString *)stringFromBytes:(uint8_t *)bytes length:(int)length {
NSMutableString *strM = [NSMutableString string];
for (int i = 0; i < length; i++) {
[strM appendFormat:@"%02x", bytes[i]];
}
return [strM copy];
}
对于其他的算法文件加密方式也是这样的,它们都是由CommonCrypto库提供的.
HMAC散列计算(加盐)
HMAC是密钥相关的哈希运算消息认证码,HMAC运算利用哈希算法,以一个密钥和一个消息为输入,生成一个消息摘要作为输出。
HMAC算法更象是一种加密算法,它引入了密钥,其安全性已经不完全依赖于所使用的HASH算法,有些类似对称加密,但是是不可逆的那种~.
以MD5为例:
- (NSString *)hmacMD5StringWithKey:(NSString *)key {
const char *keyData = key.UTF8String;
const char *strData = self.UTF8String;
//切换其他散列函数替换这里(如:CC_SHA256_DIGEST_LENGTH)
uint8_t buffer[CC_MD5_DIGEST_LENGTH];
//切换其他散列函数替换这里(如:kCCHmacAlgSHA256)
CCHmac(kCCHmacAlgMD5, keyData, strlen(keyData), strData, strlen(strData), buffer);
//切换其他散列函数替换这里(如:CC_SHA256_DIGEST_LENGTH)
return [self stringFromBytes:buffer length:CC_MD5_DIGEST_LENGTH];
}
AES(对称加密)
简介
美国国家标准技术研究所在2001年发布了高级加密标准(AES)。
AES是基于数据块的加密方式,
即,每次处理的数据是一块(16字节),当数据不是16字节的倍数时填充,
这就是所谓的分组密码(区别于基于比特位的流密码),16字节是分组长度。
AES在软件及硬件上都能快速地加解密,相对来说较易于实现,
实现
在使用AES时要配置几个加密参数,只有都一致才能使 客户端与服务端 结果一致.
参数配置
密钥长度
key常见的长度有三种:128、192和256 bits
加密模式
AES属于块加密(Block Cipher),块加密中有CBC、ECB、CTR、OFB、CFB等几种工作模式.
ECB
是一种基础的加密方式,AES默认没收,密文被分割成分组长度相等的块(不足补齐),然后单独一个个加密,一个个输出组成密文.
CBC
这个模式是链式的,后一块需要前一块做基础,第一块需要一个需要初始化向量IV做基础.
相同的输入产生不同的输出.
能看到的数据是“明文+IV”或“明文+前一个密文”的乱码,所以能隐藏明文.
所以加密/解密 需要: 明文/密文 + 秘钥 + 初始向量参数
填充方式
因为AES的算法是把明文分组再处理的,他要求每个分组(16字节)是“满”的,即明文长度必须被16字节整除.
所以明文最后不足的16字节的要先进行数据填充,把不足16字节的最后一组补成16字节.
CFB,OFB和CTR模式由于与key进行加密操作的是上一块加密后的密文,因此不需要对最后一段明文进行填充.
在iOS SDK中提供了PKCS7Padding.
初始向量
正如在CBC模式哪里介绍的,开始加密时,从哪里开始就是初始向量,如不设置则系统默认为0;
代码
NSString *const kInitVector = @"初始向量";
size_t const kKeySize = kCCKeySizeAES256;//秘钥长度
+ (NSData *)encryptAES:(NSData *)content key:(NSString *)key {
NSData *contentData = content;
NSUInteger dataLength = contentData.length;
//设置加密秘钥,因C字符串结束符为'\0' 所以大小+1
char keyPtr[kKeySize + 1];
memset(keyPtr, 0, sizeof(keyPtr));
//应确保大小小于等于16个字节.
[key getCString:keyPtr maxLength:sizeof(keyPtr) encoding:NSUTF8StringEncoding];
//密文长度 = 明文长度+秘钥长度
size_t encryptSize = dataLength + kCCBlockSizeAES128;
void *encryptedBytes = malloc(encryptSize);
//密文接受指针
size_t actualOutSize = 0;
//初始向量
NSData *initVector = [kInitVector dataUsingEncoding:NSUTF8StringEncoding];
CCCryptorStatus cryptStatus = CCCrypt(kCCEncrypt, //是加密还是解密
kCCAlgorithmAES, //加密/解密方式
kCCOptionPKCS7Padding, //PKCS7Padding
keyPtr, //秘钥
kKeySize, //秘钥大小
initVector.bytes, //初始向量
contentData.bytes, //明文/密文
dataLength, //明文/密文大小
encryptedBytes, //结果: 密文/明文缓冲区
encryptSize, //结果: 密文/明文大小
&actualOutSize); //结果指针
if (cryptStatus == kCCSuccess) {
// 成功
return [NSData dataWithBytesNoCopy:encryptedBytes length:actualOutSize];
}
//释放
free(encryptedBytes);
return nil;
}
RSA
原理
数学基础
任意给定正整数n,请问在小于等于n的正整数之中,有多少个与n构成互质的数.计算这个值的函数为 欧拉函数: 如: φ(8) = 4.
若n为质数 则: φ(n) = n-1 原理3
若n为质数的n次方 则: φ(p^k) = p^k - p^(k-1)
若n为两个互质整数之积 则: φ(p1 * p2) = φ(p1)φ(p2) 原理1
若正整数 a和n互质 则: a^φ(n) 被n除的余数为1 则: a^φ(n) %n=1 欧拉定理
若正整数a和质数p互质 则: a^p-1 %p=1 //费马小定理
若正整数a和n互质,则一定可以找到正整数b 使: ab%n=1, b为a的模反元素. 原理2
秘钥生成
1, 随机生成两个不等质数p和q 如 p=61 q=53
2, 求出pq乘积n n=p * q= 61*53=3233.
这里n的长度即为,秘钥长度,如3233为二进制12位,RSA秘钥一般为1024位.
3, 计算φ(n)
根据 原理1 φ(n) = φ(p)φ(q) = (p-1)(q-1) = 3120
4, 随机选择一个整数e 条件为 1 假如选择 17 5, 获取e对于φ(n)的模反元素d ed%φ(n)=1 ---> ed -1 = kφ(n) ---> 17d + 3120k = 1 6, 这里 n和e 为公钥 n和d 为私钥 对明文信息m 加密 注意:m为正整数,且m须小于n 解密规则为: c^d %n = m 因为加密过程为: m^e % n = c ---> c = m^e - kn (m^e - kn)^d % n = m ---> m^ed % n = m 由于在制作公私钥 的第5步 所以: ed%φ(n)=1 ---> ed = hφ(n)+1 将ed代入须证明公式: m^ed % n = m ---> m^hφ(n)+1 % n = m m^hφ(n)+1 % n = m ---> ((mφ(n))h * m) % n = m 由于欧拉定理 m^φ(n) %n=1可得 因为制作公私钥 的第1步 n = p * q 因为加密方法 m^e % n = c , 且因 m 由于 n = pq 且 pq互质 所以 n有且只有 p q 两个因子. 然而 m n有公因子 所以 m n 的公因子 必定为 q或p的整数倍. 所以 m = kp 或 kq 以 m=kp为例 因为上面描述的关系 m 由欧拉定理得: 因为 k与q互质 p与q互质 --> 进一步可以确定该式成立: 由原理1和原理3 由于 ed%φ(n)=1,且m=kp 所以将h匹配为合适的值得 两侧同除m得: 由于加密方式 若想破解RSA 则需要在已知 n e的情况下 求 d 因为 因为 ed%φ(n)=1 所以需知道 φ(n) 而因式分解是十分困难的 特别是对于 特大整数的因式分解. 由于 名文m 需小于 秘钥长度n 所以常用来加密 对称加密的秘钥. 参考链接:iOS中使用RSA加密 .p12 格式文件是用来加密的 私钥 添加动态库 Security.framework 具体实现太长,我汇总了一个类 github链接
通过扩展欧几里德算法 可得到一组整数解 d=2753 k=-15加密(此处为公钥加密 n e)
m^e % n = c
这里的c就是加密后得到的密文
65^17 % 3233 = 2790 这里2790就是加密后的密文解密(n d)
解密原理
若想证明解密规则成立 则等同于证明 (m^e - kn)^d % n = m 成立
若m n 互质
((mφ(n))h * m) % n = m ---> (1^h * m) % n = m 则解密公式成立若m n 不互质
m^φ(q) % q = 1
---> 由于q为质数 ---> m^q-1 % q = 1
--> (kp)^q-1 % q = 1
((kp)^q-1 * kp)%q = kp
(((kp)^h(p-1)(q-1)) * kp)%q = kp
因为p为质数 h为任一整数(((kp)^h(p-1)(q-1)) * kp)%q = kp
---> (kp)^hφ(n)+1 %q=kp(kp)^ed %q = kp
---> (m)^ed = tq +m
t为整数.(m)^ed-1 = tq/m +1
由于ed为整数,m为整数 故tq/m为整数. 因q与m互质 所以t为m的整数倍 --> t = yp ---> m^ed = yn+m
---> m^ed % n = m
m^e % n = c
---> c^d % n = m
安全性讨论
因为 φ(n) = (p-1)(q-1) 所以需求得 qp
因为 qp=n 所以得将n因式分解iOS实现
在iOS中使用RSA加密解密 需要使用到.der 和 .p12 后缀格式文件.
.der 格式文件用来解密的 公钥
感兴趣可以下下来看看 如果帮到你 点个Star鼓励一蛤~
本文地址
博客地址