iOS資料加密(Base64,雜湊函式,AES,RSA)

dearmiku發表於2017-12-12

Base64

base64是一種基於64個可列印字元來表示二進位制資料的表示方法.嚴格來說它只能算作一種編碼方式. Base64編碼之所以稱為Base64,是因為其使用64個字元來對任意資料進行編碼,同理有Base32、Base16編碼

作用:

1, 由於某些系統中只能使用ASCII字元。Base64就是用來將非ASCII字元的資料轉換成ASCII字元的一種方法. 2, 使用SMTP協議 (Simple Mail Transfer Protocol 簡單郵件傳輸協議)來傳送郵件。因為這個協議是基於文字的協議,所以如果郵件中包含一幅圖片,我們知道圖片的儲存格式是二進位制資料(binary data),而非文字格式,我們必須將二進位制的資料編碼成文字格式,這時候Base 64 Encoding就派上用場了. 3, 通過base64將ASCII不可見字元轉換為可見字元

使用:

//編碼
- (NSString *)base64EncodedString;{
NSData *data = [self dataUsingEncoding:NSUTF8StringEncoding];
return [data base64EncodedStringWithOptions:0];
}

//解碼
- (NSString *)base64DecodedString{
NSData *data = [[NSData alloc]initWithBase64EncodedString:self options:0];
return [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding];
}
複製程式碼

MD5

MD5(訊息摘要演算法第五版)為電腦保安領域廣泛使用的一種雜湊函式,用以提供訊息的完整性保護.

特點:

1、壓縮性:任意長度的資料,算出的MD5值長度都是固定的(32摘要)。 2、容易計算:從原資料計算出MD5值很容易。 3、抗修改性:對原資料進行任何改動,哪怕只修改1個位元組,所得到的MD5值都有很大區別。 4、強抗碰撞(難逆向):已知原資料和其MD5值,想找到一個具有相同MD5值的資料(即偽造資料)是非常困難的。

作用

1, 數字簽名, 當我們傳遞敏感資訊時,可以為利用MD5+時間戳+鹽 為訊息新增唯一的數字簽名,當服務端獲得資料後,用相同演算法再次簽名.進行比較 若不一致 則資料遭到篡改. 2, 檔案驗證, 我們在下載檔案時,由於複雜的網路環境,我們下載的檔案可能會有內容丟失或篡改的可能性.(例如我們從伺服器獲取的H5檔案遭到了JS注入),利用MD5可以有效防止這些事情的發生.

同類演算法

SHA-1:

會產生一個160位的訊息摘要,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。

實現

這裡是對字串的雜湊計算,若對檔案則需要先讀取檔案流再去雜湊. 需要: import <CommonCrypto/CommonDigest.h>

MD5

- (NSString *)md5String {
    const char *str = self.UTF8String;
    uint8_t buffer[CC_MD5_DIGEST_LENGTH];
    CC_MD5(str, (CC_LONG)strlen(str), buffer);
    return [self stringFromBytes:buffer length:CC_MD5_DIGEST_LENGTH];
}
複製程式碼

SHA-1:

- (NSString *)sha1String {
    const char *str = self.UTF8String;
    uint8_t buffer[CC_SHA1_DIGEST_LENGTH];
    
    CC_SHA1(str, (CC_LONG)strlen(str), buffer);
    
    return [self stringFromBytes:buffer length:CC_SHA1_DIGEST_LENGTH];
}

複製程式碼

SHA256:

- (NSString *)sha256String {
    const char *str = self.UTF8String;
    uint8_t buffer[CC_SHA256_DIGEST_LENGTH];
    
    CC_SHA256(str, (CC_LONG)strlen(str), buffer);
    
    return [self stringFromBytes:buffer length:CC_SHA256_DIGEST_LENGTH];
}
複製程式碼

SHA512:

- (NSString *)sha512String {
    const char *str = self.UTF8String;
    uint8_t buffer[CC_SHA512_DIGEST_LENGTH];
    
    CC_SHA512(str, (CC_LONG)strlen(str), buffer);
    
    return [self stringFromBytes:buffer length:CC_SHA512_DIGEST_LENGTH];
}
複製程式碼

