iOS與PHP/Android AES128 ECB NoPadding加密

來自: http://www.henishuo.com/ios-aes128-ecb-nopadding/

前言

談談AES加密,網上有很多的版本,當我沒有真正在加密安全問題前,總以為百度出來某個AES加密算法就可以直接使用,實際上當我真正要做加密時,遇到了很多的坑,原來不是拿過來就能用的。寫下本篇文章,記錄下曾經遇到的坑,嚴防以后再出現同樣的坑。

AES規則

原輸入數據不夠16字節的整數位時,就要補齊。因此就會有padding,若使用不同的padding,那么加密出來的結果也會不一樣。

AES加密算法

蘋果提供給我們的API只有這一個函數用來加密或者解密:

 
CCCryptorStatus CCCrypt(
    CCOperation op,        /* kCCEncrypt, etc. */
    CCAlgorithm alg,        /* kCCAlgorithmAES128, etc. */
    CCOptions options,      /* kCCOptionPKCS7Padding, etc. */
    const void *key,
    size_tkeyLength,
    const void *iv,        /* optional initialization vector */
    const void *dataIn,    /* optional per op and alg */
    size_tdataInLength,
    void *dataOut,          /* data RETURNED here */
    size_tdataOutAvailable,
    size_t*dataOutMoved)
    __OSX_AVAILABLE_STARTING(__MAC_10_4, __IPHONE_2_0);
 
  • 其中第一個 CCOperation 只有兩個值,要么是 kCCEncrypt 表示加密,要么是 kCCDecrypt 表示解密。
  • 第二個參數表示加密的算法,它只有以下向種類型:
 
enum {
    kCCAlgorithmAES128 = 0,
    kCCAlgorithmAES = 0,
    kCCAlgorithmDES,
    kCCAlgorithm3DES,      
    kCCAlgorithmCAST,      
    kCCAlgorithmRC4,
    kCCAlgorithmRC2,  
    kCCAlgorithmBlowfish    
};
typedef uint32_tCCAlgorithm;
 

我們這里使用的是 kCCAlgorithmAES128 表示使用AES128位加密。

  • 第三個參數表示選項,這里使用的是 kCCOptionECBMode ,表示ECB:
 
enum {
    /* options for block ciphers */
    kCCOptionPKCS7Padding  = 0x0001,
    kCCOptionECBMode        = 0x0002
    /* stream ciphers currently have no options */
};
typedef uint32_tCCOptions;
 
  • 第四個參數表示加密/解密的密鑰。
  • 第五個參數keyLength表示密鑰的長度。
  • 第六個參數iv是個固定值,通過直接使用密鑰即可。大家一定要注視這個參數,如果安卓、服務端和iOS端不統一,那么加密結果就會不一樣,解密可能能解出來,但是解密后在末尾會出現一些\0、\t之類的。
  • 第七個參數dataIn表示要加密/解密的數據。
  • 第八個參數dataInLength表示要加密/解密的數據的長度。
  • 第九個參數dataOut用于接收加密后/解密后的結果。
  • 第十個參數dataOutAvailable表示加密后/解密后的數據的長度。
  • 第十一個參數dataOutMoved表示實際加密/解密的數據的長度。(因為有補齊)

加密算法

依賴于第三方庫: GTMBase64 ,這個庫已經幾年沒有維護了,現在還是MRC版本,要使用請到GITHUB查看使用教程,那里有ARC接入說明:

 
+ (NSString *)hyb_AESEncrypt:(NSString *)plainTextpassword:(NSString *)key {
  if (key == nil || (key.length != 16 && key.length != 32)) {
    return nil;
  }
  
  char keyPtr[kCCKeySizeAES128+1];
  memset(keyPtr, 0, sizeof(keyPtr));
  [keygetCString:keyPtrmaxLength:sizeof(keyPtr)encoding:NSUTF8StringEncoding];
  
  
  char ivPtr[kCCBlockSizeAES128+1];
  memset(ivPtr, 0, sizeof(ivPtr));
  [keygetCString:ivPtrmaxLength:sizeof(ivPtr)encoding:NSUTF8StringEncoding];
  
  NSData* data = [plainTextdataUsingEncoding:NSUTF8StringEncoding];
  NSUInteger dataLength = [datalength];
  
  int diff = kCCKeySizeAES128 - (dataLength % kCCKeySizeAES128);
  unsigned long newSize = 0;
  
  if(diff > 0) {
    newSize = dataLength + diff;
  }
  
  char dataPtr[newSize];
  memcpy(dataPtr, [databytes], [datalength]);
  for(int i = 0; i < diff; i++) {
    // 這里是關鍵,這里是使用NoPadding的
    dataPtr[i + dataLength] = 0x0000;
  }
  
  size_tbufferSize = newSize + kCCBlockSizeAES128;
  void *buffer = malloc(bufferSize);
  memset(buffer, 0, bufferSize);
  
  size_tnumBytesCrypted = 0;
  
  CCCryptorStatus cryptStatus = CCCrypt(kCCEncrypt,
                                        kCCAlgorithmAES128,
                                        kCCOptionECBMode,
                                        [keyUTF8String],
                                        kCCKeySizeAES128,
                                        ivPtr,
                                        dataPtr,
                                        sizeof(dataPtr),
                                        buffer,
                                        bufferSize,
                                        &numBytesCrypted);
  
  if (cryptStatus == kCCSuccess) {
    NSData *resultData = [NSDatadataWithBytesNoCopy:bufferlength:numBytesCrypted];
    return [GTMBase64stringByEncodingData:resultData];
  }
  
  free(buffer);
  return nil;
}
 

