數(shù)據(jù)加密(一)——對稱加密(AES為例)

目錄

  • 數(shù)據(jù)加密概述
  • 對稱密鑰加密概述
  • AES(Rijndael)概述
  • iOS中的AES(iOS SDK中的Security.framework庫,非openssl庫)

# 數(shù)據(jù)加密簡述

數(shù)據(jù)加密(Encryption)是指將明文信息(Plaintext)采取 數(shù)學方法進行函數(shù)轉換成密文(Ciphertext),只有特定接受方才能將其解密(Decryption)還原成明文的過程。

構成:

  • 明文(Plaintext):加密前的原始信息
  • 密文(Ciphertext):明文被加密后的信息
  • 密鑰(Key):控制加密算法和解密算法得以實現(xiàn)的關鍵信息,分為加密密鑰和解密密鑰(必然,密鑰不同,由明文生成的密文的結果也不同)
  • 加密(Encryption):將明文通過數(shù)學算法轉換成密文的過程
  • 解密(Decryption):將密文還原成明文的過程

## 密碼系統(tǒng)的一般模型

密碼系統(tǒng)的一般模型
  • 如果不論截取者獲得了多少密文,但在密文中都沒有足夠的信息來唯一地確定出對應的明文,則這一密碼體制稱為無條件安全的,或稱為理論上是不可破的(這種方法是不太容易獲得的,因此在現(xiàn)實生活中,更多是追求計算上安全即可。)
  • 如果密碼體制中的密碼不能被可使用的計算資源 破譯,則這一密碼體制稱為在計算上是安全的(利用已有的最好方法破譯某個密碼系統(tǒng)所需要的代價超出了破譯者的能力(如時間、空間、資金等資源))

# 對稱密鑰加密(單鑰加密)

對稱密鑰加密.jpg

常用的對稱加密算法:

  • DES/3DES(3重DES)
  • IDEA
  • RC5
  • AES(Rijndael)

DES早期用的很多,但是由于相對比較簡單,加密的安全性偏低,所以現(xiàn)在一般都使用3DES或 AES來替代。

對稱加密的優(yōu)缺點:

優(yōu)點:

  1. 算法公開(往往是標準算法,是可以公開的)
  2. 計算量小
  3. 加密速度快(核心是替換和移位,可以用硬件來實現(xiàn))
  4. 加密效率高。

缺點:

  1. 交易雙方都使用同樣鑰匙,安全性得不到保證。
  2. 每對用戶每次使用對稱加密算法時,都需要使用其他人不知道的唯一鑰匙,使得發(fā)收雙方所擁有的鑰匙數(shù)量呈幾何級數(shù)增長,密鑰管理成為用戶負擔。
  3. 密鑰分發(fā)比較困難,尤其網絡環(huán)境中安全難以保障,易成為瓶頸。

# AES(Rijndael)

下面摘自 一篇寫的非常全面的博客

AES, Advanced Encryption Standard,其實是一套標準:FIPS 197,而我們所說的AES算法其實是Rijndael算法。

AES-Mind.png

Rijndael算法是基于代換-置換網絡(SPN,Substitution-permutation network)的迭代算法。明文數(shù)據(jù)經過多輪次的轉換后方能生成密文,每個輪次的轉換操作由輪函數(shù)定義。輪函數(shù)任務就是根據(jù)密鑰編排序列(即輪密碼)對數(shù)據(jù)進行不同的代換及置換等操作。

補充代換-置換網絡SPN是一系列被應用于分組密碼中相關的數(shù)學運算,高級加密標準(AES)、3-Way、Kuznyechik、PRESENT、SAFER、SHARK、Square都有涉用。這種加密網絡使用明文塊和密鑰塊作為輸入,并通過交錯的若干“輪”(或“層”)代換操作和置換操作產生密文塊。代換(Substitution)和置換(Permutation)分別被稱作S盒(替換盒/S-boxes)和P盒(排列盒/P-boxes)。由于其實施于硬件的高效性,SPN的應用十分廣泛。

# iOS中的AES

部分摘自 簡書博客1,加了一些個人理解,有興趣可以直接移步原文

AES是開發(fā)中常用的加密算法之一。然而由于前后端開發(fā)使用的語言不統(tǒng)一,導致經常出現(xiàn)前端加密而后端不能解密的情況出現(xiàn)。然而無論什么語言系統(tǒng),AES的算法總是相同的, 因此導致結果不一致的原因在于 加密設置的參數(shù)不一致 。于是先來看看在兩個平臺使用AES加密時需要統(tǒng)一的幾個參數(shù)。

  • 密鑰長度(Key Size)
  • 加密模式(Cipher Mode)
  • 填充方式(Padding)
  • 初始向量(Initialization Vector)
