常见加密算法总结

1. 平安散列算法

Secure Hash Algorithm,常见的算法包含了 MD5SHA1HMAC 等。

将任意长度的二进制值映射为较短的固定长度的二进制值,这个短的二进制值称为哈希值,这个算法具备不可逆、碰撞低等个性。同时该类算法能够用作数字签名,用来证实某个信息的确是由某个人收回的,同时能够保障信息没有被批改。

实际上,简略来说,这种算法有两个个性:
A) 不同的输出肯定得出不同的 hash 值;
B) 无奈从 hash 值倒推出原来的输出。

2. 对称加密

symmetric-key encryption,其中常见的算法包含了 AESDES3DESRC4等。
对称加密指的是能够应用同一个密钥对内容进行加密和解密,相比非对称加密,它的特点是加/解密速度快,并且加密的内容长度简直没有限度。

3. 非对称加密

asymmetric/public-key encryption,常见的加密算法有 RSADSAECC 等。
非对称加密有两个密钥,别离为公钥和私钥,其中公钥公开给所有人,私钥永远只能本人晓得。

应用公钥加密的信息只能应用私钥解密,应用私钥加密只能应用公钥解密。前者用来传输须要窃密的信息,因为全世界只有晓得对应私钥的人才能够解密;后者用来作数字签名,因为公钥对所有人公开的,能够用来确认这个信息是否是从私钥的拥有者收回的。

平安散列算法

MD5信息摘要

MD5 Message-Digest Algorithm,一种被宽泛应用的明码散列函数,能够产生出一个128位(16字节)的散列值(hash value),用于确保信息传输残缺统一。
MD5由美国明码学家罗纳德·李维斯特(Ronald Linn Rivest)设计,于1992年公开,用以取代MD4算法。
将数据(如一段文字)运算变为另一固定长度值,是散列算法的根底原理。
1996年后被证实存在弱点,能够被加以破解,对于须要高度安全性的数据,专家个别倡议改用其余算法,如SHA-2。2004年,证实MD5算法无奈避免碰撞(collision),因而不适用于安全性认证,如SSL公开密钥认证或是数字签名等用处。
#include <CommonCrypto/CommonCrypto.h>@implementation NSData (Add)- (NSString *)md5String {    unsigned char result[CC_MD5_DIGEST_LENGTH];    CC_MD5(self.bytes, (CC_LONG)self.length, result);    NSMutableString *hash = [NSMutableString string];    for (int i = 0; i < CC_MD5_DIGEST_LENGTH; i++) {        [hash appendFormat:@"%02x", result[i]];    }    return hash;}@end

SHA家族

平安散列算法(英语:Secure Hash Algorithm,缩写为SHA)是一个明码散列函数家族,是FIPS所认证的平安散列算法。
能计算出一个数字音讯所对应到的,长度固定的字符串(又称音讯摘要)的算法。且若输出的音讯不同,它们对应到不同字符串的机率很高。
SHA家族的算法,由美国国家安全局(NSA)所设计,并由美国国家标准与技术研究院(NIST)公布,是美国的政府规范,其别离是:

SHA-0:1993年公布,过后称做平安散列规范(Secure Hash Standard),公布之后很快就被NSA撤回,是SHA-1的前身。
SHA-1:1995年公布,SHA-1在许多平安协定中广为应用,包含TLS和SSL、PGP、SSH、S/MIME和IPsec,曾被视为是MD5(更早之前被广为应用的散列函数)的后继者。但SHA-1的安全性在2000年当前曾经不被大多数的加密场景所承受。
2017年荷兰密码学钻研小组CWI和Google正式发表攻破了SHA-1。

SHA-2:2001年公布,包含SHA-224、SHA-256、SHA-384、SHA-512、SHA-512/224、SHA-512/256。
尽管至今尚未呈现对SHA-2无效的攻打,它的算法跟SHA-1基本上依然类似;因而有些人开始倒退其余代替的散列算法。

SHA-3:2015年正式公布,SHA-3并不是要取代SHA-2,因为SHA-2目前并没有呈现显著的弱点。
因为对MD5呈现胜利的破解,以及对SHA-0和SHA-1呈现实践上破解的办法,NIST感觉须要一个与之前算法不同的,可替换的加密散列算法,也就是当初的SHA-3。

