Base64編碼原理
Base64編碼之所以稱為Base64,是因為其使用64個字符來對任意數(shù)據(jù)進行編碼,同理有Base32、Base16編碼。標(biāo)準(zhǔn)Base64編碼使用的64個字符為:
這64個字符是各種字符編碼(比如ASCII編碼)所使用字符的子集翘悉,基本,并且可打印居触。唯一有點特殊的是最后兩個字符妖混,因?qū)ψ詈髢蓚€字符的選擇不同,Base64編碼又有很多變種轮洋,比如Base64 URL編碼制市。
Base64編碼本質(zhì)上是一種將二進制數(shù)據(jù)轉(zhuǎn)成文本數(shù)據(jù)的方案。對于非二進制數(shù)據(jù)砖瞧,是先將其轉(zhuǎn)換成二進制形式息堂,然后每連續(xù)6比特(2的6次方=64)計算其十進制值,根據(jù)該值在上面的索引表中找到對應(yīng)的字符,最終得到一個文本字符串荣堰。
假設(shè)我們要對 Hello! 進行Base64編碼床未,按照ASCII表,其轉(zhuǎn)換過程如下圖所示:
可知 Hello! 的Base64編碼結(jié)果為 SGVsbG8h 振坚,原始字符串長度為6個字符薇搁,編碼后長度為8個字符,每3個原始字符經(jīng)Base64編碼成4個字符渡八,編碼前后長度比4/3啃洋,這個長度比很重要 - 比原始字符串長度短,則需要使用更大的編碼字符集屎鳍,這并不我們想要的宏娄;長度比越大,則需要傳輸越多的字符逮壁,傳輸時間越長孵坚。Base64應(yīng)用廣泛的原因是在字符集大小與長度比之間取得一個較好的平衡,適用于各種場景窥淆。
是不是覺得Base64編碼原理很簡單放吩?
但這里需要注意一個點:Base64編碼是每3個原始字符編碼成4個字符锨络,如果原始字符串長度不能被3整除匿刮,那怎么辦鳞骤?使用0值來補充原始字符串。
以 Hello!! 為例词裤,其轉(zhuǎn)換過程為:
注:圖表中藍色背景的二進制0值是額外補充的刺洒。
Hello!! Base64編碼的結(jié)果為 SGVsbG8hIQAA 。最后2個零值只是為了Base64編碼而補充的亚斋,在原始字符中并沒有對應(yīng)的字符作媚,那么Base64編碼結(jié)果中的最后兩個字符 AA 實際不帶有效信息,所以需要特殊處理帅刊,以免解碼錯誤。
標(biāo)準(zhǔn)Base64編碼通常用 = 字符來替換最后的 A漂问,即編碼結(jié)果為 SGVsbG8hIQ==赖瞒。因為 = 字符并不在Base64編碼索引表中,其意義在于結(jié)束符號蚤假,在Base64解碼時遇到 = 時即可知道一個Base64編碼字符串結(jié)束栏饮。
如果Base64編碼字符串不會相互拼接再傳輸,那么最后的 = 也可以省略磷仰,解碼時如果發(fā)現(xiàn)Base64編碼字符串長度不能被4整除袍嬉,則先補充 = 字符,再解碼即可。
解碼是對編碼的逆向操作伺通,但注意一點:對于最后的兩個 = 字符箍土,轉(zhuǎn)換成兩個 A 字符,再轉(zhuǎn)成對應(yīng)的兩個6比特二進制0值罐监,接著轉(zhuǎn)成原始字符之前吴藻,需要將最后的兩個6比特二進制0值丟棄,因為它們實際上不攜帶有效信息弓柱。
以下為示例代碼
1.通過NSString+Base64分類來實現(xiàn)
#import <Foundation/Foundation.h>
@interface NSString (Base64)
/**
* 轉(zhuǎn)換為Base64編碼
*/
- (NSString *)base64EncodedString;
/**
* 將Base64編碼還原
*/
- (NSString *)base64DecodedString;
@end
`
#import "NSString+Base64.h"
@implementation NSString (Base64)
- (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];
}
@end
2. 調(diào)用方法實現(xiàn)
NSString *codeString = @"Hello world";
NSLog(@"原文--%@",codeString);
NSString *base64Str = [codeString base64EncodedString];
NSLog(@"Base64編碼--%@",base64Str);
NSString *decodeStr = [base64Str base64DecodedString];
NSLog(@"Base64解碼--%@",decodeStr);
3.測試結(jié)果
切忌誤用
可能會有人在不理解Base64編碼的情況下沟堡,將其誤用于數(shù)據(jù)加密或數(shù)據(jù)校驗。
Base64是一種數(shù)據(jù)編碼方式矢空,目的是讓數(shù)據(jù)符合傳輸協(xié)議的要求航罗。標(biāo)準(zhǔn)Base64編碼解碼無需額外信息即完全可逆,即使你自己自定義字符集設(shè)計一種類Base64的編碼方式用于數(shù)據(jù)加密屁药,在多數(shù)場景下也較容易破解伤哺。
對于數(shù)據(jù)加密應(yīng)該使用專門的目前還沒有有效方式快速破解的加密算法。比如:對稱加密算法AES-128-CBC者祖,對稱加密需要密鑰立莉,只要密鑰沒有泄露,通常難以破解七问;也可以使用非對稱加密算法蜓耻,如 RSA,利用極大整數(shù)因數(shù)分解的計算量極大這一特點械巡,使得使用公鑰加密的數(shù)據(jù)刹淌,只有使用私鑰才能快速解密。
對于數(shù)據(jù)校驗讥耗,也應(yīng)該使用專門的消息認(rèn)證碼生成算法有勾,如 HMAC - 一種使用單向散列函數(shù)構(gòu)造消息認(rèn)證碼的方法,其過程是不可逆的古程、唯一確定的蔼卡,并且使用密鑰來生成認(rèn)證碼,其目的是防止數(shù)據(jù)在傳輸過程中被篡改或偽造挣磨。將原始數(shù)據(jù)與認(rèn)證碼一起傳輸雇逞,數(shù)據(jù)接收端將原始數(shù)據(jù)使用相同密鑰和相同算法再次生成認(rèn)證碼,與原有認(rèn)證碼進行比對茁裙,校驗數(shù)據(jù)的合法性塘砸。
總結(jié)
Base64兼顧字符集大小和編碼后數(shù)據(jù)長度,并且可以靈活替換字符集的最后兩個字符晤锥,以應(yīng)對多樣的需求掉蔬,使其適用場景非常廣泛廊宪。