iOS, Android, perl間で AES暗号化を行った通信をする

By | 2012年3月2日

Andorid, iOS, サーバー(perl)間で暗号化して通信する必要があったのでまとめてみました。

処理の流れはこんな感じ

クライアント(iOS, Android) からの通信
         [Request]                            [Response]
        plain text                       plain text
           ↓                                   ↑
        cipher text                        cipher text
           ↓                                   ↑
        base64 text                      base64 text
           ↓                                   ↑
          WAN(POST)                       WAN
           ↓                                   ↑
        base64 text                      base64 text
           ↓                                   ↑
        cipher text                        cipher text
           ↓                                   ↑
        plain text                      plain text

鍵は事前に交換してるものとして話をすすめます。

結論からいうと iv さえあわせとけば特にハマリませんでした。
そういいつつここで大分はまったんですけどねw

とういわけで iv をそろえないといけないので今回は iOS で NULL(0x00 x 16 Byte) にあわせました。

それは「CCCrypt(Objective-C) で暗号化したデータを Crypt::OpenSSL::AES(perl) で復号化する」を参考にしただけで特に理由はありません。

本来なら C で Android, iOS 用のライブラリをつくっちゃったらよかったのだろうけどもと思いつつそこまでやってません。

あと GET で暗号化したものを通信する際は Base64 を URLSafe にして encode / decode してください。

perl

#!/usr/bin/env perl
 
use strict;
use warnings;
 
use Digest::MD5 (); 
use MIME::Base64 (); 
use Crypt::CBC (); 
 
sub main {
    my $key = 'e8ffc7e56311679f12b6fc91aa77a5eb';           #Digest::MD5::md5_hex(time); とか。
    my $cipher = Crypt::CBC->new(
        -key         => $key,
        -literal_key => 1,
        -cipher      => 'Crypt::OpenSSL::AES',
        -header      => 'none',
        -iv          => pack('C*', map {0x00} 1..16),
    );  
 
    ############## Request(decrypt) ##############
    my $base64_text = "BhSJd4mRRJo+fGzpxIOUNg==";
    my $cipher_text = MIME::Base64::decode_base64($base64_text);
    my $plain_text  = $cipher->decrypt($cipher_text);
    print "plain text for request : $plain_text\n";         # plain text for request : crypt text!!
 
    ############## Response(crypt) ##############
    $plain_text  = "I'm hungry!";
    $cipher_text = $cipher->encrypt($plain_text);
    $base64_text = MIME::Base64::encode_base64($cipher_text);
    print "base64 text for response : $base64_text\n";      # base64 text for response : 72XrlydqnUzVrDfDE7ncnQ==
 
    return 0;            
}                        
 
exit main() unless caller;

iOS

AES crypt category」と「Base64 category」をもってきて
import すれば下記の方法でいけます。

NSString *key = @"e8ffc7e56311679f12b6fc91aa77a5eb";
 
NSData *cipherData;
NSString *base64Text, *plainText;
 
//############## Request(crypt) ##############
plainText  = @"crypt text!!";
cipherData = [[plainText dataUsingEncoding:NSUTF8StringEncoding] AES256EncryptWithKey:key];
base64Text = [cipherData base64Encoding];
NSLog(@"%@", base64Text);            //BhSJd4mRRJo+fGzpxIOUNg==
 
//############## Response(decrypt) ##############
base64Text = @"72XrlydqnUzVrDfDE7ncnQ==";
cipherData = [base64Text base64DecodedData];
plainText  = [[NSString alloc] initWithData:[cipherData AES256DecryptWithKey:key] encoding:NSUTF8StringEncoding];
NSLog(@"%@", plainText);            //I'm hungry

Android

AndroidでAESの暗号化する」を参考にしてつくりました。
違うのは鍵を外部からうけとれるようにしたのと、 iv を 0x00 の 16 Byte にそろえたくらいです。
Base64 で encode / decode しないといけないので Android SDK 2.2 以上だと動きます。
2.1 以下で使用する際は別途 Base64 のライブラリを用意してください。

エラーのハンドリングとかちゃんとしていないので参考程度にしてください。

AES256 encryption on Android

String key = "e8ffc7e56311679f12b6fc91aa77a5eb";
byte[] keyBytes = key.getBytes("UTF-8");
byte[] ivBytes = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
 
String plainText;
byte[] cipherData;
String base64Text;
 
//############## Request(crypt) ##############
plainText  = "crypt text!!";
cipherData = AES256Cipher.encrypt(ivBytes, keyBytes, plainText.getBytes("UTF-8"));
base64Text = Base64.encodeToString(cipherData, Base64.DEFAULT);
Log.d("encrypt", base64Text);   //BhSJd4mRRJo+fGzpxIOUNg==
 
//############## Response(decrypt) ##############
base64Text = "72XrlydqnUzVrDfDE7ncnQ==";
cipherData = AES256Cipher.decrypt(ivBytes, keyBytes, Base64.decode(base64Text.getBytes("UTF-8"), Base64.DEFAULT));
plainText = new String(cipherData, "UTF-8");
Log.d("dcrypt", plainText);         //I'm hungry

