1. 程式人生 > >【iOS】MD5(加密)/AES/Base64加密和解密

【iOS】MD5(加密)/AES/Base64加密和解密

MD5

  • 什麼是MD5

Message Digest Algorithm MD5(中文名為訊息摘要演算法第五版)為電腦保安領域廣泛使用的一種雜湊函式,用以提供訊息的完整性保護。MD5的典型應用是對一段資訊(Message)產生資訊摘要(Message-Digest),以防止被篡改。

  • MD5的特點

1、壓縮性:任意長度的資料,算出的MD5值長度都是固定的。
2、容易計算:從原資料計算出MD5值很容易。
3、抗修改性:對原資料進行任何改動,哪怕只修改1個位元組,所得到的MD5值都有很大區別。
4、強抗碰撞:已知原資料和其MD5值,想找到一個具有相同MD5值的資料(即偽造資料)是非常困難的。
MD5的作用是讓大容量資訊在用數字簽名軟體簽署私人金鑰前被”壓縮”成一種保密的格式(就是把一個任意長度的位元組串變換成一定長的十六進位制數字串)。
比如以前在下載windows系統的時候,很多網站都會公佈一個MD值,這個是這個軟體對應的MD5值,當系統被修改了,哪怕是一個位元組,之後生成的MD5值都會有比較大的差異。
詳細的介紹,可以一步百度百科–

MD5
MD5的實現
首先需要包含標頭檔案:

#import <CommonCrypto/CommonDigest.h>
/**
 *  MD5加密
 *
 *  @param string 需要加密的字串
 *
 *  @return 返回加密後的結果
 */
+ (NSString *)md5:(NSString *)string{
    // OC 字串轉換位C字串
    const char *cStr = [string UTF8String];
    // 16位加密
    unsigned char digest[CC_MD5_DIGEST_LENGTH];
    // 1: 需要加密的C字串
// 2: 加密的字串的長度 // 3: 加密長度 CC_MD5(cStr, (CC_LONG)strlen(cStr), digest); NSMutableString *result = [NSMutableString stringWithCapacity:CC_MD5_DIGEST_LENGTH * 2]; // 32位 for (int i = 0; i < CC_MD5_DIGEST_LENGTH; i++) { [result appendFormat:@"%02X", digest[i]]; } // 返回一個32位長度的加密後的字串
return result; }

AES

  • 什麼是AES
    高階加密標準(英語:Advanced Encryption Standard,縮寫:AES),在密碼學中又稱Rijndael加密法。AES是一個對稱分組密碼演算法,旨在取代DES成為廣泛使用的標準。根據使用的密碼長度,AES最常見的有3種方案,用以適應不同的場景要求,分別是AES-128、AES-192和AES-256。— 《iOS安全之路--AES》
    AES加密資料塊分組長度必須為128位元,金鑰長度可以是128位元、192位元、256位元中的任意一個(如果資料塊及金鑰長度不足時,會補齊)。AES加密有很多輪的重複和變換。大致步驟如下:1、金鑰擴充套件(KeyExpansion),2、初始輪(Initial Round),3、重複輪(Rounds),每一輪又包括:SubBytes、ShiftRows、MixColumns、AddRoundKey,4、最終輪(Final Round),最終輪沒有MixColumns。

  • AES加密的實現

AES 加密的實現我們需要新建一個NSData的Category類,
然後包含兩個標頭檔案:

#import <CommonCrypto/CommonDigest.h>
#import <CommonCrypto/CommonCryptor.h>

AES 加密時,我們需要自己定義一個16位的字串,作為key。

// 自定義一個KEY
#define AES_KEY  @"0123456789ABCDEF"
/**
 *  對data加密
 *
 *  @param data 需要加密的資料
 *
 *  @return 加密後的資料
 */
+(NSData *)aes256EncryptWithData:(NSData *)data{
    if (!AES_KEY || AES_KEY.length !=16) {
        NSLog(@"key length must be 16");
        return nil;
    }
    char keyPtr[kCCKeySizeAES256+1];
    bzero(keyPtr, sizeof(keyPtr));
    [AES_KEY getCString:keyPtr maxLength:sizeof(keyPtr) encoding:NSUTF8StringEncoding];
    NSUInteger dataLength = data.length;
    size_t bufferSize = dataLength + kCCBlockSizeAES128;
    void *buffer = malloc(bufferSize);
    size_t numBytesEncrypted = 0;
    CCCryptorStatus cryptStatus = CCCrypt(kCCEncrypt, kCCAlgorithmAES128,
                                          kCCOptionPKCS7Padding | kCCOptionECBMode,
                                          keyPtr, kCCBlockSizeAES128,
                                          NULL,
                                          data.bytes, dataLength,
                                          buffer, bufferSize,
                                          &numBytesEncrypted);
    if (cryptStatus == kCCSuccess) {
        return [NSData dataWithBytesNoCopy:buffer length:numBytesEncrypted];
    }
    free(buffer);
    return nil;
}

