AES加密——Java與iOS的解決方案

維基百科中對(duì)AES加密的解釋是這樣的:

高級(jí)加密標(biāo)準(zhǔn)(英語:Advanced Encryption Standard幽告,縮寫:AES)睬辐,在密碼學(xué)中又稱Rijndael加密法摩瞎,是美國聯(lián)邦政府采用的一種區(qū)塊加密標(biāo)準(zhǔn)篮绿。這個(gè)標(biāo)準(zhǔn)用來替代原先的DES饱普,已經(jīng)被多方分析且廣為全世界所使用运挫。經(jīng)過五年的甄選流程状共,高級(jí)加密標(biāo)準(zhǔn)由美國國家標(biāo)準(zhǔn)與技術(shù)研究院(NIST)于2001年11月26日發(fā)布于FIPS PUB 197,并在2002年5月26日成為有效的標(biāo)準(zhǔn)谁帕。2006年峡继,高級(jí)加密標(biāo)準(zhǔn)已然成為對(duì)稱密鑰加密中最流行的算法之一。

該算法為比利時(shí)密碼學(xué)家Joan Daemen和Vincent Rijmen所設(shè)計(jì)匈挖,結(jié)合兩位作者的名字碾牌,以Rijndael為名投稿高級(jí)加密標(biāo)準(zhǔn)的甄選流程。(Rijndael的發(fā)音近于"Rhine doll")

嚴(yán)格地說关划,AES和Rijndael加密法并不完全一樣(雖然在實(shí)際應(yīng)用中兩者可以互換)小染,因?yàn)镽ijndael加密法可以支持更大范圍的區(qū)塊和密鑰長度:AES的區(qū)塊長度固定為128 比特,密鑰長度則可以是128贮折,192或256比特裤翩;而Rijndael使用的密鑰和區(qū)塊長度可以是32位的整數(shù)倍,以128位為下限调榄,256比特為上限踊赠。加密過程中使用的密鑰是由Rijndael密鑰生成方案產(chǎn)生。

大多數(shù)AES計(jì)算是在一個(gè)特別的有限域完成的每庆。

AES加密過程是在一個(gè)4×4的字節(jié)矩陣上運(yùn)作筐带,這個(gè)矩陣又稱為“體(state)”,其初值就是一個(gè)明文區(qū)塊(矩陣中一個(gè)元素大小就是明文區(qū)塊中的一個(gè)Byte)缤灵。(Rijndael加密法因支持更大的區(qū)塊伦籍,其矩陣行數(shù)可視情況增加)加密時(shí),各輪AES加密循環(huán)(除最后一輪外)均包含4個(gè)步驟:

  1. AddRoundKey—矩陣中的每一個(gè)字節(jié)都與該次回合密鑰(round key)做XOR運(yùn)算腮出;每個(gè)子密鑰由密鑰生成方案產(chǎn)生帖鸦。

  2. SubBytes—通過一個(gè)非線性的替換函數(shù),用查找表的方式把每個(gè)字節(jié)替換成對(duì)應(yīng)的字節(jié)胚嘲。

  3. ShiftRows—將矩陣中的每個(gè)橫列進(jìn)行循環(huán)式移位作儿。

  4. MixColumns—為了充分混合矩陣中各個(gè)直行的操作。這個(gè)步驟使用線性轉(zhuǎn)換來混合每內(nèi)聯(lián)的四個(gè)字節(jié)馋劈。最后一個(gè)加密循環(huán)中省略MixColumns步驟攻锰,而以另一個(gè)AddRoundKey替換。

而如今妓雾,移動(dòng)端在和后端約定使用AES加密方式加密后娶吞,總會(huì)碰到一些問題,今天我就用iOS端和Java端為例子君珠,講解移動(dòng)端和后端的AES加密方法寝志。

首先,我們選用AES加密方式時(shí)策添,要先確定mode加密模式以及pad填充方式材部,而在這個(gè)項(xiàng)目中我選擇了CBC加密模式以及PKCS5填充方式,并且使用了AES+Base64數(shù)據(jù)混合加密與解密。

