{ 2010.12.5 }

iPhone で暗号化

    はてなブックマーク - iPhone で暗号化
    このエントリーをはてなブックマークに追加

    初めまして、Fusic 河野です。
    わたくし、IT 系のバスケの社会人チームiPhone アプリ系のコミュニティとかを
    やってたりするので、ご興味のある方はぜひご参加ください。

    弊社にはもう一人河野というものがおり、そっちとは違う方と
    覚えて頂けると溜飲が下がります。。。

    最近、プライベートではこんなことやってたりして遊んでます。
    Fusic では、主に ruby on rails をやっており、ZENPREiPhone アプリ
    担当させていただいてたりします。

    さて、昨日行われました安元の結婚式も無事に終わり、本日はとっても天気の良い日曜日です。
    それにしても、とても良い結婚式でした。

    「おめでとうございます。お二人とも末永くお幸せに!」

    で、そんな幸せに包まれつつな日曜日も Advent Calendar です。
    こんな日にバトンがまわってくるとは、、な気分ではございますがさっそく。

    ご結婚されたばかりのお二人には、なんとも、、な話かもしれませんが、
    いくら仲睦まじい関係だとしても、時には隠蔽する必要が出てくる事が
    あるやも知れません。
    そんな時に必要になってくること。

    “暗号化”についてです。

    前に山本が「as3cryptoで暗号化」 という記事を書いてますが、
    その記事の iPhone 版だと思っていただければ幸いです。

    暗号化

     objective-c で暗号化をする場合、CCCrypt() という関数を使います。

     [CCCrypt(3cc) Mac OS X Manual Page]
     developer.apple.com/library/mac/#DOCUMENTATION/Darwin/Reference/ManPages/man3/CCCrypt.3cc.html
     
     iPhone 用のサンプルコードはこちらになるのですが、見ていただくと分かるのですが、
     まー、as のように楽じゃないなというのが分かって頂けるかなと。

     CCCrypt() を使用して、NSData クラスに AES256EncryptWithKey, AES256DecryptWithKey のメソッドを追加します。
     (※ こちらを参考にしました)

     NSDataAdditions.m というファイルを作成し、以下を追加します。

    #import <CommonCrypto/CommonCryptor.h>
     
    @implementation NSData (Additions)
     
    @class NSString; 
     
    - (NSData *)AES256EncryptWithKey:(NSString *)key {
            char keyPtr[kCCKeySizeAES256+1];
            bzero(keyPtr, sizeof(keyPtr));
     
            [key getCString:keyPtr maxLength:sizeof(keyPtr) 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 | kCCOptionECBMode,
                                                                             keyPtr, kCCKeySizeAES256,
                                                                             NULL,
                                                                             [self bytes], dataLength,
                                                                             buffer, bufferSize,
                                                                             &numBytesEncrypted);
            if (cryptStatus == kCCSuccess) {
                    return [NSData dataWithBytesNoCopy:buffer length:numBytesEncrypted];
            }
            free(buffer);
            return nil;
    }
     
    - (NSData *)AES256DecryptWithKey:(NSString *)key {
            char keyPtr[kCCKeySizeAES256+1];
            bzero(keyPtr, sizeof(keyPtr));
     
            [key getCString:keyPtr maxLength:sizeof(keyPtr) 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 | kCCOptionECBMode,
                                                                             keyPtr, kCCKeySizeAES256,
                                                                             NULL,
                                                                             [self bytes], dataLength,
                                                                             buffer, bufferSize,
                                                                             &numBytesDecrypted);
     
            if (cryptStatus == kCCSuccess) {
                    return [NSData dataWithBytesNoCopy:buffer length:numBytesDecrypted];
            }
            free(buffer);
            return nil;
    }
    @end

     使う側でファイルをインポートすれば、NSData クラスのメソッドとして使えます。

    base64encoding

     暗号化したデータを ZENPRE とかのように、サーバにデータを送る必要があるなら、
     base64 エンコードも実装する必要があります。
     同じく NSData クラスに newStringInBase64FromData というメソッドを追加します。
     (※ こちらを参考にしました)

     先程のファイルに下記を追加します。

    - (NSString *)newStringInBase64FromData {
            NSMutableString *dest = [[NSMutableString alloc] initWithString:@""];
            unsigned char * working = (unsigned char *)[self bytes];
            int srcLen = [self length];
     
            for (int i=0; i<srcLen; i += 3) {
                    for (int nib=0; nib<4; nib++) {
                        int byt = (nib == 0)?0:nib-1;
                            int ix = (nib+1)*2;
     
                            if (i+byt >= srcLen) break;
     
                            unsigned char curr = ((working[i+byt] << (8-ix)) & 0x3F);
     
                            if (i+nib < srcLen) curr |= ((working[i+nib] >> ix) & 0x3F);
     
                            [dest appendFormat:@"%c", base64[curr]];
                    }
            }
            return dest;
    }

     同じく使う側でファイルをインポートすれば、NSData クラスのメソッドとして使えます。

    実行

     上記の追加したコードを含めたサンプルコードを実行します。

     

     入力した値が、base64 に変換されている事(真ん中のグレー領域)と
     暗号化されたものが復元されている事(下の白領域)を確認します。

    おわりに

     やはり、どうしても as と比べるとなんだかメンドクサイですね。
     暗号化の話は、コードも含め長文にどうしてもなってしまうので
     駆け足で進めていってしまいました。
     今回、objective-c の醍醐味の一つでもあるかなと個人的に思ってる、
     既存クラスへのメソッドの追加ができるってだけでも実感頂ければと思います。

     今回使用したサンプルのソースコードはココにあげております。
     お役に立てれば幸いです。

     それと、近々 ZENPREiPhone アプリの新版がリリースされる予定です。
     ご期待下さい。

    comments

    1. [...] blog.fusic.co.jp/archives/859 カテゴリー: 未分類   パーマリンク ← Hello world! [...]

    2. ともと より:

      鍵長にkCCBlockSizeAES128を指定してるのおかしくね?

    3. t-kawano より:

      >ともと さん
      はい。kCCKeySizeAES256 が残っていましたね。
      正しくは、kCCBlockSizeAES128 です。
      ご指摘ありがとうございました。
      修正しておきます。

    4. ともと より:

      え〜〜そしたらAES256EncryptWithKeyってメソッド名おかしくね?256って鍵長だよねッAESはブロック長128bit固定だし。
      鍵長指定するところにブロック長の定数を指定するのもきもいっすね。
      keyPtr, kCCBlockSizeAES128, って行がkeyPtr, kCCKeySizeAES256, ってなんのが正しいんじゃないかなって意図でした。

    5. t-kawano より:

      >ともと さん
      ご指摘ありがとうございます。
      確かにですね。早とちりしておりました、すみません。
      修正いたしました。
      kCCKeySizeAES256 を指定するのが正しいです。
      ありがとうございました。

    6. [...] Objective-CでのAESを使用した暗号化については下記サイトが参考になります。 iPhone で暗号化 [...]

    7. k より:

      とても参考になりました。ありがとうございます。
      AES256EncryptWithKey, AES256DecryptWithKeyはほぼ同じ事をしているので
      まとめた方が見やすいと思います。

      〜前省略〜
      typedef enum {
      AESTypeEncrypt, // 暗号化
      AESTypeDecrypt, // 復号化
      } kAESType;

      @implementation NSData (AESEncrypt)

      // 暗号化
      - (NSData *)AES256EncryptWithKey:(NSString *)key {
      return [self AES256WithKey:key AESType:AESTypeEncrypt];
      }

      // 復号化
      - (NSData *)AES256DecryptWithKey:(NSString *)key {
      return [self AES256WithKey:key AESType:AESTypeDecrypt];
      }

      - (NSData *)AES256WithKey:(NSString *)key AESType:(kAESType)aesType
      {
      char keyPtr[kCCKeySizeAES256+1];
      bzero(keyPtr, sizeof(keyPtr));
      [key getCString:keyPtr maxLength:sizeof(keyPtr) encoding:NSUTF8StringEncoding];

      NSUInteger dataLength = [self length];

      size_t bufferSize = dataLength + kCCBlockSizeAES128;
      void *buffer = malloc(bufferSize);

      CCOperation type;
      if (aesType == AESTypeEncrypt) {
      type = kCCEncrypt;
      } else {
      type = kCCDecrypt;
      }

      size_t numBytes = 0;
      CCCryptorStatus cryptStatus = CCCrypt(type, kCCAlgorithmAES128,
      kCCOptionPKCS7Padding | kCCOptionECBMode,
      keyPtr, kCCBlockSizeAES128,
      NULL,
      [self bytes], dataLength,
      buffer, bufferSize,
      &numBytes);

      if (cryptStatus == kCCSuccess) {
      return [NSData dataWithBytesNoCopy:buffer length:numBytes];
      }

      free(buffer);
      return nil;
      }