/**
 *  對data解密
 *
 *  @param data 需要解密的資料
 *
 *  @return 解密後的資料
 */
+(NSData *)aes256DecryptWithData:(NSData *)data{
    if (!AES_KEY || AES_KEY.length !=16) {
        NSLog(@"key length must be 16");
        return nil;
    }
    char keyPtr[kCCKeySizeAES256+1];
    bzero(keyPtr, sizeof(keyPtr));
    [AES_KEY getCString:keyPtr maxLength:sizeof(keyPtr) encoding:NSUTF8StringEncoding];
    NSUInteger dataLength = data.length;
    size_t bufferSize = dataLength + kCCBlockSizeAES128;
    void *buffer = malloc(bufferSize);
    size_t numBytesDecrypted = 0;
    CCCryptorStatus cryptStatus = CCCrypt(kCCDecrypt, kCCAlgorithmAES128,
                                          kCCOptionPKCS7Padding | kCCOptionECBMode,
                                          keyPtr, kCCBlockSizeAES128,
                                          NULL,
                                          data.bytes, dataLength,
                                          buffer, bufferSize,
                                          &numBytesDecrypted);
    if (cryptStatus == kCCSuccess) {
        return [NSData dataWithBytesNoCopy:buffer length:numBytesDecrypted];
    }
    free(buffer);
    return nil;
}

/**
 *  對字串加密
 *
 *  @param string 需要加密的字串
 *
 *  @return 加密後的資料
 */
+(NSData*)aes256EncryptWithString:(NSString*)string{
    NSData *data = [string dataUsingEncoding:NSUTF8StringEncoding];
    NSData *encryptedData = [self aes256EncryptWithData:data];
    return encryptedData;
}

/**
 *  解密
 *
 *  @param data 需要解密的資料
 *
 *  @return 解密後的字串
 */
+(NSString*)aes256DecryptStringWithData:(NSData *)data{

    NSData *decryData = [self aes256DecryptWithData:data];
    NSString *string = [[NSString alloc] initWithData:decryData encoding:NSUTF8StringEncoding];
    return string;
}

兩種加密和解密,一種是字串的一種是資料流的。

Base64

  • 什麼是base64

Base64是網路上最常見的用於傳輸8Bit位元組程式碼的編碼方式之一,大家可以檢視RFC2045~RFC2049,上面有MIME的詳細規範。Base64編碼可用於在HTTP環境下傳遞較長的標識資訊。例如,在Java Persistence系統Hibernate中,就採用了Base64來將一個較長的唯一識別符號(一般為128-bit的UUID)編碼為一個字串,用作HTTP表單和HTTP GET URL中的引數。在其他應用程式中,也常常需要把二進位制資料編碼為適合放在URL(包括隱藏表單域)中的形式。此時,採用Base64編碼具有不可讀性,即所編碼的資料不會被人用肉眼所直接看到。

  • base64原理

轉碼過程例子:
3*8=4*6
記憶體1個字元佔8位
轉前: s 1 3
先轉成ascii:對應 115 49 51
2進位制: 01110011 00110001 00110011
6個一組(4組) 011100110011000100110011
然後才有後面的 011100 110011 000100 110011
然後計算機是8位8位的存數 6不夠,自動就補兩個高位0了
所有有了 高位補0
科學計算器輸入 00011100 00110011 00000100 00110011
得到 28 51 4 51

  • base64的實現

base64和AES一樣,需要一個NSData的Category類,但是不需要包含那兩個標頭檔案。

base64需要自己定義一個64位長度的編碼表。