#include <CommonCrypto/CommonCrypto.h>@implementation NSData (Add)- (NSString *)sha1String {    unsigned char result[CC_SHA1_DIGEST_LENGTH];    CC_SHA1(self.bytes, (CC_LONG)self.length, result);    NSMutableString *hash = [NSMutableString stringWithCapacity:CC_SHA1_DIGEST_LENGTH * 2];    for (int i = 0; i < CC_SHA1_DIGEST_LENGTH; i++) {        [hash appendFormat:@"%02x", result[i]];    }    return hash;}- (NSString *)sha224String {    unsigned char result[CC_SHA224_DIGEST_LENGTH];    CC_SHA224(self.bytes, (CC_LONG)self.length, result);    NSMutableString *hash = [NSMutableString                             stringWithCapacity:CC_SHA224_DIGEST_LENGTH * 2];    for (int i = 0; i < CC_SHA224_DIGEST_LENGTH; i++) {        [hash appendFormat:@"%02x", result[i]];    }    return hash;}- (NSString *)sha256String {    unsigned char result[CC_SHA256_DIGEST_LENGTH];    CC_SHA256(self.bytes, (CC_LONG)self.length, result);    NSMutableString *hash = [NSMutableString                             stringWithCapacity:CC_SHA256_DIGEST_LENGTH * 2];    for (int i = 0; i < CC_SHA256_DIGEST_LENGTH; i++) {        [hash appendFormat:@"%02x", result[i]];    }    return hash;}- (NSString *)sha384String {    unsigned char result[CC_SHA384_DIGEST_LENGTH];    CC_SHA384(self.bytes, (CC_LONG)self.length, result);    NSMutableString *hash = [NSMutableString                             stringWithCapacity:CC_SHA384_DIGEST_LENGTH * 2];    for (int i = 0; i < CC_SHA384_DIGEST_LENGTH; i++) {        [hash appendFormat:@"%02x", result[i]];    }    return hash;}- (NSString *)sha512String {    unsigned char result[CC_SHA512_DIGEST_LENGTH];    CC_SHA512(self.bytes, (CC_LONG)self.length, result);    NSMutableString *hash = [NSMutableString                             stringWithCapacity:CC_SHA512_DIGEST_LENGTH * 2];    for (int i = 0; i < CC_SHA512_DIGEST_LENGTH; i++) {        [hash appendFormat:@"%02x", result[i]];    }    return hash;}@end
HMAC加密算法是一种平安的基于加密hash函数和共享密钥的音讯认证协定. 它能够无效地避免数据在传输过程中被截获和篡改,保护了数据的完整性、可靠性和安全性. HMAC加密算法是一种基于密钥的报文完整性的验证办法,其安全性是建设在Hash加密算法根底上的

HMAC 维基百科

#include <CommonCrypto/CommonCrypto.h>@implementation NSData (Add)- (NSString *)hmacStringUsingAlg:(CCHmacAlgorithm)alg withKey:(NSString *)key {    size_t size;    switch (alg) {        case kCCHmacAlgMD5: size = CC_MD5_DIGEST_LENGTH; break;        case kCCHmacAlgSHA1: size = CC_SHA1_DIGEST_LENGTH; break;        case kCCHmacAlgSHA224: size = CC_SHA224_DIGEST_LENGTH; break;        case kCCHmacAlgSHA256: size = CC_SHA256_DIGEST_LENGTH; break;        case kCCHmacAlgSHA384: size = CC_SHA384_DIGEST_LENGTH; break;        case kCCHmacAlgSHA512: size = CC_SHA512_DIGEST_LENGTH; break;        default: return nil;    }    unsigned char result[size];    const char *cKey = [key cStringUsingEncoding:NSUTF8StringEncoding];    CCHmac(alg, cKey, strlen(cKey), self.bytes, self.length, result);    NSMutableString *hash = [NSMutableString stringWithCapacity:size * 2];    for (int i = 0; i < size; i++) {        [hash appendFormat:@"%02x", result[i]];    }    return hash;}- (NSString *)hmacMD5StringWithKey:(NSString *)key {    return [self hmacStringUsingAlg:kCCHmacAlgMD5 withKey:key];}- (NSString *)hmacSHA1StringWithKey:(NSString *)key {    return [self hmacStringUsingAlg:kCCHmacAlgSHA1 withKey:key];}- (NSString *)hmacSHA224StringWithKey:(NSString *)key {    return [self hmacStringUsingAlg:kCCHmacAlgSHA224 withKey:key];}- (NSString *)hmacSHA256StringWithKey:(NSString *)key {    return [self hmacStringUsingAlg:kCCHmacAlgSHA256 withKey:key];}- (NSString *)hmacSHA384StringWithKey:(NSString *)key {    return [self hmacStringUsingAlg:kCCHmacAlgSHA384 withKey:key];}- (NSString *)hmacSHA512StringWithKey:(NSString *)key {    return [self hmacStringUsingAlg:kCCHmacAlgSHA512 withKey:key];}@end

对称加密

AES

AES256是美国NIST在几种加密算法比赛当选进去的对称加密算法,是用于取代DES的,原名为Rijndael加密法,破解的报道绝对少些。

如果单纯从密码学上讲,要实现与AES256相当的加密强度,RSA加密算法长度要达到16384位,另外RSA1024目前曾经不被认为是平安的加密算法了。

#include <CommonCrypto/CommonCrypto.h>@implementation NSData (Add)- (NSData *)AES256EncryptWithKey:(NSData *)key iv:(NSData *)iv {    if (key.length != 16 && key.length != 24 && key.length != 32) return nil;    if (iv.length != 16 && iv.length != 0) return nil;        NSData *result = nil;    size_t bufferSize = self.length + kCCBlockSizeAES128;    void *buffer = malloc(bufferSize);    if (!buffer) return nil;        size_t encryptedSize = 0;    CCCryptorStatus cryptStatus = CCCrypt(kCCEncrypt,                                          kCCAlgorithmAES,                                          kCCOptionPKCS7Padding,                                          key.bytes,                                          key.length,                                          iv.bytes,                                          self.bytes,                                          self.length,                                          buffer,                                          bufferSize,                                          &encryptedSize);    if (cryptStatus == kCCSuccess) {        result = [[NSData alloc] initWithBytes:buffer length:(NSUInteger)encryptedSize];        free(buffer);        return result;    } else {        free(buffer);        return nil;    }}- (NSData *)AES256DecryptWithKey:(NSData *)key iv:(NSData *)iv {    if (key.length != 16 && key.length != 24 && key.length != 32) return nil;    if (iv.length != 16 && iv.length != 0) return nil;        NSData *result = nil;    size_t bufferSize = self.length + kCCBlockSizeAES128;    void *buffer = malloc(bufferSize);    if (!buffer) return nil;        size_t encryptedSize = 0;    CCCryptorStatus cryptStatus = CCCrypt(kCCDecrypt,                                          kCCAlgorithmAES,                                          kCCOptionPKCS7Padding,                                          key.bytes,                                          key.length,                                          iv.bytes,                                          self.bytes,                                          self.length,                                          buffer,                                          bufferSize,                                          &encryptedSize);    if (cryptStatus == kCCSuccess) {        result = [[NSData alloc] initWithBytes:buffer length:(NSUInteger)encryptedSize];        free(buffer);        return result;    } else {        free(buffer);        return nil;    }}@end

