一、Base64
原理:
● base64的編碼都是按字符串長度,以每3個8bit的字符爲一組,
● 然後針對每組,首先獲取每個字符的ASCII編碼,
● 然後將ASCII編碼轉換成8bit的二進制,得到一組3*8=24bit的字節
● 然後再將這24bit劃分爲4個6bit的字節,並在每個6bit的字節前面都填兩個高位0,得到4個8bit的字節
● 然後將這4個8bit的字節轉換成10進制,對照Base64編碼表 (下表),得到對應編碼後的字符。
注:1. 要求被編碼字符是8bit的,所以須在ASCII編碼範圍內,\u0000-\u00ff,中文就不行。
2. 如果被編碼字符長度不是3的倍數的時候,則都用0代替,對應的輸出字符爲=
系統自帶加密base64代碼
//只支持加密英文
NSString *originStr = @"originStr";
NSData* originData = [originStr dataUsingEncoding:NSASCIIStringEncoding];
NSString* encodeResult = [originData base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed];
NSLog(@"encodeResult=%@",encodeResult);
NSData* decodeData = [[NSData alloc] initWithBase64EncodedString:encodeResult options:0];
NSString* decodeStr = [[NSString alloc] initWithData:decodeData encoding:NSASCIIStringEncoding];
NSLog(@"decodeStr=%@",decodeStr);
用第三方GTMBase64
● 用pod下載第三方 pod ‘GTMBase64’, ‘~> 1.0.0’
● 寫GTMBase64工具類,這裏我用FLTBase64.h爲例:
○ FLTBase64.h文件
#import <Foundation/Foundation.h>
@interface FLTBase64 : NSObject
+ (NSString*)encodeBase64String:(NSString *)input;
+ (NSString*)decodeBase64String:(NSString *)input;
+ (NSString*)encodeBase64Data:(NSData *)data;
+ (NSString*)decodeBase64Data:(NSData *)data;
@end
● FLTBase64.m文件
#import "FLTBase64.h"
#import <GTMBase64.h>
@implementation FLTBase64
+ (NSString*)encodeBase64String:(NSString * )input {
NSData *data = [input dataUsingEncoding:NSUTF8StringEncoding allowLossyConversion:YES];
data = [GTMBase64 encodeData:data];
NSString *base64String = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
return base64String;
}
+ (NSString*)decodeBase64String:(NSString * )input {
NSData *data = [input dataUsingEncoding:NSUTF8StringEncoding allowLossyConversion:YES];
data = [GTMBase64 decodeData:data];
NSString *base64String = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
return base64String;
}
+ (NSString*)encodeBase64Data:(NSData *)data {
data = [GTMBase64 encodeData:data];
NSString *base64String = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
return base64String;
}
+ (NSString*)decodeBase64Data:(NSData *)data {
data = [GTMBase64 decodeData:data];
NSString *base64String = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
return base64String;
}
@end
● 需要加密的地方寫加密解密代碼
/base64加密 利用第三方GTMBase64加密
NSString *password = @"password";
NSString *baseEncodeString = [FLTBase64 encodeBase64String:password];
NSString *baseDecodeString = [FLTBase64 decodeBase64String:baseEncodeString];
NSLog(@"baseEncodeString = %@",baseEncodeString);
NSLog(@"baseDecodeString = %@",baseDecodeString);
二、SSkeychain第三方加密 採用DES 對本地化用戶名 密碼進行加密 對稱加密
DES(數據加密標準)原理
DES是一個分組加密算法,它以64位爲分組對數據加密。64位一組的明文從算法的一端輸入,64位的密文從另一段輸出。它是一個對稱算法:加密和解密用的是同一個算法。
密鑰通常表示爲64位的數,但每個第8位都用作奇偶校驗,可以忽略,所以密鑰長度爲56位。密鑰可以是任意的56位的數,且可在任意的時候改變。
DES算法只不過是加密的兩個基本技術——混亂和擴散的組合,即先代替後置換,它基於密鑰作用於明文,這是一輪(round),DES在明文分組上實施16輪相同的組合技術。
這裏用第三方SSKeychain(屬於DES) 保存用戶名密碼 本地化加密
pod ‘SSKeychain’
● 加密、解密代碼
NSString *bundleID = [NSBundle mainBundle].bundleIdentifier;
[SSKeychain setPassword:@"111" forService:bundleID account:@"ni"];
[SSKeychain setPassword:@"222" forService:bundleID account:@"wo"];
[SSKeychain setPassword:@"333" forService:bundleID account:@"ta"];
NSString *passwords = [SSKeychain passwordForService:bundleID account:@"wo"];
NSLog(passwords);
三、AES加密 (高級加密標準) 對稱加密
原理:
AES 是一個新的可以用於保護電子數據的加密算法。明確地說,AES 是一個迭代的、對稱密鑰分組的密碼,它可以使用128、192 和 256 位密鑰,並且用 128 位(16字節)分組加密和解密數據。與公共密鑰密碼使用密鑰對不同,對稱密鑰密碼使用相同的密鑰加密和解密數據。通過分組密碼返回的加密數據 的位數與輸入數據相同。迭代加密使用一個循環結構,在該循環中重複置換(permutations )和替換(substitutions)輸入數據。Figure 1 顯示了 AES 用192位密鑰對一個16位字節數據塊進行加密和解密的情形。
加入第三方AES文件
● 克隆項目文件
● 拖動AES文件到自己項目。
● 加密代碼
// AES加密 (高級加密標準) 對稱加密
NSString *password = @"password";//一般來說用戶名就是用戶密碼的Key
NSString *passwordKey = @"userName";
NSString *encryptedData = [AESCrypt encrypt:password password:passwordKey];//加密
NSString *messagepassword = [AESCrypt decrypt:encryptedData password:passwordKey]; //解密
NSLog(@"加密結果 = %@",encryptedData);
NSLog(@"解密結果 = %@",messagepassword);
● 加密實例代碼
-(void)saveUserNameAndPwd:(NSString *)userName andPwd:(NSString *)pwd
{
NSUserDefaults * settings = [NSUserDefaults standardUserDefaults];
[settings removeObjectForKey:@"UserName"];
[settings removeObjectForKey:@"Password"];
[settings setObject:userName forKey:@"UserName"];
pwd = [AESCrypt encrypt:pwd password:@"pwd"];
[settings setObject:pwd forKey:@"Password"];
[settings synchronize];
}
-(NSString *)getPwd
{
NSUserDefaults * settings = [NSUserDefaults standardUserDefaults];
NSString * temp = [settings objectForKey:@"Password"];
return [AESCrypt decrypt:temp password:@"pwd"];
}
四、MD5摘要算法
MD5簡介
MD5的全稱爲Message-Digest Algorithm 5,即消息摘要算法第五版,是當前計算機領域用於確保信息傳輸完整一致而廣泛使用的散列算法之一。MD5算法的功能是將數據運算變爲另一固定長度值,是散列算法的基礎原理。MD5的前身有MD2、MD3和MD4。
原理簡介
對MD5算法簡要的敘述可以爲:MD5以512位分組來處理輸入的信息,且每一分組又被劃分爲16個32位子分組,經過了一系列的處理後,算法的輸出由四個32位分組組成,將這四個32位分組級聯後將生成一個128位散列值。
● MD5算法原理
用途
● 防止被篡改,比如我提供文件下載,爲了防止不法分子在安裝程序中添加木馬,我可以在網站上公佈由安裝文件得到的MD5輸出結果。
● 防止直接看到明文,現在很多網站在數據庫存儲用戶的密碼的時候都是存儲用戶密碼的MD5值。這樣就算不法分子得到數據庫的用戶密碼的MD5值,也無法知道用戶的密碼。
● 防止抵賴(數字簽名),例如A寫了一個文件,認證機構對此文件用MD5算法產生摘要信息並做好記錄。這樣可以防止出現以後A不承認此事而帶來的麻煩。
MD5安全性
在用戶密碼的處理方面,MD5總的來看還算是安全的,至少比明文保存密碼要好的多,目前破解MD5主要依靠大型字典的方法,將常用密碼進行MD5後建立數據庫,然後和MD5數值進行對比,通過這樣的方法來“破解”MD5,因此,通常直接將密碼進行MD5處理的話,一些弱密碼很容易可以通過這種手段“破解”出來。
不過,如果在散列的過程中,加入足夠長的salt(即干擾字符串),並且salt加入一些動態信息,例如username、隨機碼等,這樣生成的MD5還是很難被破解的,因爲僅僅從數據庫無法看到MD5具體的處理過程,必須同時看到處理時的源代碼纔可以,這就給破解MD5帶來相當大的難度。
還有一個方法,既然簡單密碼的MD5是不安全的,網站的開發者只需要一個簡單的技巧就能提高密碼的安全度:在用戶註冊的時候,錄入新密碼後進行判斷,強制密碼必須8位以上,幷包含字母和數字,否則不讓註冊,這樣用戶註冊後使用的密碼就都是不容易被破解的密碼了。
如果需要更安全的算法,建議不用MD5,而使用SHA-256, SHA(Secure Hash Algorithm,安全散列算法)是美國國家安全局(NSA)設計,美國國家標準與技術研究院(NIST)發佈的一系列密碼散列函數。目前還沒有出現針對SHA-256算法的有效碰撞攻擊方法,該算法也是開源算法,在很多地方可以找到,是MD5的一個不錯的後繼者。
MD5集成
● 1、添加依賴庫 libSystem.tdb
● 2、新建工具類
#import "FLTMD5.h"
○ import "FLTMD5.h"
#import <Foundation/Foundation.h>
@interface FLTMD5 : NSObject
+(NSString *)md5HexDigest:(NSString *)input;
@end
● import "FLTMD5.m"
#import "FLTMD5.h"
//需要加入依賴庫 libSystem.tdb
#import <CommonCrypto/CommonDigest.h>
@implementation FLTMD5
+(NSString *)md5HexDigest:(NSString *)input{
const char* str = [input UTF8String];
unsigned char result[CC_MD5_DIGEST_LENGTH];
CC_MD5(str, strlen(str), result);
NSMutableString *ret = [NSMutableString stringWithCapacity:CC_MD5_DIGEST_LENGTH];
for(int i = 0; i<CC_MD5_DIGEST_LENGTH; i++) {
[ret appendFormat:@"%02X",result];
}
return ret;
}
@end
● 3、MD5代碼
//MD5加密 MD5是不可逆的只有加密沒有解密
NSString *userName = @"cerastes";
NSString *password = @"hello Word";
// MD5加密 單純的MD5加密不安全 需要加鹽,加鹽就是在要加密的內容後加一些常量,常量越長越安全
//鹽值
NSString *salt = @"追加的:鹽值";
password = [password stringByAppendingString:salt];
//MD5有兩種方式防止暴力破解 1是多次MD5加密,次數是要保密的 2加鹽 可變鹽,文件越大越安全,當然也就越費時間。3當然還要防止別人破解你的源碼,源碼被破解是非常危險的。可以諮詢愛加密.
NSString *md5 = [FLTMD5 md5HexDigest:password];
NSLog(@"%@",md5);
NSLog(@"%lu",(unsigned long)md5.length);
以上加密方式的Demo.
五、RSA加密 非對稱加密
簡介
1977年,三位數學家Rivest、Shamir 和 Adleman 設計了一種算法,可以實現非對稱加密。這種算法用他們三個人的名字命名,叫做RSA算法。從那時直到現在,RSA算法一直是最廣爲使用的”非對稱加密算法”。毫不誇張地說,只要有計算機網絡的地方,就有RSA算法。
這種算法非常可靠,密鑰越長,它就越難破解。根據已經披露的文獻,目前被破解的最長RSA密鑰是768個二進制位。也就是說,長度超過768位的密鑰,還無法破解(至少沒人公開宣佈)。因此可以認爲,1024位的RSA密鑰基本安全,2048位的密鑰極其安全。
原理簡介
RSA是目前最有影響力的公鑰加密算法,該算法基於一個十分簡單的數論事實:將兩個大素數相乘十分容易,但那時想要對其乘積進行因式分解卻極其困難,因此可以將乘積公開作爲加密密鑰,即公鑰,而兩個大素數組合成私鑰。公鑰是可發佈的供任何人使用,私鑰則爲自己所有,供解密之用。
解密者擁有私鑰,並且將由私鑰計算生成的公鑰發佈給加密者。加密都使用公鑰進行加密,並將密文發送到解密者,解密者用私鑰解密將密文解碼爲明文。
以甲要把信息發給乙爲例,首先確定角色:甲爲加密者,乙爲解密者。首先由乙隨機確定一個KEY,稱之爲密匙,將這個KEY始終保存在機器B中而不發出來;然後,由這個 KEY計算出另一個KEY,稱之爲公匙。這個公鑰的特性是幾乎不可能通過它自身計算出生成它的私鑰。接下來通過網絡把這個公鑰傳給甲,甲收到公鑰後,利用公鑰對信息加密,並把密文通過網絡發送到乙,最後乙利用已知的私鑰,就對密文進行解碼了。以上就是RSA算法的工作流程。
● 參考網站1
● 參考網站2
RSA集成
1、導入依賴庫 Security.framework
2、導入pod第三方庫pod ‘Base64nl’, ‘~> 1.2’
3、製作祕鑰
● 1、打開Terminal (終端) 進入保存祕鑰文件夾
openssl genrsa -out private_key.pem 1024
openssl req -new -key private_key.pem -out rsaCertReq.csr
openssl x509 -req -days 3650 -in rsaCertReq.csr -signkey private_key.pem -out rsaCert.crt
openssl x509 -outform der -in rsaCert.crt -out public_key.der // Create public_key.der For IOS
openssl pkcs12 -export -out private_key.p12 -inkey private_key.pem -in rsaCert.crt // Create private_key.p12 For IOS. 這一步,請記住你輸入的密碼,IOS代碼裏會用到
openssl rsa -in private_key.pem -out rsa_public_key.pem -pubout // Create rsa_public_key.pem For Java
openssl pkcs8 -topk8 -in private_key.pem -out pkcs8_private_key.pem -nocrypt // Create pkcs8_private_key.pem For Java `
上面七個步驟,總共生成7個文件。其中 public_key.der 和 private_key.p12 這對公鑰私鑰是給IOS用的, rsa_public_key.pem 和 pkcs8_private_key.pem 是給JAVA用的。
它們的源都來自一個私鑰:private_key.pem , 所以IOS端加密的數據,是可以被JAVA端解密的,反過來也一樣。
● 2、把 public_key.der 和 private_key.p12 拖進你的Xcode項目裏去 。
4、新建RSA工具類
#import "RSAEncryptor.h"
● import "RSAEncryptor.h
#import <Foundation/Foundation.h>
@interface RSAEncryptor : NSObject
#pragma mark - Instance Methods
-(void) loadPublicKeyFromFile: (NSString*) derFilePath;
-(void) loadPublicKeyFromData: (NSData*) derData;
-(void) loadPrivateKeyFromFile: (NSString*) p12FilePath password:(NSString*)p12Password;
-(void) loadPrivateKeyFromData: (NSData*) p12Data password:(NSString*)p12Password;
-(NSString*) rsaEncryptString:(NSString*)string;
-(NSData*) rsaEncryptData:(NSData*)data ;
-(NSString*) rsaDecryptString:(NSString*)string;
-(NSData*) rsaDecryptData:(NSData*)data;
#pragma mark - Class Methods
+(void) setSharedInstance: (RSAEncryptor*)instance;
+(RSAEncryptor*) sharedInstance;
@end
● import "RSAEncryptor.m"
#import "RSAEncryptor.h"
#import <Base64.h>
#import <Security/Security.h>
@implementation RSAEncryptor
{
SecKeyRef publicKey;
SecKeyRef privateKey;
}
-(void)dealloc
{
CFRelease(publicKey);
CFRelease(privateKey);
}
-(SecKeyRef) getPublicKey {
return publicKey;
}
-(SecKeyRef) getPrivateKey {
return privateKey;
}
-(void) loadPublicKeyFromFile: (NSString*) derFilePath
{
NSData *derData = [[NSData alloc] initWithContentsOfFile:derFilePath];
[self loadPublicKeyFromData: derData];
}
-(void) loadPublicKeyFromData: (NSData*) derData
{
publicKey = [self getPublicKeyRefrenceFromeData: derData];
}
-(void) loadPrivateKeyFromFile: (NSString*) p12FilePath password:(NSString*)p12Password
{
NSData *p12Data = [NSData dataWithContentsOfFile:p12FilePath];
[self loadPrivateKeyFromData: p12Data password:p12Password];
}
-(void) loadPrivateKeyFromData: (NSData*) p12Data password:(NSString*)p12Password
{
privateKey = [self getPrivateKeyRefrenceFromData: p12Data password: p12Password];
}
#pragma mark - Private Methods
-(SecKeyRef) getPublicKeyRefrenceFromeData: (NSData*)derData
{
SecCertificateRef myCertificate = SecCertificateCreateWithData(kCFAllocatorDefault, (__bridge CFDataRef)derData);
SecPolicyRef myPolicy = SecPolicyCreateBasicX509();
SecTrustRef myTrust;
OSStatus status = SecTrustCreateWithCertificates(myCertificate,myPolicy,&myTrust);
SecTrustResultType trustResult;
if (status == noErr) {
status = SecTrustEvaluate(myTrust, &trustResult);
}
SecKeyRef securityKey = SecTrustCopyPublicKey(myTrust);
CFRelease(myCertificate);
CFRelease(myPolicy);
CFRelease(myTrust);
return securityKey;
}
-(SecKeyRef) getPrivateKeyRefrenceFromData: (NSData*)p12Data password:(NSString*)password
{
SecKeyRef privateKeyRef = NULL;
NSMutableDictionary * options = [[NSMutableDictionary alloc] init];
[options setObject: password forKey:(__bridge id)kSecImportExportPassphrase];
CFArrayRef items = CFArrayCreate(NULL, 0, 0, NULL);
OSStatus securityError = SecPKCS12Import((__bridge CFDataRef) p12Data, (__bridge CFDictionaryRef)options, &items);
if (securityError == noErr && CFArrayGetCount(items) > 0) {
CFDictionaryRef identityDict = CFArrayGetValueAtIndex(items, 0);
SecIdentityRef identityApp = (SecIdentityRef)CFDictionaryGetValue(identityDict, kSecImportItemIdentity);
securityError = SecIdentityCopyPrivateKey(identityApp, &privateKeyRef);
if (securityError != noErr) {
privateKeyRef = NULL;
}
}
CFRelease(items);
return privateKeyRef;
}
#pragma mark - Encrypt
-(NSString*) rsaEncryptString:(NSString*)string {
NSData* data = [string dataUsingEncoding:NSUTF8StringEncoding];
NSData* encryptedData = [self rsaEncryptData: data];
NSString* base64EncryptedString = [encryptedData base64EncodedString];
return base64EncryptedString;
}
// 加密的大小受限於SecKeyEncrypt函數,SecKeyEncrypt要求明文和密鑰的長度一致,如果要加密更長的內容,需要把內容按密鑰長度分成多份,然後多次調用SecKeyEncrypt來實現
-(NSData*) rsaEncryptData:(NSData*)data {
SecKeyRef key = [self getPublicKey];
size_t cipherBufferSize = SecKeyGetBlockSize(key);
uint8_t *cipherBuffer = malloc(cipherBufferSize * sizeof(uint8_t));
size_t blockSize = cipherBufferSize - 11; // 分段加密
size_t blockCount = (size_t)ceil([data length] / (double)blockSize);
NSMutableData *encryptedData = [[NSMutableData alloc] init] ;
for (int i=0; i<blockCount; i++) {
int bufferSize = MIN(blockSize,[data length] - i * blockSize);
NSData *buffer = [data subdataWithRange:NSMakeRange(i * blockSize, bufferSize)];
OSStatus status = SecKeyEncrypt(key, kSecPaddingPKCS1, (const uint8_t *)[buffer bytes], [buffer length], cipherBuffer, &cipherBufferSize);
if (status == noErr){
NSData *encryptedBytes = [[NSData alloc] initWithBytes:(const void *)cipherBuffer length:cipherBufferSize];
[encryptedData appendData:encryptedBytes];
}else{
if (cipherBuffer) {
free(cipherBuffer);
}
return nil;
}
}
if (cipherBuffer){
free(cipherBuffer);
}
return encryptedData;
}
#pragma mark - Decrypt
-(NSString*) rsaDecryptString:(NSString*)string {
NSData *data = [NSData dataWithBase64EncodedString:string];
// NSData* data = [NSData dataFromBase64String: string];
NSData* decryptData = [self rsaDecryptData: data];
NSString* result = [[NSString alloc] initWithData: decryptData encoding:NSUTF8StringEncoding];
return result;
}
-(NSData*) rsaDecryptData:(NSData*)data {
SecKeyRef key = [self getPrivateKey];
size_t cipherLen = [data length];
void *cipher = malloc(cipherLen);
[data getBytes:cipher length:cipherLen];
size_t plainLen = SecKeyGetBlockSize(key) - 12;
void *plain = malloc(plainLen);
OSStatus status = SecKeyDecrypt(key, kSecPaddingPKCS1, cipher, cipherLen, plain, &plainLen);
if (status != noErr) {
return nil;
}
NSData *decryptedData = [[NSData alloc] initWithBytes:(const void *)plain length:plainLen];
return decryptedData;
}
#pragma mark - Class Methods
static RSAEncryptor* sharedInstance = nil;
+(void) setSharedInstance: (RSAEncryptor*)instance
{
sharedInstance = instance;
}
+(RSAEncryptor*) sharedInstance
{
return sharedInstance;
}
@end
5、加密解密代碼
//配置祕鑰
RSAEncryptor* rsaEncryptor = [[RSAEncryptor alloc] init];
NSString* publicKeyPath = [[NSBundle mainBundle] pathForResource:@"public_key" ofType:@"der"];
NSString* privateKeyPath = [[NSBundle mainBundle] pathForResource:@"private_key" ofType:@"p12"];
[rsaEncryptor loadPublicKeyFromFile: publicKeyPath];
[rsaEncryptor loadPrivateKeyFromFile: privateKeyPath password:@"www.57.com"]; // 這裏,請換成你生成p12時的密碼
//字符串RSA加密
NSString *string = @"ios RSA加密";
NSString* restrinBASE64STRING = [rsaEncryptor rsaEncryptString:string];
NSLog(@"Encrypted==== %@", restrinBASE64STRING); // 請把這段字符串Copy到JAVA這邊main()裏做測試
//字符串RSA解密
NSString* decryptString = [rsaEncryptor rsaDecryptString: restrinBASE64STRING];
NSLog(@"Decrypted==== %@", decryptString);
// System.out.println the encrypt string from Java , and paste it here
// 這裏請換成你的JAVA這邊產生的加密的Base64 Encode的字符串
NSString* rsaEncrypyBase64 = [NSString stringWithFormat:@"%@\r%@\r%@",
@"ZNKCVpFYd4Oi2pecLhDXHh+8kWltUMLdBIBDeTvU5kWpTQ8cA1Y+7wKO3d/M8bhULYf1FhWt80Cg",
@"7e73SV5r+wSlgGWBvTIxqgTWFS4ELGzsEJpVVSlK1oXF0N2mugOURUILjeQrwn1QTcVdXXTMQ0wj",
@"50GNwnHbAwyLvsY5EUY="];
NSString* resultString = [rsaEncryptor rsaDecryptString: rsaEncrypyBase64];
NSLog(@"Decrypt Java RSA String=== %@", resultString);
// Do any additional setup after loading the view, typically from a nib.