[拿走直接用] iOS加密:AES+Base64

JoyZhang發表於2017-12-14

本篇文章記錄了iOS中對字串進行AES加密+Base64編碼的過程,考慮到加密物件和使用場景,理所當然的將加密過程丟到了NSString的類別中,即下面說到的NSString+AES

一、對AES認識有以下幾點(針對開發中涉及到的,其他的也沒有深入研究了):使用上總結起來就是——“保持一致”

特別要注意的:如果你想使用金鑰偏移量IV 那你的加密模式必須為CBC,不能使用別的模式了,具體關於加密引數,文章最後附上構造方法的引數文件。

  • AES有多種加密模式:ECB CBC CFB OFB至於用哪個看你心情了,但是要同WebService同學保持一致;
  • 加密解密過程需要你提供一個Key,一定是和WebService同學約定好的,不然是解密不了的;
  • 偏移量(IV):非必須的,不過如果想加的話規則同上:“保持一致”;
  • 補碼方式:PKCS7Padding PKCS5Padding

對AES的認識可以通過類似 www.seacha.com/tools/aes.h… 的線上加密網站,從UI上簡單看看構成。

AES01.png

二、在學習使用的時候看到網上一般有兩種Base64編碼方式:

  • NSData_Base-64 Encoding:NSData類中自帶的編碼方法;
  • GTMBase64:我記得是Google的,具體的我也記不住了【這裡吐下槽:搜了一下GTMBase64這個關鍵詞,所有人都說“這個是啥就不說了,大家都說爛了!@#$%^&*諸如此類的話”,到頭來也沒找到一個正經的 666】

Demo中兩種編碼方式都給大家寫了,根據個人喜好選擇。GTMBase64庫可以用CocoaPods匯入。

下面是程式碼:

建立NSString的AES類別

#import <Foundation/Foundation.h>

@interface NSString (AES)

/**< 加密方法 */
- (NSString*)aci_encryptWithAES;

/**< 解密方法 */
- (NSString*)aci_decryptWithAES;

@end

複製程式碼

.m檔案 加密解密需要匯入這兩個標頭檔案(其實只匯入第二個就夠了)

#import <CommonCrypto/CommonDigest.h>
#import <CommonCrypto/CommonCryptor.h>
複製程式碼

使用GTMBase64的話需要匯入#import "GTMBase64.h"

定義加密的Key和向量IV

static NSString *const PSW_AES_KEY = @"TESTPASSWORD";
static NSString *const AES_IV_PARAMETER = @"AES00IVPARAMETER";

@implementation NSString (AES)

- (NSString*)aci_encryptWithAES {

    NSData *data = [self dataUsingEncoding:NSUTF8StringEncoding];
    NSData *AESData = [self AES128operation:kCCEncrypt
                                       data:data
                                        key:PSW_AES_KEY
                                         iv:AES_IV_PARAMETER];
    NSString *baseStr_GTM = [self encodeBase64Data:AESData];
    NSString *baseStr = [AESData base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength];
    
    NSLog(@"*****************\nGTMBase:%@\n*****************", baseStr_GTM);
    NSLog(@"*****************\niOSCode:%@\n*****************", baseStr);
    return baseStr_GTM;
}

- (NSString*)aci_decryptWithAES {

    NSData *data = [self dataUsingEncoding:NSUTF8StringEncoding];
    NSData *baseData_GTM = [self decodeBase64Data:data];
    NSData *baseData = [[NSData alloc]initWithBase64EncodedString:self options:0];
    
    NSData *AESData_GTM = [self AES128operation:kCCDecrypt
                                         data:baseData_GTM
                                          key:PSW_AES_KEY
                                           iv:AES_IV_PARAMETER];
    NSData *AESData = [self AES128operation:kCCDecrypt
                                         data:baseData
                                          key:PSW_AES_KEY
                                           iv:AES_IV_PARAMETER];
    
    NSString *decStr_GTM = [[NSString alloc] initWithData:AESData_GTM encoding:NSUTF8StringEncoding];
    NSString *decStr = [[NSString alloc] initWithData:AESData encoding:NSUTF8StringEncoding];
    
    NSLog(@"*****************\nGTMBase:%@\n*****************", decStr_GTM);
    NSLog(@"*****************\niOSCode:%@\n*****************", decStr);
    
    return decStr;
}
複製程式碼

AES加解密演算法

/**
 *  AES加解密演算法
 *
 *  @param operation kCCEncrypt(加密)kCCDecrypt(解密)
 *  @param data      待操作Data資料
 *  @param key       key
 *  @param iv        向量
 *
 *  @return
 */
- (NSData *)AES128operation:(CCOperation)operation data:(NSData *)data key:(NSString *)key iv:(NSString *)iv {

    char keyPtr[kCCKeySizeAES128 + 1];	//kCCKeySizeAES128是加密位數 可以替換成256位的
    bzero(keyPtr, sizeof(keyPtr));
    [key getCString:keyPtr maxLength:sizeof(keyPtr) encoding:NSUTF8StringEncoding];
    
    // IV
    char ivPtr[kCCBlockSizeAES128 + 1];
    bzero(ivPtr, sizeof(ivPtr));
    [iv getCString:ivPtr maxLength:sizeof(ivPtr) encoding:NSUTF8StringEncoding];
    
    size_t bufferSize = [data length] + kCCBlockSizeAES128;
    void *buffer = malloc(bufferSize);
    size_t numBytesEncrypted = 0;
    
    // 設定加密引數
    //(根據需求選擇什麼加密位數128or256,PKCS7Padding補碼方式之類的_(:з」∠)_,詳細的看下面吧)
    CCCryptorStatus cryptorStatus = CCCrypt(operation, kCCAlgorithmAES128, kCCOptionPKCS7Padding,
                                            keyPtr, kCCKeySizeAES128,
                                            ivPtr,
                                            [data bytes], [data length],
                                            buffer, bufferSize,
                                            &numBytesEncrypted);
    
    if(cryptorStatus == kCCSuccess) {
        NSLog(@"Success");
        return [NSData dataWithBytesNoCopy:buffer length:numBytesEncrypted];
        
    } else {
        NSLog(@"Error");
    }
    
    free(buffer);
    return nil;
}
複製程式碼