RC4

Rivest Cipher 4是一种流加密算法,密钥长度可变。它加解密应用雷同的密钥,因而也属于对称加密算法。
#ifndef CX_SWAP // swap two value#define CX_SWAP(_a_, _b_) do {__typeof__(_a_) _tmp_ = (_a_); (_a_) = (_b_); (_b_) = (_tmp_); } while(0)#endif@implementation NSString (CX)- (NSString *)rc4WithKey:(NSString *)key {    int j = 0;    unichar res[self.length];    const unichar *buffer = res;    unsigned char s[256];    for (int i = 0; i < 256; i++) {        s[i] = i;    }    for (int i = 0; i < 256; i++) {        j = (j + s[i] + [key characterAtIndex:(i%key.length)])%256;        CX_SWAP(s[i], s[j]);    }    int i = j = 0;    for (int y = 0; y < self.length; y++) {        i = (i + 1) % 256;        j = (j + 1) % 256;        CX_SWAP(s[i], s[j]);                unsigned char f = [self characterAtIndex:y] ^ s[ (s[i] + s[j]) % 256 ];        res[y] = f;    }    return [NSString stringWithCharacters:buffer length:self.length];}@end

以下代码参考自 Objective-C-RSA

@interface RSA : NSObject// return base64 encoded string+ (NSString *)encryptString:(NSString *)str publicKey:(NSString *)pubKey;// return raw data+ (NSData *)encryptData:(NSData *)data publicKey:(NSString *)pubKey;// return base64 encoded string+ (NSString *)encryptString:(NSString *)str privateKey:(NSString *)privKey;// return raw data+ (NSData *)encryptData:(NSData *)data privateKey:(NSString *)privKey;// decrypt base64 encoded string, convert result to string(not base64 encoded)+ (NSString *)decryptString:(NSString *)str publicKey:(NSString *)pubKey;+ (NSData *)decryptData:(NSData *)data publicKey:(NSString *)pubKey;+ (NSString *)decryptString:(NSString *)str privateKey:(NSString *)privKey;+ (NSData *)decryptData:(NSData *)data privateKey:(NSString *)privKey;@end
#import "RSA.h"#import <Security/Security.h>@implementation RSAstatic NSString *base64_encode_data(NSData *data){    data = [data base64EncodedDataWithOptions:0];    NSString *ret = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];    return ret;}static NSData *base64_decode(NSString *str){    NSData *data = [[NSData alloc] initWithBase64EncodedString:str options:NSDataBase64DecodingIgnoreUnknownCharacters];    return data;}+ (NSData *)stripPublicKeyHeader:(NSData *)d_key{    // Skip ASN.1 public key header    if (d_key == nil) return(nil);        unsigned long len = [d_key length];    if (!len) return(nil);        unsigned char *c_key = (unsigned char *)[d_key bytes];    unsigned int  idx    = 0;        if (c_key[idx++] != 0x30) return(nil);        if (c_key[idx] > 0x80) idx += c_key[idx] - 0x80 + 1;    else idx++;        // PKCS #1 rsaEncryption szOID_RSA_RSA    static unsigned char seqiod[] =    { 0x30,   0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,        0x01, 0x05, 0x00 };    if (memcmp(&c_key[idx], seqiod, 15)) return(nil);        idx += 15;        if (c_key[idx++] != 0x03) return(nil);        if (c_key[idx] > 0x80) idx += c_key[idx] - 0x80 + 1;    else idx++;        if (c_key[idx++] != '\0') return(nil);        // Now make a new NSData from this buffer    return([NSData dataWithBytes:&c_key[idx] length:len - idx]);}//credit: http://hg.mozilla.org/services/fx-home/file/tip/Sources/NetworkAndStorage/CryptoUtils.m#l1036+ (NSData *)stripPrivateKeyHeader:(NSData *)d_key{    // Skip ASN.1 private key header    if (d_key == nil) return(nil);    unsigned long len = [d_key length];    if (!len) return(nil);    unsigned char *c_key = (unsigned char *)[d_key bytes];    unsigned int  idx    = 22; //magic byte at offset 22    if (0x04 != c_key[idx++]) return nil;    //calculate length of the key    unsigned int c_len = c_key[idx++];    int det = c_len & 0x80;    if (!det) {        c_len = c_len & 0x7f;    } else {        int byteCount = c_len & 0x7f;        if (byteCount + idx > len) {            //rsa length field longer than buffer            return nil;        }        unsigned int accum = 0;        unsigned char *ptr = &c_key[idx];        idx += byteCount;        while (byteCount) {            accum = (accum << 8) + *ptr;            ptr++;            byteCount--;        }        c_len = accum;    }    // Now make a new NSData from this buffer    return [d_key subdataWithRange:NSMakeRange(idx, c_len)];}+ (SecKeyRef)addPublicKey:(NSString *)key{    NSRange spos = [key rangeOfString:@"-----BEGIN PUBLIC KEY-----"];    NSRange epos = [key rangeOfString:@"-----END PUBLIC KEY-----"];    if(spos.location != NSNotFound && epos.location != NSNotFound){        NSUInteger s = spos.location + spos.length;        NSUInteger e = epos.location;        NSRange range = NSMakeRange(s, e-s);        key = [key substringWithRange:range];    }    key = [key stringByReplacingOccurrencesOfString:@"\r" withString:@""];    key = [key stringByReplacingOccurrencesOfString:@"\n" withString:@""];    key = [key stringByReplacingOccurrencesOfString:@"\t" withString:@""];    key = [key stringByReplacingOccurrencesOfString:@" "  withString:@""];        // This will be base64 encoded, decode it.    NSData *data = base64_decode(key);    data = [RSA stripPublicKeyHeader:data];    if(!data){        return nil;    }    //a tag to read/write keychain storage    NSString *tag = @"RSAUtil_PubKey";    NSData *d_tag = [NSData dataWithBytes:[tag UTF8String] length:[tag length]];        // Delete any old lingering key with the same tag    NSMutableDictionary *publicKey = [[NSMutableDictionary alloc] init];    [publicKey setObject:(__bridge id) kSecClassKey forKey:(__bridge id)kSecClass];    [publicKey setObject:(__bridge id) kSecAttrKeyTypeRSA forKey:(__bridge id)kSecAttrKeyType];    [publicKey setObject:d_tag forKey:(__bridge id)kSecAttrApplicationTag];    SecItemDelete((__bridge CFDictionaryRef)publicKey);        // Add persistent version of the key to system keychain    [publicKey setObject:data forKey:(__bridge id)kSecValueData];    [publicKey setObject:(__bridge id) kSecAttrKeyClassPublic forKey:(__bridge id)     kSecAttrKeyClass];    [publicKey setObject:[NSNumber numberWithBool:YES] forKey:(__bridge id)     kSecReturnPersistentRef];        CFTypeRef persistKey = nil;    OSStatus status = SecItemAdd((__bridge CFDictionaryRef)publicKey, &persistKey);    if (persistKey != nil){        CFRelease(persistKey);    }    if ((status != noErr) && (status != errSecDuplicateItem)) {        return nil;    }    [publicKey removeObjectForKey:(__bridge id)kSecValueData];    [publicKey removeObjectForKey:(__bridge id)kSecReturnPersistentRef];    [publicKey setObject:[NSNumber numberWithBool:YES] forKey:(__bridge id)kSecReturnRef];    [publicKey setObject:(__bridge id) kSecAttrKeyTypeRSA forKey:(__bridge id)kSecAttrKeyType];        // Now fetch the SecKeyRef version of the key    SecKeyRef keyRef = nil;    status = SecItemCopyMatching((__bridge CFDictionaryRef)publicKey, (CFTypeRef *)&keyRef);    if(status != noErr){        return nil;    }    return keyRef;}+ (SecKeyRef)addPrivateKey:(NSString *)key{    NSRange spos;    NSRange epos;    spos = [key rangeOfString:@"-----BEGIN RSA PRIVATE KEY-----"];    if(spos.length > 0){        epos = [key rangeOfString:@"-----END RSA PRIVATE KEY-----"];    }else{        spos = [key rangeOfString:@"-----BEGIN PRIVATE KEY-----"];        epos = [key rangeOfString:@"-----END PRIVATE KEY-----"];    }    if(spos.location != NSNotFound && epos.location != NSNotFound){        NSUInteger s = spos.location + spos.length;        NSUInteger e = epos.location;        NSRange range = NSMakeRange(s, e-s);        key = [key substringWithRange:range];    }    key = [key stringByReplacingOccurrencesOfString:@"\r" withString:@""];    key = [key stringByReplacingOccurrencesOfString:@"\n" withString:@""];    key = [key stringByReplacingOccurrencesOfString:@"\t" withString:@""];    key = [key stringByReplacingOccurrencesOfString:@" "  withString:@""];    // This will be base64 encoded, decode it.    NSData *data = base64_decode(key);    data = [RSA stripPrivateKeyHeader:data];    if(!data){        return nil;    }    //a tag to read/write keychain storage    NSString *tag = @"RSAUtil_PrivKey";    NSData *d_tag = [NSData dataWithBytes:[tag UTF8String] length:[tag length]];    // Delete any old lingering key with the same tag    NSMutableDictionary *privateKey = [[NSMutableDictionary alloc] init];    [privateKey setObject:(__bridge id) kSecClassKey forKey:(__bridge id)kSecClass];    [privateKey setObject:(__bridge id) kSecAttrKeyTypeRSA forKey:(__bridge id)kSecAttrKeyType];    [privateKey setObject:d_tag forKey:(__bridge id)kSecAttrApplicationTag];    SecItemDelete((__bridge CFDictionaryRef)privateKey);    // Add persistent version of the key to system keychain    [privateKey setObject:data forKey:(__bridge id)kSecValueData];    [privateKey setObject:(__bridge id) kSecAttrKeyClassPrivate forKey:(__bridge id)     kSecAttrKeyClass];    [privateKey setObject:[NSNumber numberWithBool:YES] forKey:(__bridge id)     kSecReturnPersistentRef];    CFTypeRef persistKey = nil;    OSStatus status = SecItemAdd((__bridge CFDictionaryRef)privateKey, &persistKey);    if (persistKey != nil){        CFRelease(persistKey);    }    if ((status != noErr) && (status != errSecDuplicateItem)) {        return nil;    }    [privateKey removeObjectForKey:(__bridge id)kSecValueData];    [privateKey removeObjectForKey:(__bridge id)kSecReturnPersistentRef];    [privateKey setObject:[NSNumber numberWithBool:YES] forKey:(__bridge id)kSecReturnRef];    [privateKey setObject:(__bridge id) kSecAttrKeyTypeRSA forKey:(__bridge id)kSecAttrKeyType];    // Now fetch the SecKeyRef version of the key    SecKeyRef keyRef = nil;    status = SecItemCopyMatching((__bridge CFDictionaryRef)privateKey, (CFTypeRef *)&keyRef);    if(status != noErr){        return nil;    }    return keyRef;}/* START: Encryption & Decryption with RSA private key */+ (NSData *)encryptData:(NSData *)data withKeyRef:(SecKeyRef) keyRef isSign:(BOOL)isSign {    const uint8_t *srcbuf = (const uint8_t *)[data bytes];    size_t srclen = (size_t)data.length;        size_t block_size = SecKeyGetBlockSize(keyRef) * sizeof(uint8_t);    void *outbuf = malloc(block_size);    size_t src_block_size = block_size - 11;        NSMutableData *ret = [[NSMutableData alloc] init];    for(int idx=0; idx<srclen; idx+=src_block_size){        //NSLog(@"%d/%d block_size: %d", idx, (int)srclen, (int)block_size);        size_t data_len = srclen - idx;        if(data_len > src_block_size){            data_len = src_block_size;        }                size_t outlen = block_size;        OSStatus status = noErr;                if (isSign) {            status = SecKeyRawSign(keyRef,                                   kSecPaddingPKCS1,                                   srcbuf + idx,                                   data_len,                                   outbuf,                                   &outlen                                   );        } else {            status = SecKeyEncrypt(keyRef,                                   kSecPaddingPKCS1,                                   srcbuf + idx,                                   data_len,                                   outbuf,                                   &outlen                                   );        }        if (status != 0) {            NSLog(@"SecKeyEncrypt fail. Error Code: %d", status);            ret = nil;            break;        }else{            [ret appendBytes:outbuf length:outlen];        }    }        free(outbuf);    CFRelease(keyRef);    return ret;}+ (NSString *)encryptString:(NSString *)str privateKey:(NSString *)privKey{    NSData *data = [RSA encryptData:[str dataUsingEncoding:NSUTF8StringEncoding] privateKey:privKey];    NSString *ret = base64_encode_data(data);    return ret;}+ (NSData *)encryptData:(NSData *)data privateKey:(NSString *)privKey{    if(!data || !privKey){        return nil;    }    SecKeyRef keyRef = [RSA addPrivateKey:privKey];    if(!keyRef){        return nil;    }    return [RSA encryptData:data withKeyRef:keyRef isSign:YES];}+ (NSData *)decryptData:(NSData *)data withKeyRef:(SecKeyRef) keyRef{    const uint8_t *srcbuf = (const uint8_t *)[data bytes];    size_t srclen = (size_t)data.length;        size_t block_size = SecKeyGetBlockSize(keyRef) * sizeof(uint8_t);    UInt8 *outbuf = malloc(block_size);    size_t src_block_size = block_size;        NSMutableData *ret = [[NSMutableData alloc] init];    for(int idx=0; idx<srclen; idx+=src_block_size){        //NSLog(@"%d/%d block_size: %d", idx, (int)srclen, (int)block_size);        size_t data_len = srclen - idx;        if(data_len > src_block_size){            data_len = src_block_size;        }                size_t outlen = block_size;        OSStatus status = noErr;        status = SecKeyDecrypt(keyRef,                               kSecPaddingNone,                               srcbuf + idx,                               data_len,                               outbuf,                               &outlen                               );        if (status != 0) {            NSLog(@"SecKeyEncrypt fail. Error Code: %d", status);            ret = nil;            break;        }else{            //the actual decrypted data is in the middle, locate it!            int idxFirstZero = -1;            int idxNextZero = (int)outlen;            for ( int i = 0; i < outlen; i++ ) {                if ( outbuf[i] == 0 ) {                    if ( idxFirstZero < 0 ) {                        idxFirstZero = i;                    } else {                        idxNextZero = i;                        break;                    }                }            }                        [ret appendBytes:&outbuf[idxFirstZero+1] length:idxNextZero-idxFirstZero-1];        }    }        free(outbuf);    CFRelease(keyRef);    return ret;}+ (NSString *)decryptString:(NSString *)str privateKey:(NSString *)privKey{    NSData *data = [[NSData alloc] initWithBase64EncodedString:str options:NSDataBase64DecodingIgnoreUnknownCharacters];    data = [RSA decryptData:data privateKey:privKey];    NSString *ret = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];    return ret;}+ (NSData *)decryptData:(NSData *)data privateKey:(NSString *)privKey{    if(!data || !privKey){        return nil;    }    SecKeyRef keyRef = [RSA addPrivateKey:privKey];    if(!keyRef){        return nil;    }    return [RSA decryptData:data withKeyRef:keyRef];}/* END: Encryption & Decryption with RSA private key *//* START: Encryption & Decryption with RSA public key */+ (NSString *)encryptString:(NSString *)str publicKey:(NSString *)pubKey{    NSData *data = [RSA encryptData:[str dataUsingEncoding:NSUTF8StringEncoding] publicKey:pubKey];    NSString *ret = base64_encode_data(data);    return ret;}+ (NSData *)encryptData:(NSData *)data publicKey:(NSString *)pubKey{    if(!data || !pubKey){        return nil;    }    SecKeyRef keyRef = [RSA addPublicKey:pubKey];    if(!keyRef){        return nil;    }    return [RSA encryptData:data withKeyRef:keyRef isSign:NO];}+ (NSString *)decryptString:(NSString *)str publicKey:(NSString *)pubKey{    NSData *data = [[NSData alloc] initWithBase64EncodedString:str options:NSDataBase64DecodingIgnoreUnknownCharacters];    data = [RSA decryptData:data publicKey:pubKey];    NSString *ret = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];    return ret;}+ (NSData *)decryptData:(NSData *)data publicKey:(NSString *)pubKey{    if(!data || !pubKey){        return nil;    }    SecKeyRef keyRef = [RSA addPublicKey:pubKey];    if(!keyRef){        return nil;    }    return [RSA decryptData:data withKeyRef:keyRef];}/* END: Encryption & Decryption with RSA public key */