這些模式以及填充方式的選擇唯竹,在我們的代碼中會(huì)有體現(xiàn)乐导。

iOS平臺(tái)的AES加密

首先我們先創(chuàng)建一個(gè)NSData類category。并且引用頭文件

#import <CommonCrypto/CommonCryptor.h>

單純使用AES加密解密的代碼如下

//(key和iv向量這里是16位的) 這里是CBC加密模式浸颓,安全性更高

- (NSData *)AES128EncryptWithKey:(NSString *)key gIv:(NSString *)Iv{//加密
    char keyPtr[kCCKeySizeAES128+1];
    bzero(keyPtr, sizeof(keyPtr));
    [key getCString:keyPtr maxLength:sizeof(keyPtr) encoding:NSUTF8StringEncoding];
    
    char ivPtr[kCCKeySizeAES128+1];
    memset(ivPtr, 0, sizeof(ivPtr));
    [Iv getCString:ivPtr maxLength:sizeof(ivPtr) encoding:NSUTF8StringEncoding];
    
    NSUInteger dataLength = [self length];
    size_t bufferSize = dataLength + kCCBlockSizeAES128;
    void *buffer = malloc(bufferSize);
    size_t numBytesEncrypted = 0;
    CCCryptorStatus cryptStatus = CCCrypt(kCCEncrypt,
                                          kCCAlgorithmAES128,
                                          kCCOptionPKCS7Padding,
                                          keyPtr,
                                          kCCBlockSizeAES128,
                                          ivPtr,
                                          [self bytes],
                                          dataLength,
                                          buffer,
                                          bufferSize,
                                          &numBytesEncrypted);
    if (cryptStatus == kCCSuccess) {
        return [NSData dataWithBytesNoCopy:buffer length:numBytesEncrypted];
    }
    free(buffer);
    return nil;
}

//AES解密
- (NSData *)AES128DecryptWithKey:(NSString *)key gIv:(NSString *)Iv {
    char keyPtr[kCCKeySizeAES128+1];
    bzero(keyPtr, sizeof(keyPtr));
    [key getCString:keyPtr maxLength:sizeof(keyPtr) encoding:NSUTF8StringEncoding];
    
    char ivPtr[kCCKeySizeAES128+1];
    memset(ivPtr, 0, sizeof(ivPtr));
    [Iv getCString:ivPtr maxLength:sizeof(ivPtr) encoding:NSUTF8StringEncoding];
    
    NSUInteger dataLength = [self length];
    size_t bufferSize = dataLength + kCCBlockSizeAES128;
    void *buffer = malloc(bufferSize);
    size_t numBytesDecrypted = 0;
    CCCryptorStatus cryptStatus = CCCrypt(kCCDecrypt,
                                          kCCAlgorithmAES128,
                                          kCCOptionPKCS7Padding,
                                          keyPtr,
                                          kCCBlockSizeAES128,
                                          ivPtr,
                                          [self bytes],
                                          dataLength,
                                          buffer,
                                          bufferSize,
                                          &numBytesDecrypted);
    if (cryptStatus == kCCSuccess) {
        return [NSData dataWithBytesNoCopy:buffer length:numBytesDecrypted];
    }
    free(buffer);
    return nil;
}


而我們之前說了物臂,這里是需要用AES+Base64數(shù)據(jù)混合加密與解密。

那么之后一個(gè)完整詳細(xì)的加密過程是怎么樣的呢产上。

來看接下來的代碼

#pragma mark - AES加密
//將string轉(zhuǎn)成帶密碼的data
+(NSString*)neu_encryptAESData:(NSString*)string
{
    //將nsstring轉(zhuǎn)化為nsdata
    NSData *data = [string dataUsingEncoding:NSUTF8StringEncoding];
    //使用密碼對(duì)nsdata進(jìn)行加密
    NSData *encryptedData = [data AES128EncryptWithKey:KEY gIv:Iv];
    //返回進(jìn)行base64進(jìn)行轉(zhuǎn)碼的加密字符串
    return [self encodeBase64Data:encryptedData];
}

