iOS與Java的AES對(duì)稱加密的那些坑

AES是開(kāi)發(fā)中常用的加密算法之一箱玷。然而由于前后端開(kāi)發(fā)環(huán)境差異吮龄,導(dǎo)致出現(xiàn)前端加密而后端不能解密的情況出現(xiàn)拷窜。然而無(wú)論什么環(huán)境开皿,AES的算法總是相同的, 因此導(dǎo)致結(jié)果不一致的原因在于加密配置的參數(shù)不一致 装黑。于是先來(lái)看看在兩個(gè)平臺(tái)使用AES加密時(shí)需要統(tǒng)一的幾個(gè)參數(shù)副瀑。

  • 密鑰長(zhǎng)度(Key Size)

  • 加密模式(Cipher Mode)

  • 填充方式(Padding)

  • 初始向量(Initialization Vector)
    1.密鑰長(zhǎng)度

AES算法下,key的長(zhǎng)度有三種:128恋谭、192和256 bits糠睡。由于歷史原因,JDK默認(rèn)只支持不大于128 bits的密鑰疚颊,而128 bits的key已能夠滿足商用安全需求狈孔。因此本例先使用AES-128。(Java使用大于128 bits的key方法在文末提及)

2.加密模式

AES屬于分組加密(Block Cipher)材义,塊加密中有CBC均抽、ECB、CTR其掂、OFB油挥、CFB等幾種工作模式。本例統(tǒng)一使用CBC模式款熬。

3.填充方式

由于分組加密只能對(duì)特定長(zhǎng)度的數(shù)據(jù)塊進(jìn)行加密深寥,因此CBC、ECB模式需要在最后一數(shù)據(jù)塊加密前進(jìn)行數(shù)據(jù)填充贤牛。(CFB惋鹅,OFB和CTR模式由于與key進(jìn)行加密操作的是上一塊加密后的密文,因此不需要對(duì)最后一段明文進(jìn)行填充)

在iOS SDK中提供了PKCS7Padding殉簸,而JDK則提供了PKCS5Padding闰集。原則上PKCS5Padding限制了填充的Block Size為8 bytes,而Java實(shí)際上當(dāng)塊大于該值時(shí)般卑,其PKCS5Padding與PKCS7Padding是相等的:每需要填充χ個(gè)字節(jié)武鲁,填充的值就是χ。
4.初始向量

使用除ECB以外的其他加密模式均需要傳入一個(gè)初始向量蝠检,其大小與Block Size相等(AES的Block Size為128 bits)洞坑,而兩個(gè)平臺(tái)的API文檔均指明當(dāng)不傳入初始向量時(shí),系統(tǒng)將默認(rèn)使用一個(gè)全0的初始向量蝇率。

有了上述的基礎(chǔ)之后迟杂,可以開(kāi)始分別在兩個(gè)平臺(tái)進(jìn)行實(shí)現(xiàn)了刽沾。

iOS實(shí)現(xiàn)

先定義一個(gè)初始向量的值。

NSString *const kInitVector = @"16-Bytes--String";// 16個(gè)字節(jié)的初始化向量

確定密鑰長(zhǎng)度排拷,這里選擇 AES-128侧漓。

size_t const kKeySize = kCCKeySizeAES128;

加密方法

+ (NSString *)encryptAES:(NSString *)content key:(NSString *)key {
NSData *contentData = [content dataUsingEncoding:NSUTF8StringEncoding];
NSUInteger dataLength = contentData.length;
// 為結(jié)束符'\\0' +1
char keyPtr[kKeySize + 1];
memset(keyPtr, 0, sizeof(keyPtr));
[key getCString:keyPtr maxLength:sizeof(keyPtr) encoding:NSUTF8StringEncoding];
// 密文長(zhǎng)度 <= 明文長(zhǎng)度 + BlockSize
size_t encryptSize = dataLength + kCCBlockSizeAES128;
void *encryptedBytes = malloc(encryptSize);
size_t actualOutSize = 0;
NSData *initVector = [kInitVector dataUsingEncoding:NSUTF8StringEncoding];
CCCryptorStatus cryptStatus = CCCrypt(kCCEncrypt,
kCCAlgorithmAES,
kCCOptionPKCS7Padding,  // 系統(tǒng)默認(rèn)使用 CBC,然后指明使用 PKCS7Padding
keyPtr,
kKeySize,
initVector.bytes,
contentData.bytes,
dataLength,
encryptedBytes,
encryptSize,
&actualOutSize);
if (cryptStatus == kCCSuccess) {
// 對(duì)加密后的數(shù)據(jù)進(jìn)行 base64 編碼
return [[NSData dataWithBytesNoCopy:encryptedBytes length:actualOutSize] base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed];
}
free(encryptedBytes);
return nil;
}

