據說今天520是個好日子,為什么我想起的是502、500、404這些?還好服務器沒事!
一、Base64編碼
Base64編碼要求把3個8位字節(3*8=24)
轉化為4個6位的字節(4*6=24)
,之后在6位的前面補兩個0,形成8位一個字節的形式,這樣每一個字節的有效位為6位,則取值范圍0~630 ~ (2^6 - 1)
。如果最后剩下的字符不到3個字節,則用0填充,輸出字符使用'=',因此我們看到Base64末尾會有1到2個'='。另外標准還要求每76個字符要插入換行(不過,這個視具體情況定)。
iOS7之后蘋果有自己的Base64編碼解碼API,NSData的擴展:NSData (NSDataBase64Encoding)
兩種存儲方式
- 可見字符串形式
為了保證所輸出的每一個編碼字節都是可讀字符,而不是0~63這些數字,Base64制作了一個碼表,就像ASCII碼表一樣,每一個Base64碼值都有對應的字符。64個可讀字符從0到63非別是A-Z、a-z、0-9、+、/
,這也是Base64名字的由來。
- 以16進制形式
即NSData形式保存,Base64編碼結果為字符,而這些字符又對應ASCII碼表的碼值,NSData就是存儲ASCII碼表的碼值。
下面舉個例子,並以蘋果提供的API來詳細介紹Base64編碼解碼過程:
假設我們對字符串"123"進行Base64編碼,"123"對應的16進制是313233,二進制為00110001、00110010、00110011
,將其變為4*6結果即下表中的第一行。然后根據Base64的碼表,它們分別對應表中的第二行。那么"123"編碼的最終結果即為MTIz,以字符串的形式保存。然后根據MTIz對應ASCII碼值,以NSData形式存儲,如表中的第三行。
轉換為4*6結果 | 00001100 | 00010011 | 00001000 | 00110011 |
---|---|---|---|---|
Base64對應字符 | M | T | I | z |
對應ASCII碼值(16進制) | 4d | 54 | 49 | 7a |
上面的過程通過代碼實現如下:
// 1 待編碼的原始字符串
NSString *plainStr = @"123";
// 2 將其轉換成NSData保存,那么"123"對應的ASCII碼表碼值是31、32、33(16進制)
NSData *plainData = [plainStr dataUsingEncoding:NSUTF8StringEncoding];
// 3.1 將其進行Base64編碼,且結果以字符串形式保存,對應表中的第二行
NSString *baseStr = [plainData base64EncodedStringWithOptions:0];
// 3.2 將其進行Base64編碼,且結果以NSData形式保存
NSData *base64Data = [plainData base64EncodedDataWithOptions:0];
另外對於參數NSDataBase64EncodingOptions選項,有多種取值
- NSDataBase64Encoding64CharacterLineLength:每64個字符插入\r或\n
- NSDataBase64Encoding76CharacterLineLength:每76個字符插入\r或\n,標准中有要求是76個字符要換行,不過具體還是自己定
- NSDataBase64EncodingEndLineWithCarriageReturn:插入字符為\r
- NSDataBase64EncodingEndLineWithLineFeed:插入字符為\n
前兩個選項為是否允許插入字符,以及多少個字符長度插入,兩個可以選其一或者都不選。后兩個選項代表要插入的具體字符。比如我們想76個字符后插入一個\r則可以NSDataBase64Encoding76CharacterLineLength | NSDataBase64EncodingEndLineWithCarriageReturn
。而在上面舉的例子中選項為0,則代表不插入字符。
第三方框架
在iOS7之前我們一般用的都是第三方框架,比如nicklockwood寫的https://github.com/nicklockwood/Base64還有Google的GTMBase64,雖然蘋果有了自己的實現,但是許多其它的加密框架都用到了它,所以還是要了解一下,另外它還提供任意長度字符插入\r\n
,而蘋果只能是64或76長度。
二、MD5、SHA1、SHA256、SHA512、HMAC實現
主要用於驗證,防止信息被修改。介紹請參照http://www.jianshu.com/p/003b85fd3e36。
具體的實現參考第三方框架:https://github.com/kelp404/CocoaSecurity。非常全面,不過不是太方便,比如想要獲得MD5結果
NSString *plainStr = @"123";
CocoaSecurityResult *md5 = [CocoaSecurity md5:plainStr];
// 獲取md5結果
NSString *md5Str = md5.hexLower;
不能直接plainStr.MD5Hash就獲得字符串形式的結果,這里我封裝了一個,可以參見工程中的NSString+Hash類https://github.com/mddios/EncryptionTools,可以直接對字符串進行操作,類似plainStr.MD5Hash、plainStr.sha1Hash···plainStr.sha256Hash···
,非常方便。
比如對@"123"哈希,下面用上面提到的兩種方法舉例:
- (void)hashTest {
NSString *plainStr = @"123";
// md5
CocoaSecurityResult *md5 = [CocoaSecurity md5:plainStr];
NSLog(@"md5:%lu---%@---%@",plainStr.md5Hash.length, plainStr.md5Hash,md5.hex);
// 40
CocoaSecurityResult *sha1 = [CocoaSecurity sha1:plainStr];
NSLog(@"sha1:%lu---%@---%@",plainStr.sha1Hash.length, plainStr.sha1Hash,sha1.hex);
// 56
CocoaSecurityResult *sha224 = [CocoaSecurity sha224:plainStr];
NSLog(@"sha224:%lu---%@---%@",plainStr.sha224Hash.length,plainStr.sha224Hash,sha224.hex);
// 64
CocoaSecurityResult *sha256 = [CocoaSecurity sha256:plainStr];
NSLog(@"sha256:%lu---%@---%@",plainStr.sha256Hash.length,plainStr.sha256Hash,sha256.hex);
// 96
CocoaSecurityResult *sha384 = [CocoaSecurity sha384:plainStr];
NSLog(@"sha384:%lu---%@---%@",plainStr.sha384Hash.length,plainStr.sha384Hash,sha384.hex);
// 128
CocoaSecurityResult *sha512 = [CocoaSecurity sha512:plainStr];
NSLog(@"sha512:%lu---%@---%@",plainStr.sha512Hash.length,plainStr.sha512Hash,sha512.hex);
// hmac
CocoaSecurityResult *hmacmd5 = [CocoaSecurity hmacMd5:plainStr hmacKey:plainStr];
NSLog(@"hmacmd5:%lu---%@---%@",[plainStr hmacMD5WithKey:plainStr].length,[plainStr hmacMD5WithKey:plainStr],hmacmd5.hex);
}
- 在電腦終端來獲取結果
封裝的代碼中NSString+Hash.h
頭文件,有具體列出終端命令方法,如下:
/// 返回結果:32長度 終端命令:md5 -s "123"
- (NSString *)md5Hash;
/// 返回結果:40長度 終端命令:echo -n "123" | openssl sha -sha1
- (NSString *)sha1Hash;
/// 返回結果:56長度 終端命令:echo -n "123" | openssl sha -sha224
- (NSString *)sha224Hash;
/// 返回結果:64長度 終端命令:echo -n "123" | openssl sha -sha256
- (NSString *)sha256Hash;
/// 返回結果:96長度 終端命令:echo -n "123" | openssl sha -sha384
- (NSString *)sha384Hash;
/// 返回結果:128長度 終端命令:echo -n "123" | openssl sha -sha512
- (NSString *)sha512Hash;
#pragma mark - HMAC
/// 返回結果:32長度 終端命令:echo -n "123" | openssl dgst -md5 -hmac "123"
- (NSString *)hmacMD5WithKey:(NSString *)key;
/// 返回結果:40長度 echo -n "123" | openssl sha -sha1 -hmac "123"
- (NSString *)hmacSHA1WithKey:(NSString *)key;
- (NSString *)hmacSHA224WithKey:(NSString *)key;
- (NSString *)hmacSHA256WithKey:(NSString *)key;
- (NSString *)hmacSHA384WithKey:(NSString *)key;
- (NSString *)hmacSHA512WithKey:(NSString *)key;
- 關於MD5加鹽,只是多了下面第一行
plainStr = [plainStr stringByAppendingString:salt];
NSString *md5Str = plainStr.md5Hash;