SHA3:

需要在github上下載keccak程式碼包 :https://github.com/gvanas/KeccakCodePackage

大檔案的計算:

這裡以MD5為例:

#define FileHashDefaultChunkSizeForReadingData 4096

- (NSString *)fileMD5Hash {
    //開啟一個檔案準備讀取
    NSFileHandle *fp = [NSFileHandle fileHandleForReadingAtPath:self];
    if (fp == nil) {
    //若路徑為資料夾這種的(如:.framework)則會返回Null
        return nil;
    }
    //建立MD5變數
    CC_MD5_CTX hashCtx;
    //初始化MD5變數
    CC_MD5_Init(&hashCtx);
    
    while (YES) {
        @autoreleasepool {
        //讀取檔案指定長度資料,迴圈讀取避免一次載入到記憶體過大
            NSData *data = [fp readDataOfLength:FileHashDefaultChunkSizeForReadingData];
            //準備MD5加密,將內容上傳
            CC_MD5_Update(&hashCtx, data.bytes, (CC_LONG)data.length);
            
            if (data.length == 0) {
                break;
            }
        }
    }
    //關閉檔案
    [fp closeFile];
    
    //建立MD5結果緩衝區
    uint8_t buffer[CC_MD5_DIGEST_LENGTH];
    //將MD5結果寫進緩衝區
    CC_MD5_Final(buffer, &hashCtx);
    
    //原始資料轉換為字串
    return [self stringFromBytes:buffer length:CC_MD5_DIGEST_LENGTH];
}

- (NSString *)stringFromBytes:(uint8_t *)bytes length:(int)length {
    NSMutableString *strM = [NSMutableString string];
    
    for (int i = 0; i < length; i++) {
        [strM appendFormat:@"%02x", bytes[i]];
    }
    
    return [strM copy];
}
複製程式碼

對於其他的演算法檔案加密方式也是這樣的,它們都是由CommonCrypto庫提供的.

HMAC雜湊計算(加鹽)

HMAC是金鑰相關的雜湊運算訊息認證碼,HMAC運算利用雜湊演算法,以一個金鑰和一個訊息為輸入,生成一個訊息摘要作為輸出。 HMAC演算法更象是一種加密演算法,它引入了金鑰,其安全性已經不完全依賴於所使用的HASH演算法,有些類似對稱加密,但是是不可逆的那種~. 以MD5為例:

- (NSString *)hmacMD5StringWithKey:(NSString *)key {
    const char *keyData = key.UTF8String;
    const char *strData = self.UTF8String;
    //切換其他雜湊函式替換這裡(如:CC_SHA256_DIGEST_LENGTH)
    uint8_t buffer[CC_MD5_DIGEST_LENGTH];
        //切換其他雜湊函式替換這裡(如:kCCHmacAlgSHA256)
    CCHmac(kCCHmacAlgMD5, keyData, strlen(keyData), strData, strlen(strData), buffer);
    
   //切換其他雜湊函式替換這裡(如:CC_SHA256_DIGEST_LENGTH)
    return [self stringFromBytes:buffer length:CC_MD5_DIGEST_LENGTH];
}
複製程式碼

AES(對稱加密)

簡介

美國國家標準技術研究所在2001年釋出了高階加密標準(AES)。 AES是基於資料塊的加密方式, 即,每次處理的資料是一塊(16位元組),當資料不是16位元組的倍數時填充, 這就是所謂的分組密碼(區別於基於位元位的流密碼),16位元組是分組長度。

AES在軟體及硬體上都能快速地加解密,相對來說較易於實現,

15110869784780.jpg

實現

在使用AES時要配置幾個加密引數,只有都一致才能使 客戶端與服務端 結果一致.

引數配置

金鑰長度

key常見的長度有三種:128、192和256 bits

加密模式

AES屬於塊加密(Block Cipher),塊加密中有CBC、ECB、CTR、OFB、CFB等幾種工作模式.

ECB

