1. 程式人生 > >iOS 編碼(Base64) + 雜湊摘要(MD5+SHA)

iOS 編碼(Base64) + 雜湊摘要(MD5+SHA)

一、編碼演算法

(1)Base64編碼

        在MIME格式的電子郵件中,base64可以用來將binary的位元組序列資料編碼成ASCII字元序列構成的文字。使用時,在傳輸編碼方式中指定base64。使用的字元包括大小寫字母各26個,加上10個數字,和加號“+”,斜槓“/”,一共64個字元,等號“=”用來作為字尾用途。
        完整的base64定義可見 RFC1421和 RFC2045。編碼後的資料比原始資料略長,為原來的4/3。在電子郵件中,根據RFC822規定,每76個字元,還需要加上一個回車換行。可以估算編碼後資料長度大約為原長的135.1%。
        轉換的時候,將三個byte的資料,先後放入一個24bit的緩衝區中,先來的byte佔高位。資料不足3byte的話,於緩衝區中剩下的Bit用0補足。然後,每次取出6個bit,按照其值選擇ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/中的字元作為編碼後的輸出。不斷進行,直到全部輸入資料轉換完成。
如果最後剩下兩個輸入資料,在編碼結果後加1個“=”;如果最後剩下一個輸入資料,編碼結果後加2個“=”;如果沒有剩下任何資料,就什麼都不要加,這樣才可以保證資料還原的正確性。

(2)UrlEncode編碼

       百分號編碼(Percent-encoding), 也稱作URL編碼(URL encoding), 是特定上下文的統一資源定位符 (URI)的編碼機制. 實際上也適用於統一資源標誌符(URI)的編碼. 也用於為"application/x-www-form-urlencoded" MIME準備資料, 因為它用於通過HTTP的請求操作(request)提交HTML表單資料。
       URI所允許的字元分作保留與未保留. 保留字元是那些具有特殊含義的字元. 例如, 斜線字元用於URL (或者更一般的, URI)不同部分的分界符. 未保留字元沒有這些特殊含義. 百分號編碼把保留字元表示為特殊字元序列. 上述情形隨URI與URI的不同版本規格會有輕微的變化。
部分轉換規則如下:

空格 ! # $ % + @ : = ?
%20 %21 %23 %24 %25 %2B %40 %3A %3D %3F

(3)BCD碼

       8421碼是BCD程式碼中最常用的一種。在這種編碼方式中每一位二值程式碼的1都是代表一個固定數值,把每一位的1代表的十進位制數加起來,得到的結果就是它所代表的十進位制數碼。由於程式碼中從左到右每一位的1分別表示8,4,2,1,所以把這種程式碼叫做8421程式碼。每一位的1代表的十進位制數稱為這一位的權。8421碼中的每一位的權是固定不變的,它屬於恆權程式碼。

二、雜湊摘要(Hash)

(1)MD5

       Message Digest Algorithm MD5(中文名為訊息摘要演算法第五版)為電腦保安領域廣泛使用的一種雜湊函式,用以提供訊息的完整性保護。
       MD5即Message-Digest Algorithm 5(資訊-摘要演算法5),用於確保資訊傳輸完整一致。是計算機廣泛使用的雜湊演算法之一(又譯摘要演算法、雜湊演算法),主流程式語言普遍已有MD5實現。將資料(如漢字)運算為另一固定長度值,是雜湊演算法的基礎原理,MD5的前身有MD2、MD3和MD4。
       MD5演算法具有以下特點:
       1、壓縮性:任意長度的資料,算出的MD5值長度都是固定的。
       2、容易計算:從原資料計算出MD5值很容易。
       3、抗修改性:對原資料進行任何改動,哪怕只修改1個位元組,所得到的MD5值都有很大區別。
       4、強抗碰撞:已知原資料和其MD5值,想找到一個具有相同MD5值的資料(即偽造資料)是非常困難的。
       MD5的作用是讓大容量資訊在用數字簽名軟體簽署私人金鑰前被"壓縮"成一種保密的格式(就是把一個任意長度的位元組串變換成一定長的十六進位制數字串)。

(2)SHA

       安全雜湊演算法(Secure Hash Algorithm)主要適用於數字簽名標準(Digital Signature Standard DSS)裡面定義的數字簽名演算法(Digital Signature Algorithm DSA)。對於長度小於2^64位的訊息,SHA1會產生一個160位的訊息摘要。該演算法經過加密專家多年來的發展和改進已日益完善,並被廣泛使用。該演算法的思想是接收一段明文,然後以一種不可逆的方式將它轉換成一段(通常更小)密文,也可以簡單的理解為取一串輸入碼(稱為預對映或資訊),並把它們轉化為長度較短、位數固定的輸出序列即雜湊值(也稱為資訊摘要或資訊認證程式碼)的過程。雜湊函式值可以說是對明文的一種“指紋”或是“摘要”所以對雜湊值的數字簽名就可以視為對此明文的數字簽名。

