ActionScript 3.0で暗号化・復号化のメモ(1) データフォーマットについての予備知識(バイナリ、Hex、Base64...)

取り急ぎ、本業でActionScript 3.0でのスクリプティングが必要な案件にあたりました。
僕は、オブジェクト指向プログラミング、型指定、イベント...などといったキーワードから避けて通ってきてしまったように思います。
いやむしろ、「ActionScript 3.0はウチの開発者さんのための言語だろう」までに思っていたのですが、なにしろ取り急ぎだったので、慌てて書店に向かい2冊ほど開きながら書きましたよ。ふう。

参考になったサイト

ActionScript関連

as3crypto - Google Code
参考になった。というか、このライブラリで暗号化・復号化が実現できます。必須です。
また、下記のデモサイトで各種の暗号化・復号化が行えます。ActionScript 3.0の動作確認に重宝しました

as3cryptoのデモ確認ページ

ActionScript3.0を極める | AS3で暗号化
as3cryptoライブラリの利用例を一通り紹介してくれています。大変感謝感激でした。が、復号化については記載されていませんでした。

ウノウラボ Unoh Labs: PHPで暗号化して、Flashで復号してみたときのメモ
結局たどり着いたのが、ウノウラボKeitaさんの記事です。Flex2ですが壁を乗り越えることが出来ました。
感激のあまり、ブログコメントをしようまいかドギマギしたくらいです。ありがとうございます。

encrypton - ActionScript.org Forums
上記でもas3cryptoライブラリの記述例があります。PHPで照合確認するためのコードとPHPライブラリも配布されています。
ただし、64bitの復号用Keyに対応していないようで、結局今回は利用しませんでした。


as3cryptoライブラリを利用する(下準備)

(1) as3crypto - Google Code から Crypto.zip をダウンロード後、適宜解凍してください
例)解凍したファイル一式を(I:)ドライブの「/Crypto/」という名前のフォルダに置きました

(設置箇所)
I:\Crypto
(設置内容)
/certs/
/com/
BenchmarkTab.mxml
build-swc.xml
CryptoDemo.mxml
HashTab.mxml
HmacTab.mxml
LICENSE.txt
MonkeyTab.mxml
PublicTab.mxml
RELEASENOTES.txt
SecretTab.mxml
TLSDemo.mxml
UnitTestTab.mxml

(2) 上記のライブラリを利用するには
ActionScript 3.0でのメインタイムラインのフレームアクションに、次のように記述します(例として)

import com.hurlant.crypto.symmetric.*;
import com.hurlant.crypto.prng.Random;
import com.hurlant.crypto.Crypto;
import com.hurlant.crypto.hash.*;

(3) また、コンパイル時に上記(2)のクラスを読み込むためのパス指定を行います。
[ファイル > パブリッシュ設定 > "Flash"タブ] を選択し、 [ActionScriptのバージョン:]の右にある[設定ボタン]をクリックします。
[ActionScript 3.0設定]ウィンドウの[クラスパス:]欄に下記/Crypto/ライブラリのパスを追加します。

I:\Crypto

暗号化・復号化でやり取りするデータの種類について(プレーンテキスト,バイナリ,Hex,Base64)

暗号化についての基礎知識が皆無だった僕には、まずデータフォーマットについての予備知識が必要でした。
as3cryptoライブラリでは、プレーンテキストとHex文字列やbase64文字列とを簡単に変換することができるクラスが用意されています。
暗号化・復号化には、3種の文字列(プレーンテキスト、Hex、base64)とバイナリとの変換が必要ですので、as3cryptoライブラリを利用して各種変換する方法をご参考ください。

※また、暗号化・復号化の際に必ず出てくる用語についてのメモ書きをページ下部に列記しましたので、「バイナリ?聞いたことあるけど...」という僕くらいの方は目を通しておいてくださいね。


サンプルコード

下記.flaファイルをダウンロードください(flashCS3)。

.flaファイル

http://www.ideamans.com/_dat/sample-1.fla (約65KB)

上記の内容
import com.hurlant.util.Hex;
import com.hurlant.util.Base64;

/*■プレーンテキスト*/

	//半角文字の場合
	var strA:String;
	strA = 'abcdef';
	trace('プレーンテキスト(半角文字):\n' + strA + '\n');

	//全角文字(マルチバイトの場合)
	var strB:String;
	strB = 'あいうえお';
	trace('プレーンテキスト(全角文字):\n' + strB + '\n');
	