CRC

CRC即循环冗余校验码(Cyclic Redundancy Check [1] ):是数据通信畛域中最罕用的一种查错校验码,其特色是信息字段和校验字段的长度能够任意选定。

循环冗余查看(CRC)是一种数据传输检错性能,对数据进行多项式计算,并将失去的后果附在帧的前面,接管设施也执行相似的算法,以保障数据传输的正确性和完整性。

#import <zlib.h>ZEXTERN uLong ZEXPORT crc32   OF((uLong crc, const Bytef *buf, uInt len));/*     Update a running CRC-32 with the bytes buf[0..len-1] and return the   updated CRC-32.  If buf is Z_NULL, this function returns the required   initial value for the crc.  Pre- and post-conditioning (one's complement) is   performed within this function so it shouldn't be done by the application.   Usage example:     uLong crc = crc32(0L, Z_NULL, 0);     while (read_buffer(buffer, length) != EOF) {       crc = crc32(crc, buffer, length);     }     if (crc != original_crc) error();*/

数字签名

假如,咱们有一段受权文本,须要公布,为了避免中途篡改文本内容,保障文本的完整性,以及文本是由指定的权限狗发的。首先,先将文本内容通过摘要算法,失去摘要,再用权限狗的私钥对摘要进行加密失去密文,将源文本、密文、和私钥对应的公钥一并公布即可。那么如何验证呢?