いじょう。

XML::Simple で RSS の entry を順番通りに取り出す方法

By | 2009年9月6日

たとえばこんな感じのフィードがあったとします。

<?xml version="1.0" encoding="utf-8"?>
<feed>
    <entry>
        <key>blog1</key>
        <title>blog1-title</title>
        <content>blog1-content</content>
    </entry>
    <entry>
        <key>blog2</key>
        <title>blog2-title</title>
        <content>blog2-content</content>
    </entry>
    <entry>
        <key>blog3</key>
        <title>blog3-title</title>
        <content>blog3-content</content>
    </entry>
    <entry>
        <key>blog4</key>
        <title>blog4-title</title>
        <content>blog4-content</content>
    </entry>
    <entry>
        <key>blog5</key>
        <title>blog5-title</title>
        <content>blog5-content</content>
    </entry>
</feed>

単純に parse するとハッシュに格納されるので
entry の並び順はめちゃくちゃになります。

dump結果

$VAR1 = {
          'entry' => {
                     'blog2' => {
                                'content' => 'blog2-content',
                                'title' => 'blog2-title'
                              },
                     'blog5' => {
                                'content' => 'blog5-content',
                                'title' => 'blog5-title'
                              },
                     'blog4' => {
                                'content' => 'blog4-content',
                                'title' => 'blog4-title'
                              },
                     'blog1' => {
                                'content' => 'blog1-content',
                                'title' => 'blog1-title'
                              },
                     'blog3' => {
                                'content' => 'blog3-content',
                                'title' => 'blog3-title'
                              }
                   }
        };

ランキングの RSS とかだと entry の順番が変わるだけで致命的です。
今回がまさにその状態。
blog1〜5の順番で entry に入っていてほしかったのです。

できないかと思って調べてたらいけました。
how to preserve XML::Simple element order
どうやら Tie::IxHash を使えばいけそうとのこと。
Tie::IxHash についてはハッシュのキーを挿入(追加)した順番通りに取り出すがとても
まとまっておりわかりやすいです。

#!/usr/bin/perl
 
use strict;
use warnings;
 
use Tie::IxHash;
use XML::Simple;
use Data::Dumper; 
 
*XML::Simple::new_hashref = 'new_hashref';
sub new_hashref {
    my $self = shift;
    tie my %hash, 'Tie::IxHash';
    %hash = @_; 
    return \%hash;
}
 
my $parser = XML::Simple->new();
my $xml = $parser->XMLin('sample.xml');
warn Dumper $xml;
 
1;
 
__END__
結果
$VAR1 = {
          'entry' => {
                     'blog1' => {
                                'content' => 'blog1-content',
                                'title' => 'blog1-title'
                              },
                     'blog2' => {
                                'content' => 'blog2-content',
                                'title' => 'blog2-title'
                              },
                     'blog3' => {
                                'content' => 'blog3-content',
                                'title' => 'blog3-title'
                              },
                     'blog4' => {
                                'content' => 'blog4-content',
                                'title' => 'blog4-title'
                              },
                     'blog5' => {
                                'content' => 'blog5-content',
                                'title' => 'blog5-title'
                              }
                   }
        };

このように new_hashref を Tie::IxHash で tie してやった
ハッシュリファレンスを返すように override してやれば大丈夫です。
XML::LibXML でも同じようなことはできるらしいです。

モジュールに定義されている関数名一覧を取得

By | 2009年6月7日

よく忘れるのでメモ。
シンボルテーブルの一覧を取得するには以下の方法で取得できます。
ポイントはモジュール名の後に :: をつけること。
これよく忘れます。

%モジュール::;

で、これだと変数なのか関数なのかが判断できません。
そこで便利なのが Devel::Symdump です。

 @array = Devel::Symdump->functions(@packs);

これだけで関数一覧が取得できます。
関数だけでなく hash や scalar が取得できたり
diff とれたり HTML で出力できたり地味に便利です。

ports で入れた perl で Mac::Growl が使えるようになるまで

By | 2009年5月25日

Test::Continuous に興味がわいたので
さわってみようとしたらMac::Growl が必要とのこと。
いれようとしたら Foundation.pm 等がないためちゃんと test に通らなかった。

どうやら ports でいれたのには Foundation の ラッパーモジュール がないらしい。
variants で調べて +darwin を足していれなおしたけどなかった。
というわけでいいのかわからないけど、デフォルトに入ってるのにシンボリックリンクを貼って
その場をしのいだ。

以下、説明。
mac 独自に bundle されたモジュール類(Foundation.pmとか)は
/System/Library/Perl/Extras/5.8.8/darwin-thread-multi-2level 以下にあります。
Foundation.pm が PerlObjCBridge.pm と CoreFoundation.pm にも依存していたので忘れなく貼ること。

自分の環境だと以下のパスに貼った。