上面就是我們使用的加密方法棵磷,注釋很詳細(xì),當(dāng)然了 - encodeBase64Data: 方法是我已經(jīng)封裝好了的晋涣,到時(shí)候下載的時(shí)候拿出來用就好了仪媒。

所以在我們加密解密時(shí),只要去調(diào)用+(NSString*)neu_encryptAESData:(NSString*)string 這個(gè)方法就可以了谢鹊,是不是其實(shí)非常簡潔方便呢算吩。 解密的代碼我也貼一下,是一樣簡單的佃扼。

#pragma mark - AES解密
//將帶密碼的data轉(zhuǎn)成string
+(NSString*)neu_decryptAESData:(NSString *)string
{
   //base64解密
   NSData *decodeBase64Data=[NEUBase64 decodeString:string];
   //使用密碼對(duì)data進(jìn)行解密
   NSData *decryData = [decodeBase64Data AES128DecryptWithKey:KEY gIv:Iv];
   //將解了密碼的nsdata轉(zhuǎn)化為nsstring
   NSString *str = [[NSString alloc] initWithData:decryData encoding:NSUTF8StringEncoding];
   return str;
}

iOS平臺(tái)的AES加密到這里就結(jié)束了偎巢。

Java平臺(tái)的AES加密

Java平臺(tái)的加密解密,所有的配置和原理和iOS端都是一樣的兼耀,所以我就偷懶了压昼,直接把Java端的代碼貼上來了。

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;

/**
* Created by Lix on 16/9/21.
*/
public class AESOperator {
   /*
    * 加密用的Key 可以用26個(gè)字母和數(shù)字組成 此處使用AES-128-CBC加密模式瘤运,key需要為16位窍霞。
    */
   private String sKey = "ed16b1f8a9e648d4";
   private String ivParameter = "ed16b1f8a9e648d4";
   private static AESOperator instance = null;

   private AESOperator() {

   }

   public static AESOperator getInstance() {
       if (instance == null)
           instance = new AESOperator();
       return instance;
   }

   public static String Encrypt(String encData ,String secretKey,String vector) throws Exception {

       if(secretKey == null) {
           return null;
       }
       if(secretKey.length() != 16) {
           return null;
       }
       Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
       byte[] raw = secretKey.getBytes();
       SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
       IvParameterSpec iv = new IvParameterSpec(vector.getBytes());// 使用CBC模式,需要一個(gè)向量iv尽超,可增加加密算法的強(qiáng)度
       cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv);
       byte[] encrypted = cipher.doFinal(encData.getBytes("utf-8"));
       return new BASE64Encoder().encode(encrypted);// 此處使用BASE64做轉(zhuǎn)碼官撼。
   }


   // 加密
   public String encrypt(String sSrc) throws Exception {
       Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
       byte[] raw = sKey.getBytes();
       SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
       IvParameterSpec iv = new IvParameterSpec(ivParameter.getBytes());// 使用CBC模式,需要一個(gè)向量iv似谁,可增加加密算法的強(qiáng)度
       cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv);
       byte[] encrypted = cipher.doFinal(sSrc.getBytes("utf-8"));
       return new BASE64Encoder().encode(encrypted);// 此處使用BASE64做轉(zhuǎn)碼傲绣。
   }

   // 解密
   public String decrypt(String sSrc) throws Exception {
       try {
           byte[] raw = sKey.getBytes("ASCII");
           SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
           Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
           IvParameterSpec iv = new IvParameterSpec(ivParameter.getBytes());
           cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv);
           byte[] encrypted1 = new BASE64Decoder().decodeBuffer(sSrc);// 先用base64解密
           byte[] original = cipher.doFinal(encrypted1);
           String originalString = new String(original, "utf-8");
           return originalString;
       } catch (Exception ex) {
           return null;
       }
   }

   public String decrypt(String sSrc,String key,String ivs) throws Exception {
       try {
           byte[] raw = key.getBytes("ASCII");
           SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
           Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
           IvParameterSpec iv = new IvParameterSpec(ivs.getBytes());
           cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv);
           byte[] encrypted1 = new BASE64Decoder().decodeBuffer(sSrc);// 先用base64解密
           byte[] original = cipher.doFinal(encrypted1);
           String originalString = new String(original, "utf-8");
           return originalString;
       } catch (Exception ex) {
           return null;
       }
   }

   public static String encodeBytes(byte[] bytes) {
       StringBuffer strBuf = new StringBuffer();

       for (int i = 0; i < bytes.length; i++) {
           strBuf.append((char) (((bytes[i] >> 4) & 0xF) + ((int) 'a')));
           strBuf.append((char) (((bytes[i]) & 0xF) + ((int) 'a')));
       }

       return strBuf.toString();
   }

   public static void main(String[] args) throws Exception {
       // 需要加密的字串
       String cSrc = "123456";

       // 加密
       long lStart = System.currentTimeMillis();
       String enString = AESOperator.getInstance().encrypt(cSrc);
       System.out.println("加密后的字串是:" + enString);

       long lUseTime = System.currentTimeMillis() - lStart;
       System.out.println("加密耗時(shí):" + lUseTime + "毫秒");
       // 解密
       lStart = System.currentTimeMillis();
       String DeString = AESOperator.getInstance().decrypt(enString);
       System.out.println("解密后的字串是:" + DeString);
       lUseTime = System.currentTimeMillis() - lStart;
       System.out.println("解密耗時(shí):" + lUseTime + "毫秒");
   }
}