验证方首先查看公钥是否是权限狗的,而后用公钥对密文进行解密失去摘要,将文本用同样的摘要算法失去摘要,两个摘要进行比对,如果相等那么一切正常。这个过程只有有一步出问题就视为有效。

数字签名能够疾速验证文本的完整性和合法性,已广泛应用于各个领域。了解了数字签名当前,咱们进一步来看什么是数字证书。

明码攻打类型

① 惟密文攻打

(Ciphtext-only attack)

在惟密文攻打中,明码剖析者晓得明码算法,但仅能依据截获的密文进行剖析,以得出明文或密钥。因为明码剖析者所能利用的数据资源仅为密文,这是对明码剖析者最不利的状况。

②已知明文攻打

(Plaintext-known attack)

已知明文攻打是指明码剖析者除了有截获的密文外,还有一些已知的“明文—密文对”来破译明码。明码剖析者的工作指标是推出用来加密的密钥或某种 算法,这种算法能够对用该密钥加密的任何新的音讯进行解密。

③ 抉择明文攻打

(Chosen-plaintext attack)

抉择明文攻打是指明码剖析者不仅可失去一些“明文—密文对”,还能够抉择被加密的明文,并取得相应的密文。这时明码剖析者可能抉择特定的明文数据块去加密,并比拟明文和对应的密文,已剖析和发现更多的与密钥相干的信息。