/*■バイナリ変換*/
	
	//半角文字の場合
	var baA:ByteArray = new ByteArray();
	baA.writeUTFBytes( strA);
	
	//全角文字(マルチバイトの場合)
	var baB:ByteArray = new ByteArray();
	baB.writeMultiByte( strB , "utf-8");


/*■2進数に変換して0と1の文字列で表す(確認のため)*/

	var i:int;
	//半角文字の場合
	var strA2:String = '';
	for ( i = 0; i <baA.length; i++ ){
		strA2 += baA[i].toString(2);
	}
	trace('バイナリを2進数の文字列に(半角文字):\n' + strA2 + '\n');
	
	//全角文字(マルチバイトの場合)
	var strB2:String = '';
	for ( i = 0; i <baB.length; i++ ){
		strB2 += baB[i].toString(2);
	}
	trace('バイナリを2進数の文字列に(全角文字):\n' + strB2 + '\n');


/*■Hex文字列変換*/
	//import com.hurlant.util.Hex を利用します
	
	//(1) プレーンテキストをHex文字列に変換
	var strHexA:String;
	strHexA = Hex.fromString(strA);
	trace('(1) プレーンテキストをHex文字列に変換:\n' + strHexA + '\n');
	
	//(2) バイナリをHex文字列に変換
	var strHexB:String;
	strHexB = Hex.fromArray(baA);
	trace('(2) バイナリをHex文字列に変換:\n' + strHexB + '\n');	
	
	//(1)と(2)が同じになりますね
	
	//(3) Hex文字列をプレーンテキストに変換
	var hexStr:String;
	hexStr = Hex.toString( strHexA );
	trace('(3) Hex文字列をプレーンテキストに変換:\n' + hexStr + '\n');		

	//(4) Hex文字列をバイナリに変換
	var hexBa:ByteArray;
	hexBa = Hex.toArray( strHexA );
	
	//2進数に変換して0と1の文字列で表す(確認のため)
	var hexBaStr:String = '';
	for ( i = 0; i <hexBa.length; i++ ){
		hexBaStr += hexBa[i].toString(2);
	}	
	trace('(4) Hex文字列をバイナリに変換し、確認の為2進数に変換して0と1の文字列で表す:\n' + hexBaStr + '\n');			


/*■base64文字列変換*/
	//import com.hurlant.util.Base64 を利用します

	//(5) プレーンテキストをBase64文字列に変換
	var strB64:String;
	strB64 = Base64.encode(strA);
	trace('(5) プレーンテキストをBase64文字列に変換:\n' + strB64 + '\n');
	
	//(6) バイナリをBase64文字列に変換
	var baB64:String;
	baB64 = Base64.encodeByteArray(baA);
	trace('(6) バイナリをBase64文字列に変換:\n' + baB64 + '\n');
	
	//(5)と(6)が同じになりますね
	
	//(7) Base64文字列をプレーンテキストに変換
	var b64Str:String;
	b64Str = Base64.decode( strB64 );
	trace('(7) Base64文字列をプレーンテキストに変換:\n' + b64Str + '\n');
	
	//(8) Base64文字列をバイナリに変換
	var b64Ba:ByteArray;
	b64Ba = Base64.decodeToByteArray( strB64 );
	
	//2進数に変換して0と1の文字列で表す(確認のため)
	var b64BaStr:String = '';
	for ( i = 0; i <b64Ba.length; i++ ){
		b64BaStr += b64Ba[i].toString(2);
	}	
	trace('(8) Base64文字列をバイナリに変換し、確認の為2進数に変換して0と1の文字列で表す:\n' + b64BaStr + '\n');
出力結果
プレーンテキスト(半角文字):
abcdef

プレーンテキスト(全角文字):
あいうえお

バイナリを2進数の文字列に(半角文字):
110000111000101100011110010011001011100110

バイナリを2進数の文字列に(全角文字):
111000111000000110000010111000111000000110000100111000111000000110000110111000111000000110001000111000111000000110001010

(1) プレーンテキストをHex文字列に変換:
616263646566

(2) バイナリをHex文字列に変換:
616263646566

(3) Hex文字列をプレーンテキストに変換:
abcdef

(4) Hex文字列をバイナリに変換し、確認の為2進数に変換して0と1の文字列で表す:
110000111000101100011110010011001011100110

(5) プレーンテキストをBase64文字列に変換:
YWJjZGVm

(6) バイナリをBase64文字列に変換:
YWJjZGVm

(7) Base64文字列をプレーンテキストに変換:
abcdef

(8) Base64文字列をバイナリに変換し、確認の為2進数に変換して0と1の文字列で表す:
110000111000101100011110010011001011100110

用語予備知識メモ

プレーンテキスト