解密方法

+ (NSString *)decryptAES:(NSString *)content key:(NSString *)key {
// 把 base64 String 轉(zhuǎn)換成 Data
NSData *contentData = [[NSData alloc] initWithBase64EncodedString:content options:NSDataBase64DecodingIgnoreUnknownCharacters];
NSUInteger dataLength = contentData.length;
char keyPtr[kKeySize + 1];
memset(keyPtr, 0, sizeof(keyPtr));
[key getCString:keyPtr maxLength:sizeof(keyPtr) encoding:NSUTF8StringEncoding];
size_t decryptSize = dataLength + kCCBlockSizeAES128;
void *decryptedBytes = malloc(decryptSize);
size_t actualOutSize = 0;
NSData *initVector = [kInitVector dataUsingEncoding:NSUTF8StringEncoding];
CCCryptorStatus cryptStatus = CCCrypt(kCCDecrypt,
kCCAlgorithmAES,
kCCOptionPKCS7Padding,
keyPtr,
kKeySize,
initVector.bytes,
contentData.bytes,
dataLength,
decryptedBytes,
decryptSize,
&actualOutSize);
if (cryptStatus == kCCSuccess) {
return [[NSString alloc] initWithData:[NSData dataWithBytesNoCopy:decryptedBytes length:actualOutSize] encoding:NSUTF8StringEncoding];
}
free(decryptedBytes);
return nil;
}

Java實(shí)現(xiàn)

同理先在類中定義一個(gè)初始向量监氢,需要與iOS端的統(tǒng)一布蔗。

private static final String IV_STRING = "16-Bytes--String";

另 Java 不需手動(dòng)設(shè)置密鑰大小,系統(tǒng)會(huì)自動(dòng)根據(jù)傳入的 Key 進(jìn)行判斷浪腐。

加密方法

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);
// 同樣對(duì)加密后數(shù)據(jù)進(jìn)行 base64 編碼
Encoder encoder = Base64.getEncoder();
return encoder.encodeToString(encryptedBytes);
}

解密方法

public static String decryptAES(String content, String key)
throws InvalidKeyException, NoSuchAlgorithmException,
NoSuchPaddingException, InvalidAlgorithmParameterException,
IllegalBlockSizeException, BadPaddingException, UnsupportedEncodingException {
// base64 解碼
Decoder decoder = Base64.getDecoder();
byte[] encryptedBytes = decoder.decode(content);
byte[] enCodeFormat = key.getBytes();
SecretKeySpec secretKey = new SecretKeySpec(enCodeFormat, "AES");
byte[] initParam = IV_STRING.getBytes();
IvParameterSpec ivParameterSpec = new IvParameterSpec(initParam);
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, secretKey, ivParameterSpec);
byte[] result = cipher.doFinal(encryptedBytes);
return new String(result, "UTF-8");
}

至此,AES 在 iOS 與 Java 同步實(shí)現(xiàn)完成特漩。

注意以上實(shí)現(xiàn)的是 AES-128吧雹,因此方法傳入的 key 需為長(zhǎng)度為 16 的字符串。

關(guān)于Java使用大于128 bits的key

到Oracle官網(wǎng)下載對(duì)應(yīng)Java版本的 JCE 涂身,解壓后放到 JAVA_HOME/jre/lib/security/ 雄卷,然后修改 iOS 端的 kKeySize 和兩端對(duì)應(yīng)的 key 即可。