(3)MD5與SHA1的區別

       因為二者均由MD4匯出,SHA-1和MD5彼此很相似。相應的,他們的強度和其他特性也是相似,但還有以下幾點不同:
       1、對強行攻擊的安全性:最顯著和最重要的區別是SHA-1摘要比MD5摘要長32 位。使用強行技術,產生任何一個報文使其摘要等於給定報摘要的難度對MD5是2^128數量級的操作,而對SHA-1則是2^160數量級的操作。這樣,SHA-1對強行攻擊有更大的強度。
       2、對密碼分析的安全性:由於MD5的設計,易受密碼分析的攻擊,SHA-1顯得不易受這樣的攻擊。
       3、速度:在相同的硬體上,SHA-1的執行速度比MD5慢。

(4)SHA-1停止計劃

  2005年,密碼學家就證明SHA-1的破解速度比預期提高了2000倍,雖然破解仍然是極其困難和昂貴的,但隨著計算機變得越來越快和越來越廉價,SHA-1演算法的安全性也逐年降低,已被密碼學家嚴重質疑,希望由安全強度更高的SHA-2替代它。
微軟第一個宣佈了SHA-1棄用計劃,對於SSL證書和程式碼簽名證書,微軟設定了不同的替換時間表:
  1、所有Windows受信任的根證書頒發機構(CA)從2016年1月1日起必須停止簽發新的SHA-1簽名演算法SSL證書和程式碼簽名證書;
  2、對於SSL證書,Windows將於2017年1月1日起停止支援SHA1證書。也就是說:任何在之前簽發的SHA-1證書必須替換成SHA-2證書;
  3、對於程式碼簽名證書,Windows將於2016年1月1日停止接受沒有時間戳的SHA-1簽名的程式碼和SHA-1證書。也就是說,Windows仍然接受在2016年1月1日之前使用SAH-1簽名的已經加上RFC3161時間戳的程式碼,直到微軟認為有可能出現SHA-1攻擊時。
  Google官方部落格宣佈,將在Chrome瀏覽器中逐漸降低SHA-1證書的安全指示,逐步停止對使用SHA-1雜湊演算法證書的支援。近日,Chrome 39新版本 PC 端釋出,在部分作業系統下,該版本瀏覽器中已開始出現“該網站使用的安全設定已過期” 提示,在接下來的6個月內會變得越來越嚴格。最終,使用了有效期至2016年的SHA-1證書的站點可能會被給予黃色警告。
  Mozilla也做了同樣的決定,在其對外公佈近期更新計劃中表示:“現在依然有不少網站使用基於 SHA-1簽名的 SSL證書,所以我們決定加入微軟和谷歌的陣營,認為應在 2016 年 1 月 1 日前停止發放 SHA-1 證書,在 2017 年 1 月1 日後不再信任此證書。”

三、程式碼實現(Base64 + MD5 + SHA)

//
//  main.m
//  CryptoDemo
//
//  Created by 555chy on 6/17/16.
//  Copyright © 2016 555chy. All rights reserved.
//

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

#define G_KEY               @""
#define G_IV                @""
#define G_SECRET_KEY_LENGTH 24

//ios的base64編碼有兩種方式(使用ios7系統自帶的編碼庫和GMTBase64兩種方式),這裡是用的是系統自帶的方法
NSString* base64Encode(const char* originalCStr, NSInteger len) {
    NSData *originalData = [NSData dataWithBytes:originalCStr length:len];
    /*
     typedef NS_OPTIONS(NSUInteger, NSDataBase64EncodingOptions) {
     // Use zero or one of the following to control the maximum line length after which a line ending is inserted. No line endings are inserted by default.
     NSDataBase64Encoding64CharacterLineLength = 1UL << 0,
     NSDataBase64Encoding76CharacterLineLength = 1UL << 1,
     
     // Use zero or more of the following to specify which kind of line ending is inserted. The default line ending is CR LF.
     NSDataBase64EncodingEndLineWithCarriageReturn = 1UL << 4,
     NSDataBase64EncodingEndLineWithLineFeed = 1UL << 5,
     
     } NS_ENUM_AVAILABLE(10_9, 7_0);
     */
    NSData *base64Data = base64Data = [originalData base64EncodedDataWithOptions:NSDataBase64EncodingEndLineWithLineFeed];
    return [[NSString alloc] initWithData:base64Data encoding:NSUTF8StringEncoding];
}