「平文」とも。通常判読可能な文字列です。この文章もプレーンテキストとして変換表示されている。ということになります。

(プレーンテキスト)
本日は晴天なり
Binary(バイナリ)

プレーンテキスト形式(文字データ)以外のデータ形式全般のこと。

コンピュータが解釈する情報は「0」と「1」の2進法の数で構成される。0あるいは1の情報単位はビットと呼ばれる。1ビットが8つ並んで8ビットを形成すると、256種類のアルファベット、数字、カナなどの文字に対応させることができる。日本語の漢字を表現するには2バイトが必要である。2バイトあると4096文字までに対応させることができる。

バイト (byte)とは: - IT用語辞典バイナリ

僕らが普段PCで読んでいる文(文字)は、0か1かの数字の組み合わせで形成されています。
暗号化・復号化には、この「0110101011…」といったデータの並び(バイナリデータ)を利用します。

(プレーンテキスト)
本日は晴天なり

↓↓上記を2進数で表記↓↓↓

(バイナリ)
1001011001111011100100111111101010000010110011011001000010110000100100110101011010000010110010001000001011101000

(解説)
全角日本語1文字は2バイト(16ビット)で構成されるのでプレーンテキストの「本」は「1001011001111011」、

10010110 01111011 → 本
10010011 11111010 → 日
10000010 11001101 → は
10010000 10110000 → 晴
10010011 01010110 → 天
10000010 11001000 → な
10000010 11101000 → り

文字コードShift_jisを想定しています

Hex

Hex(Hexadecimal number)とは、

「基数を16とした数値の表現方法」で0〜9までの数字とA〜Fまでの文字を用いた文字列のことをいいます。

16進数とは 【hexadecimal number】 - 意味・解説 : IT用語辞典

16進数は,2進数の代用としてよく使われます。16進数と2進数は相互に変換が容易です。16進数の1桁が2進数の4桁にピッタリ一致するからです。その理由が分かりますか?2進数の4桁は2倍を4回分ですから,2進数の4桁=2倍×2倍×2倍×2倍=16倍=16進数の1桁になるからです。

【5分で覚えるIT基礎の基礎】ゼロから学ぶ2進数 第5回:ITpro

(プレーンテキスト)
本日は晴天なり

↓↓上記を2進数で表記↓↓↓

(バイナリ)
1001 0110 0111 1011 → 本
1001 0011 1111 1010 → 日
1000 0010 1100 1101 → は
1001 0000 1011 0000 → 晴
1001 0011 0101 0110 → 天
1000 0010 1100 1000 → な
1000 0010 1110 1000 → り

↓↓上記を16進数で表記↓↓↓

9 6 7 B → 本
9 3 F A → 日
8 2 C D → は
9 0 B 0 → 晴
9 3 5 6 → 天
8 2 C 8 → な
8 2 E 8 → り

↓↓
(Hex文字列)
967B93FA82CD90B0935682C882E8
Base64

2進数のバイナリデータを0123456789ABCDEFの16文字に置き換えるHexのほかに、下記の62つの文字、2つの記号と余白で置き換えた文字列のことをいいます。
後述するPadding(パディング)の存在をここで知りました。

Wikiの説明が分かりやすいですね。

A–Z, a–z, 0–9 までの62文字と、記号2つ (+ , /) 、さらにパディング(余った部分を詰める)のための記号として = が用いられる。この変換によって、データ量は4/3になる。

(Base64 - Wikipedia)

Wikiより引用)
変換例
元データ 
文字列: "ABCDEFG" 
16進数: 41, 42, 43, 44, 45, 46, 47 
2進数: 0100 0001, 0100 0010, 0100 0011, 0100 0100, 0100 0101, 0100 0110, 0100 0111 
6bitずつに分割 
010000 010100 001001 000011 010001 000100 010101 000110 010001 11 
2bit余るので、4bit分0を追加して6bitにする 
010000 010100 001001 000011 010001 000100 010101 000110 010001 110000 
変換表により、4文字ずつ変換 
"QUJD","REVG","Rw" 
2文字余るので、2文字分 = 記号を追加して4文字にする 
"QUJD","REVG","Rw==" 
Base64文字列 
"QUJDREVGRw==" 

次回予告

ActionScript3.0で暗号化・復号化のメモ(2) MD5,SHA,AES,DES,3DES,BlowFish
  • 暗号化・復号化の予備知識メモ
  • ハッシュ関数
    • MD5を利用する
    • SHAを利用する
  • 共通鍵暗号化・復号化
    • AESを利用する
    • DES,3DESを利用する
    • BlowFishを利用する