密鑰長度

AES算法標準下,key的長度有三種:128、192和256 bits。由于歷史原因,JDK默認只支持不大于128 bits的密鑰,而128 bits的key已能夠滿足商用安全需求。因此本例先使用AES-128。(Java使用大于128 bits的key方法在文末提及)

加密模式

AES屬于塊加密(Block Cipher),塊加密中有ECB、CBC、CFB、OFB、CTR、CCM、GCM等幾種工作模式。本例統(tǒng)一使用CBC模式。

填充方式

由于塊加密只能對特定長度的數(shù)據(jù)塊進行加密,因此CBC、ECB模式需要在最后一數(shù)據(jù)塊加密前進行數(shù)據(jù)填充,解密后刪除掉填充的數(shù)據(jù)。(CFB,OFB和CTR模式由于與key進行加密操作的是上一塊加密后的密文,因此不需要對最后一段明文進行填充)

  • NoPadding
    顧名思義,不填充,自己對長度不足block size的部分進行填充
  • ZeroPadding
    數(shù)據(jù)長度不對齊時使用0填充,否則不填充(當原數(shù)據(jù)尾部也存在0時,在unpadding時可能會存在問題)。
  • PKCS7Padding
    如果數(shù)據(jù)長度需要填充n(n>0)個字節(jié)才對齊,那么填充n個字節(jié),每個字節(jié)都是n;
    如果數(shù)據(jù)本身就已經對齊了,則填充一塊長度為塊大小的數(shù)據(jù),每個字節(jié)都是塊大小。
  • PKCS5Padding
    PKCS7Padding的子集,塊大小固定為8字節(jié),其它一致(即PKCS5Padding是限制塊大小的PKCS7Padding)。
  • PKCS1Padding
    與RSA算法一起使用,這里不再贅述

附上文檔鏈接:
PKCS #7: Cryptographic Message Syntax 10.3節(jié)中講到了上面提到的填充算法, 對Block Size并沒有做規(guī)定
PKCS #5: Password-Based Cryptography Specification 在6.1.1 中對 填充做了說明,該標準只討論了 8字節(jié)(64位) 塊的加密, 對其他塊大小沒有做說明,其填充算法跟 PKCS7是一樣的

使用PKCS7Padding/PKCS5Padding填充時,最后一個字節(jié)肯定為填充數(shù)據(jù)的長度,所以在解密后,取最后一位,就可以準確刪除填充的數(shù)據(jù)。

在iOS SDK中提供了PKCS7Padding,而JDK則提供了PKCS5Padding(限制Block Size為8 bytes),但AES等算法,后來都把BlockSize擴充到了16字節(jié)或更大,Java中,采用PKCS5實質上就是采用PKCS7(PKCS5Padding與PKCS7Padding填充結果是相等的)。

初始向量

使用除ECB以外的其他加密模式均需要傳入一個初始向量,其大小(即串的長度)與Block Size相等(AES的Block Size為128 bits),而兩個平臺的API文檔均指明當不傳入初始向量時,系統(tǒng)將默認使用一個全0的初始向量。(在區(qū)塊加密中,使用了初始化向量的加密模式被稱為區(qū)塊加密模式)

以CBC為例:IV是長度為分組大小的一組隨機,通常情況下不用保密,不過在大多數(shù)情況下,針對同一密鑰不應多次使用同一組IV。 CBC要求第一個分組的明文在加密運算前先與IV進行異或;從第二組開始,所有的明文先與前一分組加密后的密文進行異或。

## iOS實現(xiàn)

//先定義一個初始向量IV的值。ECB模式不需要
NSString *const kInitVector = @"16-Bytes--String";
//確定密鑰長度,這里選擇 AES-128。即"密鑰是個16位字符串
size_t const kKeySize = kCCKeySizeAES128;

