iOS各种加密 Base64 MD5 DES AES RSA

一、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加密  (高级加密标准) 对称加密
    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值,也无法知道用户的密码。
  • 防止抵赖(数字签名),例如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算法的工作流程。

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 getPublicKey];
    size_t blockSize = SecKeyGetBlockSize(key);
    size_t blockCount = (size_t)ceil([data length] / (double)blockSize);
    NSMutableData *decryptedData = [[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)];
        NSData *itemData = [self deCode:buffer];// 分段解密
        if (itemData.length>0) {
            [decryptedData appendData:itemData];
        }
        
    }
    return decryptedData;
}

// 分段解密
-(NSData*)deCode:(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) - 11;
    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;
}
    
    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.

以上代码Demo

RSA参考文档

推荐阅读更多精彩内容