密码

原则

  1. 不允许在网络上传递明文的用户隐私信息
  2. 不允许在本地保存明文的用户隐私信息

RSA

非对称加密

RSA使用“钥匙对”对数据进行加密解密。在加密解密数据前,需要先生成公钥和私钥

公钥(public key):用于加密数据。可以公开,一般存放在数据提供方,例如iOS客户端
私钥(private key):用于解密数据。必须保密,泄露会造成安全问题

第一步:公钥、私钥的生成

iOS开发者可直接在Mac终端生成,生成公钥der文件的时候需要填写国家地区等基本信息,也可直接忽略不填。生成私钥p12文件的时候需要填写密码,这个必填而且要记住,后面会用得着。

命令如下:

// 生成1024位私钥 
openssl genrsa -out private_key.pem 1024 
// 根据私钥生成CSR文件 
openssl req -new -key private_key.pem -out rsaCertReq.csr 
// 根据私钥和CSR文件生成crt文件 
openssl x509 -req -days 3650 -in rsaCertReq.csr -signkey private_key.pem -out rsaCert.crt

// 为iOS端生成公钥der文件 
openssl x509 -outform der -in rsaCert.crt -out public_key.der

// 将私钥导出为p12文件 
openssl pkcs12 -export -out private_key.p12 -inkey private_key.pem -in rsaCert.crt
第二步:加密相关的代码

在加密加密的时候需要定义公有变量公钥和私钥

SecKeyRef _publicKey;
SecKeyRef _privateKey;

加密相关的代码

//用本地证书加载公钥
- (void)loadPublicKeyWithPath:(NSString *)derFilePath
{
    NSData *derData = [[NSData alloc] initWithContentsOfFile:derFilePath];
    if (derData.length > 0)
    {
        [self loadPublicKeyWithData:derData];
    }
    else
    {
        NSLog(@"load public key fail with path: %@", derFilePath);
    }
}

//加载公钥方法
- (void)loadPublicKeyWithData:(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);
    
    _publicKey = securityKey;
}

//将文本内容加密
- (NSString *)rsaEncryptText:(NSString *)text
{
    NSData *encryptedData = [self rsaEncryptData:[text dataUsingEncoding:NSUTF8StringEncoding]];
    NSString *base64EncryptedString = [encryptedData base64EncodedStringWithOptions:0];
    return base64EncryptedString;
}

//分段再加密数据
- (NSData *)rsaEncryptData:(NSData *)data
{
    SecKeyRef key = _publicKey;
    
    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++)
    {
        size_t 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 - 解密相关
- (void)loadPrivateKeyWithPath:(NSString *)p12FilePath password:(NSString *)p12Password
{
    NSData *data = [NSData dataWithContentsOfFile:p12FilePath];
    if (data.length > 0)
    {
        [self loadPrivateKeyWithData:data password:p12Password];
    }
    else
    {
        NSLog(@"load private key fail with path: %@", p12FilePath);
    }
}

//生成私钥
- (void)loadPrivateKeyWithData:(NSData *)p12Data password:(NSString *)p12Password
{
    SecKeyRef privateKeyRef = NULL;
    NSMutableDictionary * options = [[NSMutableDictionary alloc] init];
    
    [options setObject:p12Password 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;
        }
    }
    
    _privateKey = privateKeyRef;
    CFRelease(items);
}

//调用下面方法进行解密,最后返回一个字符串
- (NSString *)rsaDecryptText:(NSString *)text
{
    NSData *data = [[NSData alloc] initWithBase64EncodedString:text options:0];
    NSData *decryptData = [self rsaDecryptData:data];
    NSString *result = [[NSString alloc] initWithData:decryptData encoding:NSUTF8StringEncoding];
    return result;
}


//用私钥解密的方法,被上面方法调用
- (NSData *)rsaDecryptData:(NSData *)data
{
    SecKeyRef key = _privateKey;
    
    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;
}

哈希(散列)

特点:

  • 算法公开
  • 加密相同的数据,得到一样的结果
  • 加密不同的数据,得到定长的结果(32位字符,a~z,0~9)
  • 信息摘要(信息指纹),用来识别数据
  • 不能反算数据

用途:

  • 密码(服务器也不知道用户的真实密码)
  • 搜索
  • 版权

破解:

  • 目前可破解的只有MD5、SHA1
  • 散列碰撞(不同的数据,使用MD5后能得到相同的散列结果)
MD5

实现代码:

#import <CommonCrypto/CommonCrypto.h>

- (NSString *)MD5WithString:(NSString *)string {
    const char *data = string.UTF8String;
    CC_LONG len = (CC_LONG)strlen(data);
    unsigned char md[CC_MD5_DIGEST_LENGTH];
    CC_MD5(data, len, md);
    NSMutableString *md5String = [NSMutableString string];
    for (int i = 0; i < CC_MD5_DIGEST_LENGTH; ++i) {
        [md5String appendFormat:@"%02X", md[i]];
    }
    return md5String;
}

加“盐”:

static NSString *salt = @"123456";

[self MD5WithString:[password stringByAppendingString:salt]]

终端命令:

md5 -s "关键字"
md5 文件名
HMAC

给定一个密钥,对明文进行加密,并且做两次散列

#import <CommonCrypto/CommonCrypto.h>

- (NSString *)HMACWithKey:(NSString *)key string:(NSString *)string {
   const char *charKey = key.UTF8String;
   size_t keyLength = strlen(charKey);
   const char *charString = string.UTF8String;
   size_t stringLength = strlen(charString);
   unsigned char macOut[CC_MD5_DIGEST_LENGTH];
   CCHmac(kCCHmacAlgMD5, charKey, keyLength, charString, stringLength, macOut);
   NSMutableString *hmacString = [NSMutableString string];
   for (int i = 0; i < CC_MD5_DIGEST_LENGTH; ++i) {
       [hmacString appendFormat:@"%02X", macOut[i]];
   }
   return hmacString;
}

案例:

  • 服务器需保存账号、密钥、加密后的密码
  • 客户端查找密钥
  • 客户端如果没有密钥,用账号向服务器请求密钥
  • 设备锁可通过该功能实现
HMAC+MD5

客户端:

  1. 加密密码 = (原始密码 + 密钥).HMAC
  2. 客户端最终密码 = (加密密码 + 年月日时分).MD5

服务端:

  1. 从账号、密钥、加密密码表取出加密密码
  2. 服务端最终密码 = (加密密码 + 年月日时当前的分).MD5 || (加密密码 + 年月日时临近的分).MD5
  3. 判断客户端最终密码是否等同于服务端最终密码
SHA1

终端命令:

echo -n "关键字" | openssl sha1

SHA256
终端命令:

echo -n "关键字" | openssl sha256

SHA512
终端命令:

echo -n "关键字" | openssl sha512

对称加密

DES
3DES

AES

钥匙串

用AES加密
第三方库SSKeychain可以方便的调用

示例代码:

#import "SSKeychain.h"

- (void)saveWithUsername:(NSString *)username password:(NSString *)password {
    [SSKeychain setPassword:password forService:[NSBundle mainBundle].infoDictionary[@"CFBundleIdentifier"] account:username];
}

- (NSString *)passwordWithUsername:(NSString *)username {
    return [SSKeychain passwordForService:[NSBundle mainBundle].infoDictionary[@"CFBundleIdentifier"] account:username];
}

service参数:APP唯一标识,一般用BundleID.MD5

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值