是一種基礎的加密方式,AES預設沒收,密文被分割成分組長度相等的塊(不足補齊),然後單獨一個個加密,一個個輸出組成密文.

15110873264632.jpg

CBC

這個模式是鏈式的,後一塊需要前一塊做基礎,第一塊需要一個需要初始化向量IV做基礎. 相同的輸入產生不同的輸出. 能看到的資料是“明文+IV”或“明文+前一個密文”的亂碼,所以能隱藏明文.

所以加密/解密 需要: 明文/密文 + 祕鑰 + 初始向量引數

15110876564551.jpg

填充方式

因為AES的演算法是把明文分組再處理的,他要求每個分組(16位元組)是“滿”的,即明文長度必須被16位元組整除.

所以明文最後不足的16位元組的要先進行資料填充,把不足16位元組的最後一組補成16位元組.

CFB,OFB和CTR模式由於與key進行加密操作的是上一塊加密後的密文,因此不需要對最後一段明文進行填充. 在iOS SDK中提供了PKCS7Padding.

初始向量

正如在CBC模式哪裡介紹的,開始加密時,從哪裡開始就是初始向量,如不設定則系統預設為0;

程式碼

NSString *const kInitVector = @"初始向量";

size_t const kKeySize = kCCKeySizeAES256;//祕鑰長度

+ (NSData *)encryptAES:(NSData *)content key:(NSString *)key {
    
    NSData *contentData = content;
    NSUInteger dataLength = contentData.length;
    
    //設定加密祕鑰,因C字串結束符為'\0' 所以大小+1
    char keyPtr[kKeySize + 1];
    memset(keyPtr, 0, sizeof(keyPtr));
    //應確保大小小於等於16個位元組.
    [key getCString:keyPtr maxLength:sizeof(keyPtr) encoding:NSUTF8StringEncoding];
    
    //密文長度 = 明文長度+祕鑰長度
    size_t encryptSize = dataLength + kCCBlockSizeAES128;
    void *encryptedBytes = malloc(encryptSize);
    
    //密文接受指標
    size_t actualOutSize = 0;
    
    //初始向量
    NSData *initVector = [kInitVector dataUsingEncoding:NSUTF8StringEncoding];
    
    CCCryptorStatus cryptStatus = CCCrypt(kCCEncrypt,               //是加密還是解密
                                          kCCAlgorithmAES,          //加密/解密方式
                                          kCCOptionPKCS7Padding,    //PKCS7Padding
                                          keyPtr,                   //祕鑰
                                          kKeySize,                 //祕鑰大小
                                          initVector.bytes,         //初始向量
                                          contentData.bytes,        //明文/密文
                                          dataLength,               //明文/密文大小
                                          encryptedBytes,           //結果: 密文/明文緩衝區
                                          encryptSize,              //結果: 密文/明文大小
                                          &actualOutSize);          //結果指標
    
    if (cryptStatus == kCCSuccess) {
        // 成功
        return [NSData dataWithBytesNoCopy:encryptedBytes length:actualOutSize];
    }
    //釋放
    free(encryptedBytes);
    return nil;
}
複製程式碼

RSA

原理

數學基礎

任意給定正整數n,請問在小於等於n的正整數之中,有多少個與n構成互質的數.計算這個值的函式為 尤拉函式: 如: φ(8) = 4.

若n為質數 則: φ(n) = n-1 原理3 若n為質數的n次方 則: φ(p^k) = p^k - p^(k-1) 若n為兩個互質整數之積 則: φ(p1 * p2) = φ(p1)φ(p2) 原理1 若正整數 a和n互質 則: a^φ(n) 被n除的餘數為1 則: a^φ(n) %n=1 尤拉定理 若正整數a和質數p互質 則: a^p-1 %p=1 //費馬小定理 若正整數a和n互質,則一定可以找到正整數b 使: ab%n=1, b為a的模反元素. 原理2

祕鑰生成

1, 隨機生成兩個不等質數p和q 如 p=61 q=53

2, 求出pq乘積n n=p * q= 61*53=3233.

這裡n的長度即為,祕鑰長度,如3233為二進位制12位,RSA祕鑰一般為1024位.