明码剖析者的工作指标也是推出用来加密的密钥或某种算法,该算法能够对用该密钥加密的任何新的音讯进行解密。

④ 抉择密文攻打

(Chosen—ciphenext attack)

抉择密文攻打是指明码剖析者能够抉择一些密文,并失去相应的明文。明码剖析者的工作指标是推出密钥。这种 明码剖析多用于攻打 公钥明码体制。

iOS证书签名原理剖析

通常咱们所说的签名就是数字签名,它是基于非对称加密算法实现的。对称加密是通过同一份密钥加密和解密数据,而非对称加密则有两份密钥,别离是公钥和私钥,用公钥加密的数据,要用私钥能力解密;用私钥加密的数据,要用公钥能力解密。这里的非对称加密就是咱们所熟知的RSA,要理解RSA背地的数学原理能够参考RSA算法原理(一)(二)

1.从App Store装置App

这个过程的签名形式绝对简略一些。苹果官网生成一对公私钥,在苹果手机外面内置一个公钥,私钥由苹果后盾保留,咱们传App上AppStore时,苹果后盾用私钥对App数据值的MD5值进行签名,iOS零碎下载这个App后,用公钥验证这个签名,若签名正确,这个App必定由苹果后盾认证的,并且没有被批改过,也就达到了苹果的需要:保障装置的每一个App都是通过苹果认证容许的。

2.其余形式装置APP

在理论工作当中,咱们还有一些其余的形式把APP装置到手机上:
开发App时能够间接把开发中的利用装置进手机调试;
In-House企业外部散发,能够间接装置企业证书签名后的App;
AD-Hoc相当于企业散发的限度版,限度装置设施数量,较少用。