NSString* base64Decode(NSString* encodedBase64Str) {
    NSData *base64Data = [[NSData alloc] initWithBase64EncodedString:encodedBase64Str options:NSDataBase64DecodingIgnoreUnknownCharacters];
    return [[NSString alloc] initWithData:base64Data encoding:NSUTF8StringEncoding];
}

//加密出啦的是一個32位的16進位制序列
NSString* md5Hash(NSString* originalStr, BOOL is16Or32) {
    unsigned char md5CStr[CC_MD5_DIGEST_LENGTH];
    //const char *cOriginalStr = [originalStr cStringUsingEncoding:NSUTF8StringEncoding];
    const char *cOriginalStr = [originalStr UTF8String];
    CC_MD5(cOriginalStr, (CC_LONG)strlen(cOriginalStr), md5CStr);
    //NSMutableString *md5Str = [[NSMutableString alloc] init];
    NSMutableString *md5Str = [NSMutableString stringWithCapacity:CC_MD5_DIGEST_LENGTH*2];
    for(int i=0;i<CC_MD5_DIGEST_LENGTH;i++) {
        [md5Str appendFormat:@"%02x", md5CStr[i]];
    }
    if(is16Or32) {
        //return [[md5Str substringToIndex:24] substringFromIndex:8];
        return [[md5Str substringFromIndex:8] substringToIndex:16];
    }
    return md5Str;
}

//SHA
/*
 16進位制序列位數    位元組數       位元數
 CC_SHA1     40             20          160
 CC_SHA256   64             32          256
 CC_SHA384   96             48          384
 CC_SHA512  128             64          512
 */
NSString* shaHash(NSString* originalStr, unsigned char* (*CC_SHAX)(const void *data, CC_LONG len, unsigned char *md), unsigned int shaDigestLength) {
    unsigned char sha1CStr[shaDigestLength];
    //const char *cOriginalStr = [originalStr cStringUsingEncoding:NSUTF8StringEncoding];
    const char *cOriginalStr = [originalStr UTF8String];
    CC_SHAX(cOriginalStr, (CC_LONG)strlen(cOriginalStr), sha1CStr);
    //NSMutableString *sha1Str = [[NSMutableString alloc] init];
    NSMutableString *sha1Str = [NSMutableString stringWithCapacity:shaDigestLength*2];
    for(int i=0;i<shaDigestLength;i++) {
        [sha1Str appendFormat:@"%02x", sha1CStr[i]];
    }
    return sha1Str;
}

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // insert code here...

        NSString *originalStr = @"welcome to chy龍神的部落格";
        //NSString *originalStr = @"歡迎光臨JerryVon的部落格";
        // const char* originalCstr = [originalStr cStringUsingEncoding:NSUTF8StringEncoding];
        const char *originalCstr = [originalStr UTF8String];
        NSString *base64EncodeStr = base64Encode(originalCstr, strlen(originalCstr));
        NSString *base64DecodeStr = base64Decode(base64EncodeStr);
        NSLog(@"originalStr = %@", originalStr);
        NSLog(@"base64EncodeStr = %@", base64EncodeStr);
        NSLog(@"base64DecodeStr = %@", base64DecodeStr);
        
        NSString *md5Hash16Str = md5Hash(originalStr, YES);
        NSString *md5Hash32Str = md5Hash(originalStr, NO);
        NSLog(@"md5Hash16Str = %@", md5Hash16Str);
        NSLog(@"md5Hash32Str = %@", md5Hash32Str);
        
        NSString *sha1HashStr = shaHash(originalStr, CC_SHA1, CC_SHA1_DIGEST_LENGTH);
        NSString *sha256HashStr = shaHash(originalStr, CC_SHA256, CC_SHA256_DIGEST_LENGTH);
        NSString *sha384HashStr = shaHash(originalStr, CC_SHA384, CC_SHA384_DIGEST_LENGTH);
        NSString *sha512HashStr = shaHash(originalStr, CC_SHA512, CC_SHA512_DIGEST_LENGTH);
        NSLog(@"sha1HashStr = %@", sha1HashStr);
        NSLog(@"sha25HashStr = %@", sha256HashStr);
        NSLog(@"sha384HashStr = %@", sha384HashStr);
        NSLog(@"sha512HashStr = %@", sha512HashStr);
    }
    return 0;
}

四、執行結果


五、第三方驗證