RavyPop

=͟͟͞͞(๑=͟͟͞͞(๑•̀=͟͟͞͞(๑•̀д•́=͟͟͞͞(๑•̀д•́๑)=͟͟͞͞

objective-CでAES暗号化を行う

CommonCryptoを使う

以前XcodeでopenSSLを使うとめっちゃ警告でてなんなの?って記事を書いたりしたんですけど、

 

openssl is deprecated in Mac OS X 10.7 - tokihiro_k's blog

 

apple的にはCommonCrypto使いなさいよーって言ってるみたいなのでこの休日でちょっと実装してみました。実装したのはAESを使って暗号化を行うプログラムです。思ったより簡単でした。

 

ステートレスにできるメソッドもあるみたいですけど(ググると出てくるCommonCrypto使ってる実装はだいたいこっち)今回は面倒だけど動きがよくわかる方法で実装しました。以下参考にしたサイト(ただのヘッダファイルだけど)と使用する暗号化用メソッド。

 

CommonCryptor.h



"CCCryptorCreate"は最初に暗号化や復号の設定を行う

CCCryptorCreate(CCOperation op, CCAlgorithm alg, CCOptions options, const void *key, size_t keyLength, const void *iv, CCCryptorRef *cryptorRef);
  1. op:暗号化か復号化を指定....kCCEncrypt, kCCDecrypt
  2. alg:使用するアルゴリズム...上記サイトに使えるのが書いてある
  3. options:モードを指定...デフォルトがCBCらしい
  4. key:鍵データ
  5. key Length:鍵長
  6. iv:初期化ベクタデータ
  7. cryptorRef:初期化されたオブジェクトが返ってくる

 

 

"CCCryptorUpdate"から暗号化が開始される。入力データが基底バイトより多い場合何度も呼び出され平文が基底バイト以下になるまで繰り返される

CCCryptorUpdate(CCCryptorRef cryptorRef, const void *dataIn, size_t dataInLength, void *dataOut, size_t dataOutAvailable, size_t *dataOutMoved);
  1. cryptorRef:上のメソッドと同じもの
  2. dataIn:暗号化するデータ...NSData型なら"[data bytes]"とかする
  3. dataInLength:暗号化するデータの大きさ
  4. dataOut:暗号化されたデータが入る...自分でメモリを確保して割り当てる
  5. dataOutAvailable:4での返り値で利用できるメモリの大きさ
  6. dataOutMoved:成功した場合何バイト書き込まれたかが返る

 

 

"CCCryptorFinal"で最後の16byte(aes128の場合)が暗号化される

CCCryptorFinal(CCCryptorRef cryptorRef, void *dataOut, size_t dataOutAvailable, size_t *dataOutMoved);
  1. cryptorRef:上と同じ
  2. dataOut:暗号化されたデータが入る...CCCryptorUpdateが呼ばれた場合を考慮し、"すでに書き込まれたバイト数"分の大きさを加える必要がある
  3. dataOutAvailable:同じ
  4. dataOutMoved:同じ

 

この3つのメソッドを順番通りに呼べば暗号化されたデータが出来上がります。上記した"dataOut"の型を見るとわかりますけど、"void *"で指定されています。つまり汎用ポインタです。NSData型で返ってきた方が使う方としてはいいと思うのでそのように実装しました。

 

"cc_crypto.h"

#import <Foundation/Foundation.h>
#import <CommonCrypto/CommonCrypto.h>

@interface cc_crypto:NSObject
{
  CCOperation op;
  NSUInteger CLen;
  unsigned char crypto_key[16];
  unsigned char crypto_iv[16];
}

- (id)initWithOperation:(CCOperation)op key:(unsigned char *)key iv:(unsigned char *)iv;
- (NSUInteger)CLen;
- (NSData *)CCCrypto:(NSData *)data ;

@end
 

"cc_crypto.m"

#import "cc_crypto.h"

@implementation cc_crypto
- (id)initWithOperation:(CCOperation)operation key:(unsigned char *)key iv:(unsigned char *)iv{
  self = [super init];
  if(self != nil){
    op = operation;
    memcpy(crypto_key, key, 16);
    memcpy(crypto_iv, iv, 16);
  }
  return self;
}

- (NSUInteger)CLen {
  return CLen;
}

- (NSData *)CCCrypto:(NSData *)data {
  CCCryptorStatus err;
  CCCryptorRef ref;
  void *response;
  size_t bufferSize;
  size_t updateResultLength = 0;
  size_t finalResultLength = 0;

  NSUInteger length = [data length];

  bufferSize = length + kCCBlockSizeAES128;

  err = CCCryptorCreate(op, kCCAlgorithmAES128, kCCOptionPKCS7Padding, crypto_key, kCCKeySizeAES128, crypto_iv, &ref);
  NSLog(@"CCCryptorCreate = %d", err);

  response = malloc(bufferSize*sizeof(size_t));
  err = CCCryptorUpdate(ref, [data bytes], [data length], response, bufferSize, &updateResultLength);
  NSLog(@"CCCryptorUpdate = %d, resultLength = %zu", err, updateResultLength);

  err = CCCryptorFinal(ref, response + updateResultLength, bufferSize, &finalResultLength);
  NSLog(@"CCCryptorFinal = %d, resultLength = %zu", err, finalResultLength);

  if( err == 0 ) {
    printf("success\n");
    CLen = (updateResultLength + finalResultLength);
    NSData* data = [NSData dataWithBytes:(const void *)response length:CLen];
    CCCryptorRelease(ref);
    return data;
  } else {
    CCCryptorRelease(ref);
    return nil; 
  }

}

@end

鍵とivの型はC言語から流用したので"unsigned char"になってしまいました。。。。。まぁ汎用ポインタですからなんでもいいんでしょうけど、とりあえずここは各々使いやすいように変更しましょう。

 

こいつの使い方はまず"initWithOperation"を読んで暗号化か復号かと鍵、ivを決定してオブジェクトを作ります。その後CCCryptoに暗号化したいデータを渡せば処理されてNSData型で値が返ってきます。あとはそのデータを好きなようにすればいいと思います。

 

テスト用に文字列を暗号化して復号するmainファイルを作りました。

"main.m"

#import "cc_crypto.h"
#import <Foundation/Foundation.h>
#import <CommonCrypto/CommonCrypto.h>

int main(void){
  unsigned char key[16] = {0x01, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff};
  unsigned char iv[16] = {0x01, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff};
  NSString* test = @"tesaaaaaaaaaaaataaaaaaaaaaaaaaaabbbbagafgarfajaowefjawpoefjao";
  NSData* test_data = [test dataUsingEncoding:NSUTF8StringEncoding];
  id cc, cc2;
  void *res, *res2;

  cc = [[cc_crypto alloc] initWithOperation:kCCEncrypt key:key iv:iv];
  NSData* data = [cc CCCrypto:test_data];
  NSLog(@"len = %lu", [cc CLen]);

  cc2 = [[cc_crypto alloc] initWithOperation:kCCDecrypt key:key iv:iv];
  NSData* data2 = [cc2 CCCrypto:data];

  NSString *str= [[NSString alloc] initWithData:data2 encoding:NSUTF8StringEncoding];
  NSLog(@"%@", str);
}

以下実行結果

bash-3.2$ cc main.m cc_crypto.m -framework Foundation
bash-3.2$ ./a.out 
2013-11-17 18:14:33.958 a.out[28566:507] CCCryptorCreate = 0
2013-11-17 18:14:33.960 a.out[28566:507] CCCryptorUpdate = 0, resultLength = 48
2013-11-17 18:14:33.960 a.out[28566:507] CCCryptorFinal = 0, resultLength = 16
success
2013-11-17 18:14:33.961 a.out[28566:507] len = 64
2013-11-17 18:14:33.961 a.out[28566:507] CCCryptorCreate = 0
2013-11-17 18:14:33.961 a.out[28566:507] CCCryptorUpdate = 0, resultLength = 48
2013-11-17 18:14:33.962 a.out[28566:507] CCCryptorFinal = 0, resultLength = 13
success
2013-11-17 18:14:33.962 a.out[28566:507] tesaaaaaaaaaaaataaaaaaaaaaaaaaaabbbbagafgarfajaowefjawpoefjao

 

そして、当たり前ですけどこのメソッドを使えばファイルの暗号化もできます。次にjpgファイルを暗号化して、すぐ復号するテストを行います。

"main2.m"

#import "cc_crypto.h"
#import <Foundation/Foundation.h>
#import <CommonCrypto/CommonCrypto.h>

/*
 * 参考
 * http://www.objectivec-iphone.com/foundation/NSFileManager/writeData.html
 *
 */

int main(void) {
  unsigned char key[16] = {0x01, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff};
  unsigned char iv[16] = {0x01, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff};
  id cc, cc2;
  NSString *filePath = @"/Users/tokihiro/image/yuuyake03.jpg";
  NSString *decfilePath = @"/Users/tokihiro/image/hoge.jpg";
  NSFileHandle *fileHandle = [NSFileHandle fileHandleForReadingAtPath:filePath];

  if (!fileHandle) {
    NSLog(@"ファイルがありません.");
    exit(EXIT_FAILURE);
  }
  // ファイルの末尾まで読み込み、暗号化を行う
  NSData *data = [fileHandle readDataToEndOfFile];
  cc = [[cc_crypto alloc] initWithOperation:kCCEncrypt key:key iv:iv];
  NSData* EncData = [cc CCCrypto:data];

  //暗号化されたデータを復号する
  cc2 = [[cc_crypto alloc] initWithOperation:kCCDecrypt key:key iv:iv];
  NSData* DecData = [cc2 CCCrypto:EncData ];

  /** 復号されたデータをファイルとして書き込む **/
  NSFileManager *fileManager = [NSFileManager defaultManager];
  // ファイルが存在しないか?
  if (![fileManager fileExistsAtPath:decfilePath]) { // yes
    // 空のファイルを作成する
    BOOL result = [fileManager createFileAtPath:decfilePath contents:[NSData data] attributes:nil];
    if (!result) {
        NSLog(@"ファイルの作成に失敗");
        exit(EXIT_FAILURE);
    }
}
  // ファイルハンドルを作成する
  NSFileHandle *writefileHandle = [NSFileHandle fileHandleForWritingAtPath:decfilePath];
  // ファイルハンドルの作成に失敗したか?
  if (!writefileHandle) { // no
    NSLog(@"ファイルハンドルの作成に失敗");
    exit(EXIT_FAILURE);
  }
  // ファイルに書き込む
  [writefileHandle writeData:data];
}

一応GitHubにも同じソースコードがあがっています。こっちは適宜更新する可能性があります。

 

tokihiro000/CCCrypto · GitHub

 

そういうわけでobjective-CでCommonCryptoを使った暗号化でした。