Java端和iOS端的代碼,都在這里,希望對(duì)您有幫助的可以Star一下巩踏。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末秃诵,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子塞琼,更是在濱河造成了極大的恐慌菠净,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,941評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異毅往,居然都是意外死亡牵咙,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,397評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門攀唯,熙熙樓的掌柜王于貴愁眉苦臉地迎上來洁桌,“玉大人,你說我怎么就攤上這事侯嘀×砹瑁” “怎么了?”我有些...
    開封第一講書人閱讀 165,345評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵戒幔,是天一觀的道長吠谢。 經(jīng)常有香客問我,道長诗茎,這世上最難降的妖魔是什么工坊? 我笑而不...
    開封第一講書人閱讀 58,851評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮错沃,結(jié)果婚禮上栅组,老公的妹妹穿的比我還像新娘。我一直安慰自己枢析,他們只是感情好玉掸,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,868評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著醒叁,像睡著了一般司浪。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上把沼,一...
    開封第一講書人閱讀 51,688評(píng)論 1 305
  • 那天啊易,我揣著相機(jī)與錄音,去河邊找鬼饮睬。 笑死租谈,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的捆愁。 我是一名探鬼主播割去,決...
    沈念sama閱讀 40,414評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼昼丑!你這毒婦竟也來了呻逆?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,319評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤菩帝,失蹤者是張志新(化名)和其女友劉穎咖城,沒想到半個(gè)月后茬腿,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,775評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡宜雀,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評(píng)論 3 336
  • 正文 我和宋清朗相戀三年切平,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片州袒。...
    茶點(diǎn)故事閱讀 40,096評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡揭绑,死狀恐怖弓候,靈堂內(nèi)的尸體忽然破棺而出郎哭,到底是詐尸還是另有隱情,我是刑警寧澤菇存,帶...
    沈念sama閱讀 35,789評(píng)論 5 346
  • 正文 年R本政府宣布夸研,位于F島的核電站,受9級(jí)特大地震影響依鸥,放射性物質(zhì)發(fā)生泄漏亥至。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,437評(píng)論 3 331
  • 文/蒙蒙 一贱迟、第九天 我趴在偏房一處隱蔽的房頂上張望姐扮。 院中可真熱鬧,春花似錦衣吠、人聲如沸茶敏。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,993評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽惊搏。三九已至,卻和暖如春忧换,著一層夾襖步出監(jiān)牢的瞬間恬惯,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,107評(píng)論 1 271
  • 我被黑心中介騙來泰國打工亚茬, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留酪耳,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,308評(píng)論 3 372
  • 正文 我出身青樓刹缝,卻偏偏與公主長得像碗暗,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子赞草,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,037評(píng)論 2 355

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