+ (NSString *)encryptAES:(NSString *)content key:(NSString *)key {

    NSData *contentData = [content dataUsingEncoding:NSUTF8StringEncoding];
    NSUInteger dataLength = contentData.length;
    
    // 為結束符'\0' +1
    char keyPtr[kKeySize + 1];
    memset(keyPtr, 0, sizeof(keyPtr));
    [key getCString:keyPtr maxLength:sizeof(keyPtr) encoding:NSUTF8StringEncoding];
    
    // 密文長度 <= 明文長度 + BlockSize
    size_t encryptSize = dataLength + kCCBlockSizeAES128;
    void *encryptedBytes = malloc(encryptSize);
    size_t actualOutSize = 0;
    
    NSData *initVector = [kInitVector dataUsingEncoding:NSUTF8StringEncoding];
    
    /*
      第三個參數(shù):先查看下枚舉說明,可以發(fā)現(xiàn)里面只有兩個枚舉變量,并在kCCOptionECBMode的旁邊,寫著Default is CBC.
        kCCOptionPKCS7Padding:表示函數(shù)運用CBC加密模式,并且使用PKCS7Padding的填充模式進行加密
        kCCOptionPKCS7Padding | kCCOptionECBMode:就表示函數(shù)運用ECB加密模式,并且使用PKCS7Padding的填充模式進行加密
        如果要設置NoPadding,可以填入0x0000
    */
    CCCryptorStatus cryptStatus = CCCrypt(kCCEncrypt, //加密/解密
                                          kCCAlgorithmAES, //選用的加密算法
                                          kCCOptionPKCS7Padding,  //設置工作模式+填充
                                          keyPtr,  //key
                                          kKeySize, // key length 
                                          initVector.bytes, // 初始向量IV的長度,如果不需要IV,設置為nil(不可以為@"")
                                          contentData.bytes,
                                          dataLength,
                                          encryptedBytes,
                                          encryptSize,
                                          &actualOutSize);
    
    if (cryptStatus == kCCSuccess) {
        // 對加密后的數(shù)據(jù)進行 base64 編碼
        return [[NSData dataWithBytesNoCopy:encryptedBytes length:actualOutSize] base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed];
    }
    free(encryptedBytes);
    return nil;
}

## Java實現(xiàn)

//同理先在類中定義一個初始向量,需要與iOS端的統(tǒng)一。
private static final String IV_STRING = "16-Bytes--String";
//另 Java 不需手動設置密鑰大小,系統(tǒng)會自動根據(jù)傳入的 Key 進行判斷。
public static String encryptAES(String content, String key) 
            throws InvalidKeyException, NoSuchAlgorithmException, 
            NoSuchPaddingException, UnsupportedEncodingException, 
            InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException {

    byte[] byteContent = content.getBytes("UTF-8");

    // 注意,為了能與 iOS 統(tǒng)一
    // 這里的 key 不可以使用 KeyGenerator、SecureRandom、SecretKey 生成
    byte[] enCodeFormat = key.getBytes();
    SecretKeySpec secretKeySpec = new SecretKeySpec(enCodeFormat, "AES");
        
    byte[] initParam = IV_STRING.getBytes();
    IvParameterSpec ivParameterSpec = new IvParameterSpec(initParam);
        
    // 指定加密的算法、工作模式和填充方式
    Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
    cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);
    
    byte[] encryptedBytes = cipher.doFinal(byteContent);
    
    // 同樣對加密后數(shù)據(jù)進行 base64 編碼
    Encoder encoder = Base64.getEncoder();
    return encoder.encodeToString(encryptedBytes);
}
關于Java使用大于128 bits的key

到Oracle官網下載對應Java版本的 JCE ,解壓后放到 JAVA_HOME/jre/lib/security/ ,然后修改 iOS 端的 kKeySize 和兩端對應的 key 即可。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

  • 目錄一、對稱加密?1、對稱加密是什么?2、對稱加密的優(yōu)點?3、對稱加密的問題?4、對稱加密的應用場景?5、對稱加密...
    意一ineyee閱讀 62,596評論 8 110
  • 這篇文章主要講述在Mobile BI(移動商務智能)開發(fā)過程中,在網絡通信、數(shù)據(jù)存儲、登錄驗證這幾個方面涉及的加密...
    雨_樹閱讀 3,042評論 0 6
  • 這兩天在整理網絡請求加密相關的知識,發(fā)現(xiàn)有篇文章總結的很不錯,轉載一下,整體知識結構如原作者文章所述,部分地方有加...
    南歌ccc閱讀 3,819評論 0 4
  • 我們今天一起來聊一聊是什么改變了我們的行為,從而呢改變我們的結果。我們說一個人的認知決定了行為,當然呢,雖然你的最...
    聽雨廖哥閱讀 391評論 0 0
  • 一直很糾結百家號,沒有真正放開,的確挺難的,只學一點點帶來的只是對自我的迷失,感性地談自己的感受可能會很好?用不成...
    平凡不普通版閱讀 167評論 0 1

友情鏈接更多精彩內容