對于加密算法,大家一定要注意,保證iOS、安卓、服務端的加密規則是一定的,建議統一使用No Padding的,這里使用No Padding是這樣的:

 
for(int i = 0; i < diff; i++) {
    // 這里是關鍵,這里是使用NoPadding的
    dataPtr[i + dataLength] = 0x0000;
}
 

其實所謂Padding就是指在位數不夠需要補齊時,使用什么來填充,而No Padding就是使用16個0,對應0x0000.如果三端不統一,加密出來就算能解密,也會出現一些奇怪的字符,甚至會有部分亂碼。

另外,這里使用的是 kCCOptionECBMode ,也就是ECB。在安卓端和PHP端,也得使用ECB。在調試過程中,發現PHP使用CBC解密不了IOS端的。于是改成了使用ECB。

解密算法

依賴于第三方庫: GTMBase64 ,這個庫已經幾年沒有維護了,現在還是MRC版本,要使用請到GITHUB查看使用教程,那里有ARC接入說明:

 
+ (NSString *)hyb_AESDecrypt:(NSString *)encryptTextpassword:(NSString *)key {
  if (key == nil || (key.length != 16 && key.length != 32)) {
    return nil;
  }
  
  char keyPtr[kCCKeySizeAES128 + 1];
  memset(keyPtr, 0, sizeof(keyPtr));
  [keygetCString:keyPtrmaxLength:sizeof(keyPtr)encoding:NSUTF8StringEncoding];
  
  char ivPtr[kCCBlockSizeAES128 + 1];
  memset(ivPtr, 0, sizeof(ivPtr));
  [keygetCString:ivPtrmaxLength:sizeof(ivPtr)encoding:NSUTF8StringEncoding];
  
  NSData *data = [GTMBase64decodeData:[encryptTextdataUsingEncoding:NSUTF8StringEncoding]];
  NSUInteger dataLength = [datalength];
  size_tbufferSize = dataLength + kCCBlockSizeAES128;
  void *buffer = malloc(bufferSize);
  
  size_tnumBytesCrypted = 0;
  CCCryptorStatus cryptStatus = CCCrypt(kCCDecrypt,
                                        kCCAlgorithmAES128,
                                        kCCOptionECBMode,
                                        [keyUTF8String],
                                        kCCBlockSizeAES128,
                                        ivPtr,
                                        [databytes],
                                        dataLength,
                                        buffer,
                                        bufferSize,
                                        &numBytesCrypted);
  if (cryptStatus == kCCSuccess) {
    NSData *resultData = [NSDatadataWithBytesNoCopy:bufferlength:numBytesCrypted];
    
    NSString *decoded=[[NSString alloc]initWithData:resultDataencoding:NSUTF8StringEncoding];
    return decoded;
  }
  
  free(buffer);
  return nil;
}
 

解密時也得跟加密一樣指定為ECB,否則解出來會出現亂碼,或者末尾會出現\0、\t之類的符號。

寫在最后

開發中總會遇到各種坑,網上查了很多的資料,但是終究沒有說明解決的辦法,而是只將自己的代碼放出來。對于剛接觸這方面知識的開發人員來說,是很懵懂的。甚至很多新手會覺得系統就是這樣的,我也沒辦法。其實總會有解決辦法的,關鍵在于與其他各端統一連調。

關注我

Swift/ObjC技術群一:324400294(已滿)

Swift/ObjC技術群二:494669518

ObjC/Swift高級群:461252383(注明年限,新手勿擾)

關注微信公眾號: iOSDevShares

關注新浪微博賬號:標哥Jacky

標哥的GITHUB地址: CoderJackyHuang

支持并捐助

如果您覺得文章對您很有幫忙,希望得到您的支持。您的捐肋將會給予我最大的鼓勵,感謝您的支持!

<th>微信捐助</th>

</tr>

</thead>

<td> <img src="https://simg.open-open.com/show/3bef3b99597282da299c6e97fa777537.jpg" class="alignCenter" width="320" height="442" /> </td>

</tr>

</tbody>

</table> </div>

 本文由用戶 Roc17M 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!
支付寶捐助
  • sesese色