github鏈接:https://github.com/WelkinXie/AESCipher-iOS

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末蛤售,一起剝皮案震驚了整個(gè)濱河市丁鹉,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌悴能,老刑警劉巖揣钦,帶你破解...
    沈念sama閱讀 216,651評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異搜骡,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)佑女,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,468評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門记靡,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人团驱,你說(shuō)我怎么就攤上這事摸吠。” “怎么了嚎花?”我有些...
    開(kāi)封第一講書人閱讀 162,931評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵寸痢,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我紊选,道長(zhǎng)啼止,這世上最難降的妖魔是什么道逗? 我笑而不...
    開(kāi)封第一講書人閱讀 58,218評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮献烦,結(jié)果婚禮上滓窍,老公的妹妹穿的比我還像新娘。我一直安慰自己巩那,他們只是感情好吏夯,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,234評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著即横,像睡著了一般噪生。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上东囚,一...
    開(kāi)封第一講書人閱讀 51,198評(píng)論 1 299
  • 那天跺嗽,我揣著相機(jī)與錄音,去河邊找鬼舔庶。 笑死抛蚁,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的惕橙。 我是一名探鬼主播瞧甩,決...
    沈念sama閱讀 40,084評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼弥鹦!你這毒婦竟也來(lái)了肚逸?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書人閱讀 38,926評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤彬坏,失蹤者是張志新(化名)和其女友劉穎朦促,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體栓始,經(jīng)...
    沈念sama閱讀 45,341評(píng)論 1 311
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡务冕,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,563評(píng)論 2 333
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了幻赚。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片禀忆。...
    茶點(diǎn)故事閱讀 39,731評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖落恼,靈堂內(nèi)的尸體忽然破棺而出箩退,到底是詐尸還是另有隱情,我是刑警寧澤佳谦,帶...
    沈念sama閱讀 35,430評(píng)論 5 343
  • 正文 年R本政府宣布戴涝,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏啥刻。R本人自食惡果不足惜奸鸯,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,036評(píng)論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望郑什。 院中可真熱鬧府喳,春花似錦、人聲如沸蘑拯。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 31,676評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)申窘。三九已至弯蚜,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間剃法,已是汗流浹背碎捺。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 32,829評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留贷洲,地道東北人收厨。 一個(gè)月前我還...
    沈念sama閱讀 47,743評(píng)論 2 368
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像优构,于是被迫代替她去往敵國(guó)和親诵叁。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,629評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容

  • AES是開(kāi)發(fā)中常用的加密算法之一钦椭。然而由于前后端開(kāi)發(fā)使用的語(yǔ)言不統(tǒng)一拧额,導(dǎo)致經(jīng)常出現(xiàn)前端加密而后端不能解密的情況出現(xiàn)...
    WelkinXie閱讀 27,065評(píng)論 35 86
  • 一:前言 AES是開(kāi)發(fā)中常用的加密算法之一侥锦。然而由于前后端開(kāi)發(fā)使用的語(yǔ)言不統(tǒng)一,導(dǎo)致經(jīng)常出現(xiàn)前端加密而后端不能解密...
    LikeSomeBody閱讀 2,386評(píng)論 1 1
  • 這篇文章主要講述在Mobile BI(移動(dòng)商務(wù)智能)開(kāi)發(fā)過(guò)程中德挣,在網(wǎng)絡(luò)通信恭垦、數(shù)據(jù)存儲(chǔ)、登錄驗(yàn)證這幾個(gè)方面涉及的加密...
    雨_樹(shù)閱讀 2,414評(píng)論 0 6
  • 在開(kāi)發(fā)應(yīng)用過(guò)程中格嗅,客戶端與服務(wù)端經(jīng)常需要進(jìn)行數(shù)據(jù)傳輸番挺,涉及到重要隱私安全信息時(shí),開(kāi)發(fā)者自然會(huì)想到對(duì)其進(jìn)行加密吗浩,即使...
    閑庭閱讀 3,267評(píng)論 0 11
  • 在大一的時(shí)候建芙,我嘗試寫過(guò)話劇劇本没隘,但我一直沒(méi)能如愿完成懂扼。我發(fā)現(xiàn)我的矛盾在于,我想讓劇情和細(xì)節(jié)更貼近生活或者來(lái)源于日...
    Aemeath2501閱讀 220評(píng)論 0 0