3, 計算φ(n)

根據 原理1 φ(n) = φ(p)φ(q) = (p-1)(q-1) = 3120

4, 隨機選擇一個整數e 條件為 1<e<φ(n) 且 e與φ(n)互質

假如選擇 17

5, 獲取e對於φ(n)的模反元素d

ed%φ(n)=1 ---> ed -1 = kφ(n) ---> 17d + 3120k = 1 通過擴充套件歐幾里德演算法 可得到一組整數解 d=2753 k=-15

6, 這裡 n和e 為公鑰 n和d 為私鑰

加密(此處為公鑰加密 n e)

對明文資訊m 加密 注意:m為正整數,且m須小於n

m^e % n = c 這裡的c就是加密後得到的密文 65^17 % 3233 = 2790 這裡2790就是加密後的密文

解密(n d)

解密原理

解密規則為: c^d %n = m

因為加密過程為: m^e % n = c ---> c = m^e - kn 若想證明解密規則成立 則等同於證明 (m^e - kn)^d % n = m 成立

(m^e - kn)^d % n = m ---> m^ed % n = m

由於在製作公私鑰 的第5步 所以:

ed%φ(n)=1 ---> ed = hφ(n)+1

將ed代入須證明公式:

m^ed % n = m ---> m^hφ(n)+1 % n = m

若m n 互質

m^hφ(n)+1 % n = m ---> ((m^φ(n))^h * m) % n = m

由於尤拉定理 m^φ(n) %n=1可得 ((m^φ(n))^h * m) % n = m ---> (1^h * m) % n = m 則解密公式成立

若m n 不互質

因為製作公私鑰 的第1步 n = p * q

因為加密方法 m^e % n = c , 且因 m<n(這裡是在製作時要求的) 所以 c肯定不為0 由此可得 m 與 n 不為互質關係.

由於 n = pq 且 pq互質 所以 n有且只有 p q 兩個因子. 然而 m n有公因子 所以 m n 的公因子 必定為 q或p的整數倍. 所以 m = kp 或 kq

以 m=kp為例 因為上面描述的關係 m<n , m n不互質, n = qp 所以 k與q互質 ---> m與q互質

由尤拉定理得:
m^φ(q) % q = 1 ---> 由於q為質數 ---> m^q-1 % q = 1 --> (kp)^q-1 % q = 1

因為 k與q互質 p與q互質 --> ((kp)^q-1 * kp)%q = kp

進一步可以確定該式成立: (((kp)^h(p-1)(q-1)) * kp)%q = kp 因為p為質數 h為任一整數

原理1原理3(((kp)^h(p-1)(q-1)) * kp)%q = kp ---> (kp)^hφ(n)+1 %q=kp

由於 ed%φ(n)=1,且m=kp 所以將h匹配為合適的值得 (kp)^ed %q = kp ---> (m)^ed = tq +m t為整數.

兩側同除m得: (m)^ed-1 = tq/m +1 由於ed為整數,m為整數 故tq/m為整數. 因q與m互質 所以t為m的整數倍 --> t = yp ---> m^ed = yn+m ---> m^ed % n = m

由於加密方式 m^e % n = c ---> c^d % n = m

安全性討論

若想破解RSA 則需要在已知 n e的情況下 求 d 因為

因為 ed%φ(n)=1 所以需知道 φ(n) 因為 φ(n) = (p-1)(q-1) 所以需求得 qp 因為 qp=n 所以得將n因式分解

而因式分解是十分困難的 特別是對於 特大整數的因式分解. 由於 名文m 需小於 祕鑰長度n 所以常用來加密 對稱加密的祕鑰.

iOS實現

參考連結:iOS中使用RSA加密 在iOS中使用RSA加密解密 需要使用到**.der** 和 .p12 字尾格式檔案.

.p12 格式檔案是用來加密的 私鑰 .der 格式檔案用來解密的 公鑰

新增動態庫 Security.framework

具體實現太長,我彙總了一個類 github連結 感興趣可以下下來看看 如果幫到你 點個Star鼓勵一蛤~ 本文地址 部落格地址

相關文章