Rhythm & Biology

Engineering, Science, et al.

iPhoneでAES128暗号化

iPhoneアプリ開発で暗号化を行う必要が出てきたので、試しに書いてみました。
暗号方式にはAES128を、Padding方式にはPKCS7を使っています。

ここで使っているCommonCryptoはiOS SDKについてくるものですが、mac上でも普通に使うことができます(iPhone Simulator用のライブラリを無理矢理使います)。

使い方としては、下のコードをコンパイルして、

$ ./a.out aaaabbbbccccdddd eeeeffffgggghhhh message

というようにすると"message"が暗号化され、さらに続けて復号化されます。
第一引数は鍵、第二引数はInitial Vectorです。どちらも16バイトである必要があります。

/*                                                                                                  
 * gcc -std=c99 crypto.m -framework Foundation
 */
#import <Foundation/Foundation.h>
#import <CommonCrypto/CommonCryptor.h>

@interface NSData (AES)
- (NSData *)AES128Operation:(CCOperation)operation key:(NSString *)key iv:(NSString *)iv;
- (NSData *)AES128EncryptWithKey:(NSString *)key iv:(NSString *)iv;
- (NSData *)AES128DecryptWithKey:(NSString *)key iv:(NSString *)iv;
@end

@implementation NSData (AES)
- (NSData *)AES128Operation:(CCOperation)operation key:(NSString *)key iv:(NSString *)iv
{
  char keyPtr[kCCKeySizeAES128 + 1]; 
  memset(keyPtr, 0, sizeof(keyPtr));
  [key getCString:keyPtr maxLength:sizeof(keyPtr) encoding:NSUTF8StringEncoding];

  char ivPtr[kCCBlockSizeAES128 + 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 numBytesCrypted = 0;
  CCCryptorStatus cryptStatus = CCCrypt(operation,
                                        kCCAlgorithmAES128,
                                        kCCOptionPKCS7Padding,
                                        keyPtr,
                                        kCCBlockSizeAES128,
                                        ivPtr,
                                        [self bytes],
                                        dataLength,
                                        buffer,
                                        bufferSize,
                                        &numBytesCrypted);
  if (cryptStatus == kCCSuccess) {
    return [NSData dataWithBytesNoCopy:buffer length:numBytesCrypted];
  }
  free(buffer);
  return nil;
}

- (NSData *)AES128EncryptWithKey:(NSString *)key iv:(NSString *)iv
{
  return [self AES128Operation:kCCEncrypt key:key iv:iv];
}

- (NSData *)AES128DecryptWithKey:(NSString *)key iv:(NSString *)iv
{
  return [self AES128Operation:kCCDecrypt key:key iv:iv];
}
@end

int main(int argc, char const* argv[])
{
  NSAutoreleasePool* pool;
  pool = [[NSAutoreleasePool alloc] init];

  NSString *key = [NSString stringWithCString:argv[1] encoding:NSUTF8StringEncoding];
  NSString *iv = [NSString stringWithCString:argv[2] encoding:NSUTF8StringEncoding];
  NSString *data_str = [NSString stringWithCString:argv[3] encoding:NSUTF8StringEncoding];
  NSData *data = [data_str dataUsingEncoding:NSUTF8StringEncoding];

  NSData *en_data = [data AES128EncryptWithKey:key iv:iv];
  NSData *de_data = [en_data AES128DecryptWithKey:key iv:iv];

  NSString *de_str = [[[NSString alloc] initWithData:de_data
                                            encoding:NSUTF8StringEncoding] autorelease];

  NSLog(@"%@", en_data);
  NSLog(@"%@", de_str);

  [pool drain];
  return 0;
}

暗号の話は難しいですね。勉強が必要です。


補足(2011/10/18):
CCCryptはデフォルトでCBC(cipher block chaining)を使うようになっています。ECB(electronic codebook)を指定することもできますが、使う理由は特にないでしょう。