目錄
- 數(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)的一般模型

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

常用的對稱加密算法:
- DES/3DES(3重DES)
- IDEA
- RC5
- AES(Rijndael)
DES早期用的很多,但是由于相對比較簡單,加密的安全性偏低,所以現(xiàn)在一般都使用3DES或 AES來替代。
對稱加密的優(yōu)缺點:
優(yōu)點:
- 算法公開(往往是標準算法,是可以公開的)
- 計算量小
- 加密速度快(核心是替換和移位,可以用硬件來實現(xiàn))
- 加密效率高。
缺點:
- 交易雙方都使用同樣鑰匙,安全性得不到保證。
- 每對用戶每次使用對稱加密算法時,都需要使用其他人不知道的唯一鑰匙,使得發(fā)收雙方所擁有的鑰匙數(shù)量呈幾何級數(shù)增長,密鑰管理成為用戶負擔。
- 密鑰分發(fā)比較困難,尤其網絡環(huán)境中安全難以保障,易成為瓶頸。
# AES(Rijndael)
下面摘自 一篇寫的非常全面的博客
AES, Advanced Encryption Standard,其實是一套標準:FIPS 197,而我們所說的AES算法其實是Rijndael算法。

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 即可。