iOS数据安全-编码和加密算法
声明:
- 最后更新时间:2019年3月13日
- 为保证时效性,持续更新地址为:iOS数据安全-编码和加密算法
Base64编码
Base64编码是一种用64个字符(其实是65个字符,“=”是填充字符)来表示任意二进制数据的方法,编码后的数据是一个字符串。
原理
- 准备一个包含64个字符的数组,其中包含的字符为:A-Z、a-z、0-9、+、/。
- 64个字符需要6位二进制来表示,表示成数值为0~63。
- 对二进制数据进行处理,每3个字节一组,一共是3x8=24bit,再划为4小组,每小组正好6个bit,然后查表,获得相应的4个字符,就是编码后的字符串。
- 如果数据的字节数不是3的倍数,需在原数据后面添加1个或2个零值字节,使其字节数是3的倍数。然后,在编码后的字符串后面添加1个或2个等号“=”,表示所添加的零值字节数。解码的时候,会自动去掉。
特点
- Base64编码是可逆的编码方式,从编码的方式即可逆推出解码的方式。
- Base64编码会把3字节的二进制数据编码为4字节的文本数据,长度增加33%。
- 标准的Base64编码后可能出现字符“+”和“/”,在URL中就不能直接作为参数。
- 可以自己定义64个字符的排列顺序,这样就可以自定义Base64编码。
- Base64编码的长度永远是4的倍数。
应用
- Base64适用于小段内容的编码,比如数字证书签名、Cookie的内容等。
- Base64也会经常用作一个简单的“加密”来保护某些数据(标准Base64编码解码无需额外信息即完全可逆),而真正的加密通常都比较繁琐。
- HTML内嵌Base64编码图片:绝大多数现代浏览器都支持一种名为 Data URLs 的特性,允许使用Base64对图片或其他文件的二进制数据进行编码,将其作为文本字符串嵌入网页中。
- 电子邮件系统:SMTP协议一开始是基于纯ASCII文本的,对于二进制文件(比如邮件附件中的图像、声音等)的处理并不好,所以后来新增MIME标准来编码二进制文件,使其能够通过SMTP协议传输。
iOS代码示例
NSString *base0 = [data base64EncodedStringWithOptions:0]; NSString *base1 = [data base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength]; //每行最大长度64,行之间添加“\r\n” NSString *base2 = [data base64EncodedStringWithOptions:NSDataBase64Encoding76CharacterLineLength]; //每行最大长度76,行之间添加“\r\n” NSString *base3 = [data base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithCarriageReturn]; NSString *base4 = [data base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed];
消息摘要算法
消息摘要算法主要分为三类,即:消息摘要(Message Digest:MD)、安全散列(Secure Hash Algorithm:SHA)和消息认证码(Message Authentication Code:MAC),主要作用是验证数据的完整性。消息摘要算法的主要特点有:①变长输入,定长输出。②单向、不可逆。③虽然“碰撞”是肯定存在的,但是很难找到“碰撞”。
MD算法
MD算法系列最新为MD5,Message-Digest Algorithm 5(信息-摘要算法5)一种被广泛使用的散列算法,可以产生出一个128位的散列值(hash value),用于确保信息传输完整一致。下面以MD5为例进行分析:
原理
- 输入信息进行填充,使其位长对512求余的结果等于448。如果输入信息的长度(bit)对512求余的结果不等于448,就需要填充使得对512求余的结果等于448。填充的方法是填充一个1和n个0。填充完后,信息的位长(Bits Length)将被扩展至N*512+448,N为一个非负整数,N可以是零。
- 记录信息长度,在第一步的结果后面附加一个以64位二进制表示的填充前信息长度(单位为Bit),如果二进制表示的填充前信息长度超过64位,则取低64位。经过这两步的处理,信息的位长=N512+448+64=(N+1)512,即长度恰好是512的整数倍。这样做的原因是为满足后面处理中对信息长度的要求。
- 初始化变量,初始的128位值为初试链接变量,这些参数用于第一轮的运算,以大端字节序来表示,他们分别为A=0x01234567,B=0x89ABCDEF,C=0xFEDCBA98,D=0x76543210。每一个变量给出的数值是高字节存于内存低地址,低字节存于内存高地址,即大端字节序。在程序中变量A、B、C、D的值分别为0x67452301,0xEFCDAB89,0x98BADCFE,0x10325476。
处理分组数据,分组数为N+1(块长:512位),每个分组进行4轮变换。
① 四个非线性函数,用于4轮变换。F(X,Y,Z)=(X&Y)|((~X)&Z) G(X,Y,Z)=(X&Z)|(Y&(~Z)) H(X,Y,Z)=X^Y^Z I(X,Y,Z)=Y^(X|(~Z))
②每组的4轮变换
/* F :一个非线性函数,一个函数运算一次 Mi :表示一个 32-bits 的输入数据 Ki:表示一个 32-bits 常数,用来完成每次不同的计算。假设Mj表示消息的第j个子分组(从0到15),常数ti是4294967296*abs( sin(i) )的整数部分,i 取值从1到64,单位是弧度。(4294967296=2^(32))*/ FF(a ,b ,c ,d ,Mj ,s ,ti) 操作为 a = b + ( (a + F(b,c,d) + Mj + ti) << s) // 第一轮用,共16次 GG(a ,b ,c ,d ,Mj ,s ,ti) 操作为 a = b + ( (a + G(b,c,d) + Mj + ti) << s) // 第二轮用,共16次 HH(a ,b ,c ,d ,Mj ,s ,ti) 操作为 a = b + ( (a + H(b,c,d) + Mj + ti) << s) // 第三轮用,共16次 II(a ,b ,c ,d ,Mj ,s ,ti) 操作为 a = b + ( (a + I(b,c,d) + Mj + ti) << s) // 第四轮用,共16次
③ 每组完成之后,将a、b、c、d分别在原来基础上再加上A、B、C、D。即a = a + A,b = b + B,c = c + C,d = d + D,然后用下一分组数据继续运行以上算法。
④ 最后的输出是a、b、c和d的级联,由四个32位分组组成,将这四个32位分组级联后将生成一个128位散列值。
特点
- 不可逆:根据输出值,不能得到原始的明文。
- 压缩性:任意长度的数据,算出的MD5值长度都是固定的。
- 抗修改性:对原数据进行任何改动,哪怕只修改1个字节,所得到的MD5值都有很大区别。
- 强抗碰撞:已知原数据和其MD5值,想找到一个具有相同MD5值的数据(即伪造数据)是非常困难的。
- 应用
目前,MD5算法因其普遍、稳定、快速的特点,仍广泛应用于普通数据的错误检查领域。但是MD5演算法无法防止碰撞(collision),因此不适用于安全性认证,如SSL公开金钥认证或是数位签章等用途。MD5算法主要运用在数字签名、文件完整性验证以及口令加密等方面(一致性校验)。 - 注意
MD5后所得到的通常是32位的编码,16位编码就是从32位MD5散列中把中间16位提取出来!其实破解16位MD5散列要比破解32位MD5散列还慢,因为他多了一个步骤,就是使用32位加密后再把中间16位提取出来, 然后再进行对比。 iOS代码示例
//相关类文件:#include <CommonCrypto/CommonDigest.h> -(NSString *)md5 { if(self == nil || [self length] == 0){ return nil; } const char *src = [self UTF8String]; unsigned char result[CC_MD5_DIGEST_LENGTH]; CC_MD5(src, (CC_LONG)strlen(src), result); return [NSString stringWithFormat:@"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", result[0], result[1], result[2], result[3], result[4], result[5], result[6], result[7], result[8], result[9], result[10], result[11], result[12], result[13], result[14], result[15] ]; }
SHA算法
安全散列算法(英语:Secure Hash Algorithm,缩写为SHA)是一个密码散列函数家族,是FIPS所认证的安全散列算法。能计算出一个数字消息所对应到的,长度固定的字符串(又称消息摘要)的算法。且若输入的消息不同,它们对应到不同字符串的机率很高。
- SHA-0:1993年发布,当时称做安全散列标准(Secure Hash Standard),发布之后很快就被NSA撤回,是SHA-1的前身。
- SHA-1:1995年发布,SHA-1在许多安全协议中广为使用,包括TLS和SSL、PGP、SSH、S/MIME和IPsec,曾被视为是MD5(更早之前被广为使用的散列函数)的后继者。但SHA-1的安全性在2000年以后已经不被大多数的加密场景所接受。2017年荷兰密码学研究小组CWI和Google正式宣布攻破了SHA-1[1]。Microsoft、Google以及Mozilla都宣布,它们旗下的浏览器将在2017年前停止接受使用SHA-1算法签名的SSL证书。2017年2月23日,CWI Amsterdam与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。
下表中的中继散列值(internal state)表示对每个数据区块压缩散列过后的中继值(internal hash sum)。
iOS代码示例
// 类似MD5,相关类文件:#include <CommonCrypto/CommonDigest.h> -(NSString *)sha1 { const char *cstr = [self cStringUsingEncoding:NSUTF8StringEncoding]; NSData *data = [NSData dataWithBytes:cstr length:self.length]; uint8_t digest[CC_SHA1_DIGEST_LENGTH]; CC_SHA1(data.bytes, (unsigned int)data.length, digest); NSMutableString *output = [NSMutableString stringWithCapacity:CC_SHA1_DIGEST_LENGTH * 2]; for(int i=0; i < CC_SHA1_DIGEST_LENGTH; i++) { [output appendFormat:@"%02x", digest[i]]; } return output; }
MAC算法
MAC(Message Authentication Code,消息认证码算法)结合了MD5和SHA算法的优势,并加入密钥的支持,是一种更为安全的消息摘要算法。消息认证码的算法中,通常会使用带密钥的散列函数(HMAC),或者块密码的带认证工作模式(如CBC-MAC)。信息鉴别码不能提供对信息的保密,若要同时实现保密认证,同时需要对信息进行加密。下面以HMAC为例进行分析:
原理
1.计算HMAC需要一个散列函数hash(可以是md5或者sha-1)和一个密钥key。
2.算法公式HMAC(K,M)=H(K⊕opad∣H(K⊕ipad∣M)) H 代表所采用的HASH算法(如SHA-256) K 代表认证密码 Ko 代表HASH算法的密文 M 代表一个消息输入 B 代表H中所处理的块大小,这个大小是处理块大小,而不是输出hash的大小
3.运算步骤
First-Hash = H(Ko XOR Ipad || (data to auth)) Second-Hash = H(Ko XOR Opad || First-Hash) (1) 在密钥K后面添加0来创建一个字长为B的字符串。(例如,如果K的字长是20字节,B=64字节,则K后会加入44个零字节0x00) (2) 将上一步生成的B字长的字符串与ipad做异或运算。 (3) 将数据流text填充至第二步的结果字符串中。 (4) 用H作用于第三步生成的数据流。 (5) 将第一步生成的B字长字符串与opad做异或运算。 (6) 再将第四步的结果填充进第五步的结果中。 (7) 用H作用于第六步生成的数据流,输出最终结果
应用
HMAC主要应用在身份验证中,过程如下:
- 客户端发出登录请求(假设是浏览器的GET请求)。
- 服务器返回一个随机值,并在会话中记录这个随机值。
- 客户端将该随机值作为密钥,和用户密码进行hmac运算,然后提交给服务器。
- 服务器读取用户数据库中的用户密码和步骤2中发送的随机值做与客户端一样的hmac运算,然后与用户发送的结果比较,如果结果一致则验证用户合法。
iOS代码示例
//相关类文件:#include <CommonCrypto/CommonHMAC.h> -(NSString*)hmacMD5StringWithKey:(NSString*)key{ const char* keyData = key.UTF8String; const char* strData =self.UTF8String; uint8_tbuffer[CC_MD5_DIGEST_LENGTH]; //以MD5为例 CCHmac(kCCHmacAlgMD5, keyData,strlen(keyData), strData,strlen(strData), buffer); return[self stringFromBytes:buffer length:CC_MD5_DIGEST_LENGTH]; }
对称加密算法
在对称加密算法中,使用的密钥只有一个,发收信双方都使用这个密钥对数据进行加密和解密,这就要求解密方事先必须知道加密密钥。密码学中,分组(block)密码的工作模式(mode of operation)允许使用同一个分组密码密钥对多于一块的数据进行加密,并保证其安全性。分组密码自身只能加密长度等于密码分组长度的单块数据,若要加密变长数据,则数据必须先被划分为一些单独的密码块。通常而言,最后一块数据也需要使用合适填充方式将数据扩展到匹配密码块大小的长度。一种工作模式描述了加密每一数据块的过程,并常常使用基于一个通常称为初始化向量的附加输入值以进行随机化,以保证安全。工作模式主要用来进行加密和认证。虽然工作模式通常应用于对称加密,它亦可以应用于公钥加密,例如在原理上对RSA进行处理,但在实用中,公钥密码学通常不用于加密较长的信息,而是使用结合对称加密和公钥加密的混合加密方案。
工作模式:
enum { kCCModeECB = 1, // 电码本模式(Electronic Codebook Book (ECB)) kCCModeCBC = 2, // 密码分组链接模式(Cipher Block Chaining (CBC)) kCCModeCFB = 3, // 密码反馈模式(Cipher FeedBack (CFB)) kCCModeCTR = 4, // 计数器模式(Counter (CTR)) kCCModeF8 = 5, // Unimplemented for now (not included) kCCModeLRW = 6, // Unimplemented for now (not included) kCCModeOFB = 7, // 输出反馈模式(Output FeedBack (OFB) kCCModeXTS = 8, // XEX-based Tweaked CodeBook Mode. kCCModeRC4 = 9, // RC4 as a streaming cipher is handled internally as a mode. kCCModeCFB8 = 10, // Cipher Feedback Mode producing 8 bits per round. };
填充模式:
- 不同的语言环境有不同的填充模式,比较常见的例如:NoPadding、PKCS5Padding、PKCS7Padding、ISO10126Padding。
iOS支持的填充模式有PKCS7Padding(kCCOptionPKCS7Padding表示,缺几位就补几个几)。
enum { ccNoPadding = 0, ccPKCS7Padding = 1, };
- 安全级别:AES > 3DES > DES (不同的算法对应的密钥长度不同)
DES
- DES算法是第一个公开的密码算法,它是一个迭代型分组密码算法,分组长度64位,密钥长度64位,有效密钥长度56位,迭代圈数16圈,圈密钥长度48位。但由于快速DES芯片的大量生产,使得DES仍能暂时继续使用,为提高安全强度,通常使用独立密钥的三级DES。但是DES迟早要被AES代替。密钥表面上是64位的,然而只有其中的56位被实际用于算法,其余8位可以被用于奇偶校验,并在算法中被丢弃。因此,DES的有效密钥长度仅为56位。
- 3DES(即Triple DES)是DES向AES过渡的加密算法,它使用3条64位的密钥对数据进行三次加密。3DES即是设计用来提供一种相对简单的方法,即通过增加DES的密钥长度来避免类似的攻击,而不是设计一种全新的块密码算法。
- 3DES密码选项
标准定义了三种密钥选项(三次密钥分别为K1/K2/K3):
密钥选项1:三个密钥是独立的强度最高——拥有3 x 56 = 168个独立的密钥位。
密钥选项2:K1和K2是独立的,而K3=K1——安全性稍低,拥有2 x 56 = 112个独立的密钥位,该选项比简单的应用DES两次的强度较高,即使用K1和K2,因为它可以防御中途相遇攻击。
密钥选项3:三个密钥均相等,即K1=K2=K3——钥选项3等同与DES,只有56个密钥位
密钥选项1的 3DES原理
3DES使用“密钥包”,其包含3个DES密钥,K1,K2和K3,均为56位(除去奇偶校验位)。加密算法为:密文 = EK3(DK2(EK1(明文))) 即:使用K1为密钥进行DES加密,再用K2为密钥进行DES“解密”,最后以K3进行DES加密。
而解密则为其反过程:
明文 = DK1(EK2(DK3(密文))) 即:以K3解密,以K2“加密”,最后以K1解密。
每次加密操作都只处理64位数据,称为一块。无论是加密还是解密,中间一步都是前后两步的逆。这种做法提高了使用密钥选项2时的算法强度,并在使用密钥选项3时与DES兼容。
AES
AES加密算法(Advanced Encryption Standard,AES),是美国联邦政府采用的一种区块加密标准。这个标准用来替代原先的DES。AES的基本要求是,采用对称分组密码体制,密钥的长度最少支持为128、192、256,但分组长度为128位,算法应易于各种硬件和软件实现。三种密钥长度对比如下:
iOS代码示例
//AES128密钥长度128位(16x8),通常16个字符,例如:”0123456789123456“ -(NSData *)AES128_DecryptWithKey:(NSString *)key{ char keyPtr[kCCKeySizeAES128 + 1]; bzero(keyPtr, sizeof(keyPtr)); [key getCString:keyPtr maxLength:sizeof(keyPtr) encoding:NSUTF8StringEncoding]; size_t bufferSize = self.length + kCCBlockSizeAES128; void *buffer = malloc(bufferSize); size_t numBytesEncrypted = 0; // CCCrypt 是常用简易方法,支持ECB和CBC两种工作模式,如果CCCrypt不能满足,可换用CCCryptorCreateWithMode方法 CCCryptorStatus cryptStatus = CCCrypt(kCCDecrypt, kCCAlgorithmAES128, kCCOptionPKCS7Padding | kCCOptionECBMode, // 采用ECB工作模式,PCKS7填充 keyPtr, //密钥长度128位 kCCBlockSizeAES128, NULL, //ECB模式不需要初始化向量 self.bytes, self.length, buffer, bufferSize, &numBytesEncrypted); if (cryptStatus == kCCSuccess) { return [NSData dataWithBytesNoCopy:buffer length:numBytesEncrypted]; } free(buffer); return nil; }
非对称加密算法
是密码学的一种算法,它需要两个密钥,一个是公开密钥,另一个是私有密钥;一个用作加密的时候,另一个则用作解密。使用其中一个密钥把明文加密后所得的密文,只能用相对应的另一个密钥才能解密得到原本的明文;甚至连最初用来加密的密钥也不能用作解密。由于加密和解密需要两个不同的密钥,故被称为非对称加密。与对称密码相比,非对称加密密钥交换安全,安全性高,不过,公钥加密在在计算上相当复杂,性能欠佳、远远不比对称加密;因此,在一般实际情况下,往往通过公钥加密来随机创建临时的对称秘钥,然后才通过对称加密来传输大量、主体的数据。常见的公钥加密算法有:RSA、ElGamal、背包算法、Rabin(RSA的特例)、迪菲-赫尔曼密钥交换协议中的公钥加密算法、椭圆曲线加密算法(英语:Elliptic Curve Cryptography, ECC)。
RSA
RSA是目前最有影响力的公钥加密算法,对极大整数做因数分解的难度决定了RSA算法的可靠性。
原理
RSA公钥密码体制描述如下:(m为明文,c为密文,利用欧拉公式)- 选取两个大素数p,q。p和q保密。
- 计算n=pq,r=(p-1)(q-1)。n公开,r保密。
- 随机选取正整数1<e<r,满足gcd(e,r)=1.e是公开的加密密钥。
- 计算d,满足de=1(mod r).d是保密的解密密钥
- 加密变换: c=m^e mod n
- 解密变换: m=c^d mod n
特点
- 两个密钥:一个是公开密钥,另一个是私有密钥。
- 安全性高,密钥越长,它就越难破解。
安全性
- 针对RSA最流行的攻击一般是基于大数因数分解。1999年,RSA-155 (512 bits)被成功分解,花了五个月时间(约8000 MIPS年)和224 CPU hours在一台有3.2G中央内存的Cray C916计算机上完成。
- 2009年12月12日,编号为RSA-768(768 bits, 232 digits)数也被成功分解[10]。这一事件威胁了现通行的1024-bit密钥的安全性,普遍认为用户应尽快升级到2048-bit或以上。
应用
- 加密:公钥加密私钥解密,主要用于将数据资料加密不被其他人非法获取,保证数据安全性。使用公钥将数据资料加密,只有私钥可以解密。即使密文在网络上被第三方获取由于没有私钥则无法解密。从而保证数据安全性。
- 认证(签名):私钥加密公钥解密,主要用于身份验证,判断某个身份的真实性(没有被篡改)。使用私钥加密之后,用对应的公钥解密从而验证身份真实性。(例如SSH登录)