維基百科中對(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è)步驟:
AddRoundKey—矩陣中的每一個(gè)字節(jié)都與該次回合密鑰(round key)做XOR運(yùn)算腮出;每個(gè)子密鑰由密鑰生成方案產(chǎn)生帖鸦。
SubBytes—通過一個(gè)非線性的替換函數(shù),用查找表的方式把每個(gè)字節(jié)替換成對(duì)應(yīng)的字節(jié)胚嘲。
ShiftRows—將矩陣中的每個(gè)橫列進(jìn)行循環(huán)式移位作儿。
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一下巩踏。