static const char base64EncodingTable[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

base64還需要一個解碼錶。

static const short base64DecodingTable[256] = {
    -2, -2, -2, -2, -2, -2, -2, -2, -2, -1, -1, -2,  -1,  -1, -2, -2,
    -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,  -2,  -2, -2, -2,
    -1, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, 62,  -2,  -2, -2, 63,
    52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -2, -2,  -2,  -2, -2, -2,
    -2, 0,  1,  2,  3,  4,  5,  6,  7,  8,  9,  10,  11,  12, 13, 14,
    15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -2,  -2,  -2, -2, -2,
    -2, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36,  37,  38, 39, 40,
    41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -2,  -2,  -2, -2, -2,
    -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,  -2,  -2, -2, -2,
    -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,  -2,  -2, -2, -2,
    -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,  -2,  -2, -2, -2,
    -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,  -2,  -2, -2, -2,
    -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,  -2,  -2, -2, -2,
    -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,  -2,  -2, -2, -2,
    -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,  -2,  -2, -2, -2,
    -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,  -2,  -2, -2, -2
};
/**
 *  資料流加密
 *
 *  @param data 需要加密的資料流
 *
 *  @return 加密後的字串
 */
+(NSString *)base64EncodedWithData:(NSData *)data{
    NSUInteger length = data.length;
    if (length == 0)
        return @"";

    NSUInteger out_length = ((length + 2) / 3) * 4;
    uint8_t *output = malloc(((out_length + 2) / 3) * 4);
    if (output == NULL)
        return nil;

    const char *input = data.bytes;
    NSInteger i, value;
    for (i = 0; i < length; i += 3) {
        value = 0;
        for (NSInteger j = i; j < i + 3; j++) {
            value <<= 8;
            if (j < length) {
                value |= (0xFF & input[j]);
            }
        }
        NSInteger index = (i / 3) * 4;
        output[index + 0] = base64EncodingTable[(value >> 18) & 0x3F];
        output[index + 1] = base64EncodingTable[(value >> 12) & 0x3F];
        output[index + 2] = ((i + 1) < length)
        ? base64EncodingTable[(value >> 6) & 0x3F]
        : '=';
        output[index + 3] = ((i + 2) < length)
        ? base64EncodingTable[(value >> 0) & 0x3F]
        : '=';
    }

    NSString *base64 = [[NSString alloc] initWithBytes:output length:out_length encoding:NSASCIIStringEncoding];
    free(output);
    return base64;
}

/**
 *  字串解密
 *
 *  @param base64EncodedString 需要解密的字串
 *
 *  @return 解密後的資料流
 */
+(NSData *)base64DecryptWithString:(NSString *)base64EncodedString{
    NSInteger length = base64EncodedString.length;
    const char *string = [base64EncodedString cStringUsingEncoding:NSASCIIStringEncoding];
    if (string  == NULL)
        return nil;

    while (length > 0 && string[length - 1] == '=')
        length--;

    NSInteger outputLength = length * 3 / 4;
    NSMutableData *data = [NSMutableData dataWithLength:outputLength];
    if (data == nil)
        return nil;
    if (length == 0)
        return data;

    uint8_t *output = data.mutableBytes;
    NSInteger inputPoint = 0;
    NSInteger outputPoint = 0;
    while (inputPoint < length) {
        char i0 = string[inputPoint++];
        char i1 = string[inputPoint++];
        char i2 = inputPoint < length ? string[inputPoint++] : 'A';
        char i3 = inputPoint < length ? string[inputPoint++] : 'A';

        output[outputPoint++] = (base64DecodingTable[i0] << 2)
        | (base64DecodingTable[i1] >> 4);
        if (outputPoint < outputLength) {
            output[outputPoint++] = ((base64DecodingTable[i1] & 0xf) << 4)
            | (base64DecodingTable[i2] >> 2);
        }
        if (outputPoint < outputLength) {
            output[outputPoint++] = ((base64DecodingTable[i2] & 0x3) << 6)
            | base64DecodingTable[i3];
        }
    }

    return data;
}

/**
 *  字串做加密
 *
 *  @param str 需要加密的字串
 *
 *  @return 加密後的字串
 */
+(NSString *)base64EncodedWithString:(NSString *)str{
    NSData* data = [str dataUsingEncoding:NSUTF8StringEncoding];
    return [self base64EncodedWithData:data];
}

/**
 *  對字串解密
 *
 *  @param base64EncodedString 需要解密的字串
 *
 *  @return 解密後的字串
 */
+ (NSString *)base64DecryptString:(NSString *)base64EncodedString{
    NSData *data = [self base64DecryptWithString:base64EncodedString];
    return [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
}

加密和解密也是對應有字串和NSData兩種方法。

總結

從程式碼上看,這三種加密方式還是有點難度的,但是網上一堆程式碼,我們需要知道怎麼使用。在實際專案中的使用,我使用的兩點就是,和後臺互動的時候,對介面中的引數加密就是介面地址“?”後面的資料,還有一點就是對伺服器返回的json資料的加密。一般是前臺對介面引數加密後臺解密,後臺對介面返回引數加密,前臺對返回的資料解密。
程式碼下載地址:加密+解密。