苹果对这几种形式装置的管制过程就变得复杂了,即要保障APP的装置时通过苹果认证的,又要管制APP不能被轻易装置到其余设施上,以及一些其余的权限,为了达到这样的目标,苹果采纳的流程大抵是这个样子

  • 1、在Mac上生成一对公私钥,这里称公钥M,私钥M。
  • 2、苹果本人有固定的一对公私钥,跟下面AppStore例子一样,私钥在苹果后盾,公钥内置在每个iOS设施上,这里称为公钥A,私钥A。
  • 3、把公钥M上传到苹果后盾,用苹果后盾里的私钥A去签名公钥M。失去一份数据蕴含了公钥M以及其签名(也就是公钥的HASH值),把这份数据称为证书。
  • 4、在开发时,编译完一个App后,用本地的私钥M对这个App进行签名,同时把第三步失去的证书一起打包进App里,装置到手机。
  • 5、在装置时,iOS零碎获得证书,通过零碎内置的公钥A,去验证证书的数字签名是否正确。

验证证书确保公钥M是苹果认证过的,再用公钥M去验证App的签名,这里就间接验证了这个App的装置行为是否通过苹果官网容许。(这里只验证装置行为,不验证App是否被改变,因为开发阶段App内容总是一直变动的,苹果不须要管)。

最终流程:

上述流程只解决了下面第一个需要,也就是须要通过苹果容许才能够装置,还未解决第二个防止被滥用的问题。怎么解决呢?苹果加了两个限度,一是限度在苹果后盾注册过的设施才能够装置;二是限度签名只能针对某一个具体的App。
那么它到底是怎么增加这两个限度的呢?在上述第三步,苹果用私钥A签名咱们的本地公钥M时,实际上除了签名本地公钥M外,还能够加上有限多数据,这些数据都能够保障是通过苹果官网认证的,不会有被篡改的可能。

能够把容许装置的设施ID列表和App对应的AppID等数据,都在第三步这里跟公钥M一起组成证书,再用苹果私钥A对这个证书签名。在最初第5步验证时就能够拿到设施ID列表,判断以后设施是否符合要求。依据数字签名的原理,只有数字签名通过验证,第5步这里的设施IDs/AppID/公钥M就都是通过苹果认证的,无奈被批改,苹果就能够限度可装置的设施和APP,防止滥用。

到这里这个证书曾经变得很简单了,有很多额定信息,实际上除了设施ID/AppID,还有其余信息也须要在这里用苹果签名,像App里iCloud、push、后盾运行 等权限苹果都想管制,苹果把这些权限开关统称为Entitlements,它也须要通过签名去受权。
实际上一个证书原本就有规定的格局标准,下面咱们把各种额定的信息塞入证书里是不适合的,于是苹果另外搞了一个货色,叫Provisioning Profile,一个Provisioning Profile里就蕴含了证书以及上述提到的所有额定信息,以及所有信息的签名。

所以,就成这样了:

在 Mac 上生成一对公私钥,这里称为公钥M,私钥M。
苹果本人有固定的一对公私钥,跟下面 AppStore 例子一样,私钥在苹果后盾,公钥在每个iOS设施上。这里称为公钥A,私钥A。A:Apple
把公钥M传到苹果后盾,用苹果后盾里的私钥A去签名公钥M。失去一份数据蕴含了公钥M以及其签名,把这份数据称为证书。
在苹果后盾申请AppID,配置好设施ID列表和APP可应用的权限,再加上第3步的证书,组成的数据用私钥A签名,把数据和签名一起组成一个Provisioning Profile文件,下载到本地Mac开发机。
在开发时,编译完一个APP后,用本地的私钥M对这个APP进行签名,同时把第4步失去的Provisioning Profile文件打包进APP里,文件名为 embedded.mobileprovision,把APP装置到手机上。
在装置时,iOS零碎获得证书,通过零碎内置的公钥A,去验证 embedded.mobileprovision的数字签名是否正确,外面的证书签名也会再验一遍。
确保了embedded.mobileprovision里的数据都是苹果受权当前,就能够取出外面的数据,做各种验证,包含用公钥M验证APP签名,验证设施ID是否在ID列表上,AppID是否对应得上,权限开关是否跟APP里的Entitlements对应等。

开发者证书从签名到认证最终苹果采纳的流程大抵是这样,还有一些细节像证书有效期/证书类型等就不细说了。

下面的步骤对应到咱们平时具体的操作和概念是这样的:
  • 第1步 对应的是keychain里的“从证书颁发机构申请证书”,这里就本地生成了一对公私钥,保留的CertificateSigningRequest就是公钥,私钥保留在本地电脑里。
  • 第2步 苹果本人解决,咱们不必管。
  • 第3步 对应把CertificateSigningRequest传到苹果后盾生成证书,并下载到本地。这时本地有两个证书,一个是第1步生成的,一个是这里下载回来的,keychain会把这两个证书关联起来,因为它们的公私钥是对应的,在Xcode抉择下载回来的证书的时,实际上会找到keychain外面对应的私钥去签名。这里私钥只有生成它的这台Mac才有,如果别的Mac也要编译签名这个App,把私钥导出给其余Mac应用,在keychain外面导出私钥,就会存成.p12文件,其余Mac关上后就导入私钥。
  • 第4步 都是在苹果网站上操作,配置AppID、权限、设施等,最初下载 Provisioning Profile文件。
  • 第5步 Xcode会通过第3步下载回来的证书(存着本地公钥),在本地找到对应的私钥(第1步生成的),用本地私钥去签名App,并把Provisioning Profile文件命名为embedded.mobileprovision一起打包进去。这里对App的签名数据保留分为两局部,Mach-O可执行文件会把签名间接写入这个文件里,其余资源文件则会保留在_CodeSignature目录下。
  • 第6、7步 的打包和验证都是 Xcode 和 iOS 零碎主动做的事。

