AES:(Advanced Encryption Standard)高級加密標(biāo)準(zhǔn)软啼。是一個(gè)對稱分組秘鑰算法祸挪,旨在取代 DES 成為廣泛使用的標(biāo)準(zhǔn)贞间。
秘鑰長度有三種增热,分別是 AES-128、AES-192 和 AES-256公黑。
加密模式有四種摄咆,分別是 ECB(Elecyronic Code Book,電子密碼本)、CBC(Cipher Block Chaining,加密塊鏈)吭从、CFB(Cipher FeedBack Mode,加密反饋)朝蜘、OFB(Output FeedBack,輸出反饋)。
填充模式:
由于塊加密只能對特定長度的數(shù)據(jù)塊進(jìn)行加密涩金,因此CBC谱醇、ECB模式需要在最后一數(shù)據(jù)塊加密前進(jìn)行數(shù)據(jù)填充。(CFB步做,OFB和CTR模式由于與key進(jìn)行加密操作的是上一塊加密后的密文副渴,因此不需要對最后一段明文進(jìn)行填充)
在iOS SDK中提供了PKCS7Padding,而JDK則提供了PKCS5Padding全度。原則上PKCS5Padding限制了填充的Block Size為8 bytes煮剧,而Java實(shí)際上當(dāng)塊大于該值時(shí)讼载,其PKCS5Padding與PKCS7Padding是相等的轿秧。
初始向量(偏移量)
使用除ECB以外的其他加密模式均需要傳入一個(gè)初始向量,其大小與Block Size相等(AES的Block Size為128 bits(16字節(jié)))咨堤,而兩個(gè)平臺的API文檔均指明當(dāng)不傳入初始向量時(shí)菇篡,系統(tǒng)將默認(rèn)使用一個(gè)全0的初始向量。
引入頭文件
import <CommonCrypto/CommonDigest.h>
import <CommonCrypto/CommonCryptor.h>
+ (NSString *)SB_AES_Encrypt:(NSString *)originalStr
mode:(SBMode)mode
key:(NSString *)key
keySize:(SBKeySizeAES)keySize
iv:(NSString * _Nullable )iv
padding:(SBCryptorPadding)padding;
{
NSData *data = [originalStr dataUsingEncoding:NSUTF8StringEncoding];
data = [self SB_AES_WithData:data operation:kCCEncrypt mode:mode key:key keySize:keySize iv:iv padding:padding];
return [data base64EncodedStringWithOptions:0];
}
+ (NSString *)SB_AES_Decrypt:(NSString *)originalStr
mode:(SBMode)mode
key:(NSString *)key
keySize:(SBKeySizeAES)keySize
iv:(NSString * _Nullable )iv
padding:(SBCryptorPadding)padding
{
NSData *data = [[originalStr dataUsingEncoding:NSUTF8StringEncoding] base64EncodedDataWithOptions:0];
data = [self SB_AES_WithData:data operation:kCCDecrypt mode:mode key:key keySize:keySize iv:iv padding:padding];
return [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
}
+ (NSData *)SB_AES_WithData:(NSData *)originalData
operation:(CCOperation)operation
mode:(CCMode)mode
key:(NSString *)key
keySize:(SBKeySizeAES)keySize
iv:(NSString *)iv
padding:(SBCryptorPadding)padding
{
NSAssert((mode != kCCModeECB && iv != nil && iv != NULL) || mode == kCCModeECB, @"使用 CBC 模式一喘,initializationVector(即iv驱还,填充值)必須有值");
CCCryptorRef cryptor = NULL;
CCCryptorStatus status = kCCSuccess;
NSMutableData * keyData = [[key dataUsingEncoding: NSUTF8StringEncoding] mutableCopy];
NSMutableData * ivData = [[iv dataUsingEncoding: NSUTF8StringEncoding] mutableCopy];
#if !__has_feature(objc_arc)
[keyData autorelease];
[ivData autorelease];
#endif
[keyData setLength:keySize];
[ivData setLength:keySize];
//填充模式(系統(tǒng)API只提供了兩種)
CCPadding paddingMode = (padding == ccPKCS7Padding) ? ccPKCS7Padding : ccNoPadding ;
NSData *sourceData = originalData;
if (operation == kCCEncrypt) {
sourceData = [self bitPaddingWithData:originalData mode:mode padding:padding]; //FIXME: 實(shí)際上的填充模式
}
status = CCCryptorCreateWithMode(operation, mode, kCCAlgorithmAES, paddingMode, ivData.bytes, keyData.bytes, keyData.length, NULL, 0, 0, 0, &cryptor);
if ( status != kCCSuccess ){
NSLog(@"Encrypt Error:%d",status);
return nil;
}
//確定處理給定輸入所需的輸出緩沖區(qū)大小尺寸。
size_t bufsize = CCCryptorGetOutputLength( cryptor, (size_t)[sourceData length], true );
void * buf = malloc( bufsize );
size_t bufused = 0;
size_t bytesTotal = 0;
//處理(加密凸克,解密)一些數(shù)據(jù)议蟆。如果有結(jié)果的話,寫入提供的緩沖區(qū).
status = CCCryptorUpdate( cryptor, [sourceData bytes], (size_t)[sourceData length],
buf, bufsize, &bufused );
if ( status != kCCSuccess ){
NSLog(@"Encrypt Error:%d",status);
free( buf );
return nil;
}
bytesTotal += bufused;
if (padding == SBCryptorPKCS7Padding) {
status = CCCryptorFinal( cryptor, buf + bufused, bufsize - bufused, &bufused );
if ( status != kCCSuccess ){
NSLog(@"Encrypt Error:%d",status);
free( buf );
return nil;
}
bytesTotal += bufused;
}
NSData *result = [NSData dataWithBytesNoCopy:buf length: bytesTotal];
if (operation == kCCDecrypt) {
//解密時(shí)移除填充
result = [self removeBitPaddingWithData:result mode:mode operation:operation andPadding:padding];
}
CCCryptorRelease(cryptor);
return result;
}
// 填充需要加密的字節(jié)
+ (NSData *)bitPaddingWithData:(NSData *)data
mode:(CCMode)mode
padding:(SBCryptorPadding)padding;
{
NSMutableData *sourceData = data.mutableCopy;
int blockSize = kCCBlockSizeAES128; //FIXME: AES的塊大小都是128bit,即16bytes
switch (padding) {
case SBCryptorPKCS7Padding:
{
if (mode == kCCModeCFB || mode == kCCModeOFB) {
//MARK: CCCryptorCreateWithMode方法在這兩個(gè)模式下萎战,并不會給塊自動(dòng)填充咐容,所以需要手動(dòng)去填充
NSUInteger shouldLength = blockSize * ((sourceData.length / blockSize) + 1);
NSUInteger diffLength = shouldLength - sourceData.length;
uint8_t *bytes = malloc(sizeof(*bytes) * diffLength);
for (NSUInteger i = 0; i < diffLength; i++) {
// 補(bǔ)全缺失的部分
bytes[i] = diffLength;
}
[sourceData appendBytes:bytes length:diffLength];
}
}
break;
case SBCryptorZeroPadding:
{
int pad = 0x00;
int diff = blockSize - (sourceData.length % blockSize);
for (int i = 0; i < diff; i++) {
[sourceData appendBytes:&pad length:1];
}
}
break;
case SBCryptorANSIX923:
{
int pad = 0x00;
int diff = blockSize - (sourceData.length % blockSize);
for (int i = 0; i < diff - 1; i++) {
[sourceData appendBytes:&pad length:1];
}
[sourceData appendBytes:&diff length:1];
}
break;
case SBCryptorISO10126:
{
int diff = blockSize - (sourceData.length % blockSize);
for (int i = 0; i < diff - 1; i++) {
int pad = arc4random() % 254 + 1; //FIXME: 因?yàn)槭请S機(jī)填充,所以相同參數(shù)下蚂维,每次加密都是不一樣的結(jié)果(除了分段后最后一個(gè)分段的長度為15bytes的時(shí)候加密結(jié)果相同)
[sourceData appendBytes:&pad length:1];
}
[sourceData appendBytes:&diff length:1];
}
break;
default:
break;
}
return sourceData;
}
+ (NSData *)removeBitPaddingWithData:(NSData *)sourceData mode:(CCMode)mode operation:(CCOperation)operation andPadding:(SBCryptorPadding)padding
{
int correctLength = 0;
int blockSize = kCCBlockSizeAES128;
Byte *testByte = (Byte *)[sourceData bytes];
char end = testByte[sourceData.length - 1];
if (padding == SBCryptorPKCS7Padding) {
if ((mode == kCCModeCFB || mode == kCCModeOFB) && (end > 0 && end < blockSize + 1)) {
correctLength = (short)sourceData.length - end;
}else{
return sourceData;
}
}else if (padding == SBCryptorZeroPadding && end == 0) {
for (int i = (short)sourceData.length - 1; i > 0 ; i--) {
if (testByte[i] != end) {
correctLength = i + 1;
break;
}
}
}else if ((padding == SBCryptorANSIX923 || padding == SBCryptorISO10126) && (end > 0 && end < blockSize + 1)){
correctLength = (short)sourceData.length - end;
// if (padding == MIUCryptorISO10126 || ( testByte[sourceData.length - end] == 0 && testByte[sourceData.length - 2] == 0)) {
// correctLength = (short)sourceData.length - end;
// }
}
NSData *data = [NSData dataWithBytes:testByte length:correctLength];
return data;
}
Demo代碼請參考gitHub:https://github.com/Manchitor/SBTools