sudo ln -s /System/Library/Perl/Extras/5.8.8/darwin-thread-multi-2level/Foundation.pm /opt/local/lib/perl5/5.10.0/darwin-2level/Foundation.pm
sudo ln -s /System/Library/Perl/Extras/5.8.8/darwin-thread-multi-2level/CoreFoundation.pm /opt/local/lib/perl5/5.10.0/darwin-2level/CoreFoundation.pm
sudo ln -s /System/Library/Perl/Extras/5.8.8/darwin-thread-multi-2level/PerlObjCBridge.pm  /opt/local/lib/perl5/5.10.0/darwin-2level/PerlObjCBridge.pm

perl のバージョンが 5.10 だけど 5.8.8 からのシンボリックリンクでも現状、一応動いてる。

ただ、Mac::Growl が utf8 で文字を渡しても文字化けする始末。
というわけで Net::Growl や GNTP::Growl も試してみたが
残園なことにNet::Growl は何もおこらない。
GNTP::Grwol は IO::Socket::INET でコネクションできない感じ。

IO::Socket::INET: connect: Connection refused at /opt/local/lib/perl5/site_perl/5.10.0/GNTP/Growl.pm line 44,  line 1.

うむぅ〜困った。

追記 2009/08/29 20:17:22

snow leopard にしたら Mac::Growl が使えなくなった。
どうやら Carbon 系が 32bit でしか使えないみたい。
64bit で起動させると使えない。
ソース

Feed をパースするモジュールのベンチをとったまとめ

By | 2009年4月21日

Feed(RSS, Atom) 共にモジュールがいっぱいあって何を使えばいいのかいまいちわからない。
というわけで調べるついでにベンチマークをとった。
とったといっても RSSのパース速度の比較
Atomのパース速度の比較 を参考にさせてもらい(ほぼ流用です。。。)
少しだけ比較するモジュールをたして計測してみた。

さっそく結果

[RSS]
                     Rate XML::Feed XML::RSS XML::Simple Data::Feed XML::RSS::LibXML XML::FeedPP XML::LibXML regexp
  XML::Feed        1.68/s        --      -1%        -96%       -96%             -96%        -97%       -100%  -100%
  XML::RSS         1.70/s        1%       --        -96%       -96%             -96%        -96%       -100%  -100%
  XML::Simple      37.9/s     2153%    2128%          --       -18%             -20%        -22%        -89%  -100%
  Data::Feed       46.1/s     2642%    2611%         22%         --              -3%         -5%        -87%   -99%
  XML::RSS::LibXML 47.3/s     2714%    2683%         25%         3%               --         -2%        -87%   -99%
  XML::FeedPP      48.3/s     2771%    2739%         27%         5%               2%          --        -86%   -99%
  XML::LibXML       356/s    21105%   20868%        841%       673%             653%        639%          --   -96%
  regexp           7941/s   472363%  467069%      20871%     17132%           16687%      16356%       2128%     --
 
 
[Atom]
               Rate XML::FeedPP  Data::Feed   XML::Feed   XML::Atom XML::LibXML
  XML::FeedPP 108/s          --        -68%        -69%        -73%        -81%
  Data::Feed  336/s        210%          --         -4%        -15%        -41%
  XML::Feed   352/s        225%          5%          --        -12%        -38%
  XML::Atom   398/s        267%         18%         13%          --        -30%
  XML::LibXML 570/s        426%         69%         62%         43%          --

正規表現は半端なくはやかったです。
さすがって感じ。ただ作るパターンは注意しないといけない。

で、びっくりしたのが XML::RSS がものすごくおそい。。
他にも XML::RSSLite や XML::RSS::Parser も試したが CDATA セクションが入ったりすると
パースエラーを起こしてパースできなくなったりしたんで、今回の対象からははずしました。

やっぱり速いのは XML::LibXML が Atom, RSS ともに圧倒的にはやかったです。
ただ個人的に XPath を書くのはあまりすきではないのがネック。
Atom だと XML::Atom という宮川さんが書かれたモジュールが速い。
ただ名前空間に Atom ってある通り RSS を読み込ませると試した限りパースできなかった。
Atom に最適化してあるからはやいのかな。
XML::RSS::LibXML も RSS に特化してる感じ。

Data::Feed XML::FeedPP XML::Feed の3つが RSS, Atom 共にパースできる。
中でも Data::Feed は id:lestrrat さんが作られてるとのこと。
そして 4/19 にも更新されててメンテもされてる。XML::FeedPP も XML::Feed もメンテはされてる。
ただ XML::Feed は RSS のパースがあまりに遅いので、Data::Feed か XML::FeedPP がいい気がする。
両方ともインターフェースは似てて同じ感覚で使える。あとは好みの問題なんだろーなと。
XML::FeedPP の pod の方が分かりやすいから、最初はそっちで触るのがよいかも。

XML::FeedPP か Data::Feed を使ってれば変なとこではささらない気がする。
あといらない情報が少ない Atom の方がパースも全体的に速かった。
つっこみなどあればよろしくです。