几个概念:

  • 证书:内容是公钥或私钥,由其余机构对其签名组成的数据包。
  • Entitlements:蕴含了App权限开关列表。
  • CertificateSigningRequest:本地公钥。
  • .p12:本地私钥,能够导入到其余电脑。
  • Provisioning Profile:蕴含了 证书/Entitlements 等数据,并由苹果后盾私钥签名的数据包。
其余公布形式
后面以开发包为例子说了签名和验证的流程,另外两种形式In-House企业签名和AD-Hoc流程也是差不多的,只是企业签名不限度装置的设施数,另外须要用户在iOS零碎设置上手动点击信赖这个企业能力通过验证。
而AppStore的签名验证形式有些不一样,后面咱们说到最简略的签名形式,苹果在后盾间接用私钥签名App就能够了,实际上苹果的确是这样做的,如果去下载一个AppStore的安装包,会发现它外面是没有embedded.mobileprovision文件的,也就是它装置和启动的流程是不依赖这个文件,验证流程也就跟上述几种类型不一样了。

因为上传到AppStore的包苹果会从新对内容加密,原来的本地私钥签名就没有用了,须要从新签名,从AppStore下载的包苹果也并不打算管制它的有效期,不须要内置一个embedded.mobileprovision去做校验,间接在苹果用后盾的私钥从新签名,iOS装置时用本地公钥验证App签名就能够了。

那为什么公布AppStore的包还是要跟开发版一样搞各种证书和Provisioning Profile,因为苹果想做对立治理,Provisioning Profile里蕴含一些权限管制,AppID 的测验等,苹果不想在上传AppStore 包时从新用另一种协定做一遍这些验证,就不如对立把这部分放在 Provisioning Profile里,上传AppStore时只有用同样的流程验证这个 Provisioning Profile是否非法就能够了。

所以 App 上传到AppStore后,就跟你的 证书 / Provisioning Profile 都没有关系了,无论他们是否过期或被破除,都不会影响AppStore 上的安装包。

ipa的组成

iOS程序最终都会以.ipa文件导出,先来理解一下ipa文件的构造:


事实上,ipa文件只是一个zip包,能够应用如下命令解压:

/usr/bin/unzip -q xxx.ipa -d <destination>

解压后,失去上图的Payload目录,上面是个子目录,其中的内容如下:

  1. 资源文件,例如图片、html、等等。
  2. _CodeSignature/CodeResources。这是一个plist文件,可用文本查看,其中的内容就是是程序包中(不包含Frameworks)所有文件的签名。留神这里是所有文件。意味着你的程序一旦签名,就不能更改其中任何的货色,包含资源文件和可执行文件自身。iOS零碎会查看这些签名。
  3. 可执行文件。此文件跟资源文件一样须要签名。
  4. 一个mobileprovision文件.打包的时候应用的,从MC上生成的。
  5. Frameworks。程序援用的非零碎自带的Frameworks,每个Frameworks其实就是一个app,其中的构造应该和app差不多,也蕴含签名信息CodeResources文件

相干的程序和命令

个别咱们会用Xcode自带的archive性能来打包ipa和签名,实际上xcode只不过是调用了一些内部程序实现了工作,如果咱们有朝一日须要本人实现自动化的签名流程,就须要理解到底相干的程序和命令有哪些。

用上面命令,列出零碎中可用于签名的无效证书:

/usr/bin/security find-identity -v -p codesigning 1) E056929276F94152F3FDF0EA84BD2B06396F2DDD "iPhone Developer: Liang Ding (2U967A2YJ6)" 2) 7C608F653A989E95E1A4D303EC4E6625D95EEB42 "iPhone Distribution: Liang Ding (7XPNRZE9TC)" 2 valid identities found
能够看到这个命令列出了一个字符串标示的证书名称,如:iPhone Developer: Liang Ding (2U967A2YJ6)。这个名称前面会用到的。

应用如下命令对xxx.app目录签名,codesign程序会主动将其中的文件都签名,(Frameworks不会主动签):

/user/bin/codesign -fs "iPhone Developer: Liang Ding (2U967A2YJ6)" --no-strict Payload/xxx.app
对于每个Framework,也须要应用这个命令签名,下面说了Framework的构造跟app其实差不多,所以签名命令相似。这个命令会主动找到证书相干的私钥。-f示意对于曾经签名的app强制重签。

最初用上面命令校验签名是否非法:

/usr/bin/codesign -v xxx.app

如果没有任何输入阐明没有问题。

应用zip命令从新打包成ipa包

/usr/bin/zip -qry destination source

对app从新签名的流程

如果要设计一个自动化的重签程序,大抵须要这么个流程:

  1. 首先解压ipa
  2. 如果mobileprovision须要替换,替换
  3. 如果存在Frameworks子目录,则对.app文件夹下的所有Frameworks进行签名,在Frameworks文件夹下的.dylib.framework
  4. 对xxx.app签名
  5. 从新打包

iOS设施如何验证app是否非法

要害的几个点:

  1. 解压ipa
  2. 取出embedded.mobileprovision,通过签名校验是否被篡改过 a. 其中有几个证书的公钥,其中开发证书和公布证书用于校验签名 b. BundleId c. 受权列表
  3. 校验所有文件的签名,包含Frameworks
  4. 比对Info.plist外面的BundleId是否合乎embedded.mobileprovision文件中的