GTMBase64編碼

/**< GTMBase64編碼 */
- (NSString*)encodeBase64Data:(NSData *)data {
    data = [GTMBase64 encodeData:data];
    NSString *base64String = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
    return base64String;
}

/**< GTMBase64解碼 */
- (NSData*)decodeBase64Data:(NSData *)data {
    data = [GTMBase64 decodeData:data];
    return data;
}
複製程式碼

完成!你就可以去跑,去跳,去做一個漂亮的倒掛金鉤了~

Demo下載地址:https://github.com/iOSAppleBea/AES_1

附:CCCryptorStatus構造

關於加密引數CCCryptorStatus,構造他可以使用CCCrypt CCCryptorCreateWithMode CCCryptorCreate等好多種方法,構造時對引數的使用還是比較麻煩的,看文件對英語渣來說很辛苦,大概說一下看過CCCrypt後瞭解的幾點

蘋果文件
/*!
    @function   CCCrypt
    @abstract   Stateless, one-shot encrypt or decrypt operation.
                This basically performs a sequence of CCCrytorCreate(),
                CCCryptorUpdate(), CCCryptorFinal(), and CCCryptorRelease().
    
    @param      alg             Defines the encryption algorithm.
    
    
    @param      op              Defines the basic operation: kCCEncrypt or kCCDecrypt.
    
    @param      options         A word of flags defining options. See discussion
                                for the CCOptions type.
    
    @param      key             Raw key material, length keyLength bytes. 
                                加解密的金鑰
    
    @param      keyLength       Length of key material. Must be appropriate 
                                for the select algorithm. Some algorithms may 
                                provide for varying key lengths.
                                引數解釋詳見蘋果文件中的一個Key Size的列舉
                                enum {
    											kCCKeySizeAES128          = 16,
    											kCCKeySizeAES192          = 24,
    											kCCKeySizeAES256          = 32,
    											kCCKeySizeDES             = 8,
    											kCCKeySize3DES            = 24,
    											kCCKeySizeMinCAST         = 5,
    											kCCKeySizeMaxCAST         = 16,
    											kCCKeySizeMinRC4          = 1,
    											kCCKeySizeMaxRC4          = 512,
    											kCCKeySizeMinRC2          = 1,
    											kCCKeySizeMaxRC2          = 128,
    											kCCKeySizeMinBlowfish     = 8,
    											kCCKeySizeMaxBlowfish     = 56,
											};
    
    @param      iv              Initialization vector, optional. Used for 
                                Cipher Block Chaining (CBC) mode. If present, 
                                must be the same length as the selected 
                                algorithm's block size. If CBC mode is
                                selected (by the absence of any mode bits in 
                                the options flags) and no IV is present, a 
                                NULL (all zeroes) IV will be used. This is 
                                ignored if ECB mode is used or if a stream 
                                cipher algorithm is selected. 
                                向量:大概意思說,此屬性可選,但只能用於CBC模式。
                                如果出現那麼他的長度必須和演算法的block size保持一致。
                                如果是因為預設選擇的CBC模式而且向量沒有定義,那麼向量會被定義為NULL。
                                如果選擇了ECB模式或是其他的流密碼演算法,之前所說的邏輯都不成立。
    
    @param      dataIn          Data to encrypt or decrypt, length dataInLength 
                                bytes. 
    
    @param      dataInLength    Length of data to encrypt or decrypt.
    
    @param      dataOut         Result is written here. Allocated by caller. 
                                Encryption and decryption can be performed
                                "in-place", with the same buffer used for 
                                input and output. 
    
    @param      dataOutAvailable The size of the dataOut buffer in bytes.  
    
    @param      dataOutMoved    On successful return, the number of bytes
    				written to dataOut. If kCCBufferTooSmall is
				returned as a result of insufficient buffer
				space being provided, the required buffer space
				is returned here. 
        
    @result     kCCBufferTooSmall indicates insufficent space in the dataOut
                                buffer. In this case, the *dataOutMoved 
                                parameter will indicate the size of the buffer
                                needed to complete the operation. The 
                                operation can be retried with minimal runtime 
                                penalty. 
                kCCAlignmentError indicates that dataInLength was not properly 
                                aligned. This can only be returned for block 
                                ciphers, and then only when decrypting or when 
                                encrypting with block with padding disabled. 
                kCCDecodeError  Indicates improperly formatted ciphertext or
                                a "wrong key" error; occurs only during decrypt
                                operations. 
 */
    
CCCryptorStatus CCCrypt(
    CCOperation op,         /* kCCEncrypt, etc. */編碼 or 解碼
    CCAlgorithm alg,        /* kCCAlgorithmAES128, etc. */
    CCOptions options,      /* kCCOptionPKCS7Padding, etc. */
    const void *key,
    size_t keyLength,
    const void *iv,         /* optional initialization vector */
    const void *dataIn,     /* optional per op and alg */
    size_t dataInLength,
    void *dataOut,          /* data RETURNED here */
    size_t dataOutAvailable,
    size_t *dataOutMoved)
複製程式碼

Demo下載地址:github.com/iOSAppleBea…

相關文章