CAPICOM で暗号・復化

JavaからVBScriptへのAES暗号化によるデータ渡し (OKWave) から。気になったので試してみた。

Java は javax.crypto.Cipher で、VBS は CAPICOM を利用して、AES 暗号をする。


先に、結論。CAPICOM と他の暗号ライブラリでは、暗号データのやり取りは、簡単には出来ない。


日本語が混じると文字コードが面倒なので ASCII のみで。キーやデータのサイズもパディングとか面倒なので 16 byte (128 bit) 単位で。

VBS(CAPICOM) で暗号

まずは、VBS(CAPICOM) のみで、暗号・復化する。

Const CAPICOM_ENCRYPTION_ALGORITHM_AES = 4
Const CAPICOM_ENCRYPTION_KEY_LENGTH_128_BITS = 3

passwordString = "passwordpassword"
contentString  = "AES_CBC_128_BITS"
encryptString  = "MGIGCSsGA〜AvQ=="

' Encrypt
Set objCrypt = CreateObject("CAPICOM.EncryptedData")
objCrypt.Algorithm.Name      = CAPICOM_ENCRYPTION_ALGORITHM_AES
objCrypt.Algorithm.KeyLength = CAPICOM_ENCRYPTION_KEY_LENGTH_128_BITS
objCrypt.SetSecret passwordString
objCrypt.Content = contentString
encryptString = objCrypt.Encrypt
Set objCrypt = Nothing
WScript.Echo " encrypt : " & vbNewLine & encryptString

' Decrypt
Set objCrypt = CreateObject("CAPICOM.EncryptedData")
objCrypt.Algorithm.Name      = CAPICOM_ENCRYPTION_ALGORITHM_AES
objCrypt.Algorithm.KeyLength = CAPICOM_ENCRYPTION_KEY_LENGTH_128_BITS
objCrypt.SetSecret passwordString
objCrypt.Decrypt encryptString
contentString = objCrypt.Content
Set objCrypt = Nothing
WScript.Echo " decrypt : " & vbNewLine & contentString

実行するとこんな感じ。

C:\>cscript Crypt.vbs
Microsoft (R) Windows Script Host Version 5.6
Copyright (C) Microsoft Corporation 1996-2001. All rights reserved.

 encrypt :
MIGCBgkrBgEEAYI3WAOgdTBzBgorBgEEAYI3WAMBoGUwYwIDAgABAgJmDgICAIAE
EAAAAAAAAAAAAAAAAAAAAAAEENk3JQEmcgvYnbAd/egSfLoEMB0hne2Dg0Tsyn1t
gUi8XwRv/4ex8aQZLHjK0zfBhlzQRK54+7tJUMnEPtBFK1KA/A==

 decrypt :
AES_CBC_128_BITS

なぜ「こんな感じ」なのかは、実行する度に encrypt の結果が変わる。
つまり、ECB モードではなく、初期化ベクトル(IV)が必要な CBC, CFB, OFB あたりになる(CTR の可能性もあるけど)。

Java(javax.crypto.Cipher) で暗号

続いて、Java(javax.crypto.Cipher) のみで、CBC モードの暗号・復号する。

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

import sun.misc.BASE64Encoder;

public class Crypt {

	public static void main(String[] args) throws Exception {
		String passwordString = "passwordpassword";
		String contentString = "AES_CBC_128_BITS";
		//String encryptString = "MGIGCSsGA〜AvQ==";

		byte[] password = passwordString.getBytes("UTF-8");
		byte[] content = contentString.getBytes("UTF-8");
		byte[] encrypt = null;

		SecretKey key = new SecretKeySpec(password, "AES");
		IvParameterSpec iv = null; // new IvParameterSpec(null);
		Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");

		System.out.println("Algorithm : " + cipher.getAlgorithm());
		//System.out.println("BlockSize : " + cipher.getBlockSize());
		System.out.println();

		// Encrypt
		cipher.init(Cipher.ENCRYPT_MODE, key, iv);
		encrypt = cipher.doFinal(content);

		System.out.println(" IV :" + toString(cipher.getIV()));
		System.out.println(" encrypt :");
		System.out.println(toString(encrypt));
		System.out.println();

		// Decrypt
		iv = new IvParameterSpec(cipher.getIV());
		cipher.init(Cipher.DECRYPT_MODE, key, iv);
		content = cipher.doFinal(encrypt);

		System.out.println(" IV :" + toString(cipher.getIV()));
		System.out.println(" decrypt :");
		System.out.println(new String(content, "UTF-8"));
		System.out.println();

	}

	private static String toString(byte[] data) {
		if (data == null) return null;
		return (new BASE64Encoder()).encode(data);
	}

}

実行するとこんな感じ。

Algorithm : AES/CBC/NoPadding

 IV :LjdcGoe5Va6w5fgXqWbbGg==
 encrypt :
9H7zkgDCHuKPG0rt0IrzkA==

 IV :LjdcGoe5Va6w5fgXqWbbGg==
 decrypt :
AES_CBC_128_BITS

VBS - Java 間で暗号

それぞれで暗号・復号が出来るのは確認できたので、言語の垣根を越えてやってみる。

けど、いくつか不明な点がある。

1.暗号データのサイズが違う

VBS と Java とで出力されるデータのサイズが異なる。

2.鍵の値

CAPICOM で鍵として文字列を渡しているが、これがどう扱われているのかが不明。
キー長を 128bit と指定しているので、それに合わせた長さが必要だが、16文字未満でも問題なく動く。ので、パディングされるか、一方向ハッシュ値を求めているのでは?と思う。

3.モード・パディングが不明

CAPICOM で暗号アルゴリズムは指定出来るが、モードやパディングを指定出来ない。また、何を使用しているかもわからない。

実行の都度に結果が変わるので、少なくとも、ECB (Electronic Codebook) ではないと思う。
また、与えるデータが 16文字単位 でなくてもちゃんと動くので、何かしらパディングされているとは思う。

4.初期化ベクトル

ECB ではなく、CBC (Cipher Block Chaining) や CFB (Cipher Feedback), OFB (Output Feedback) だとすると、復号する為には必ず暗号時に使った初期化ベクトルが必要になる。ただ、CAPICOM で初期化ベクトルを取得・設定する方法がない。

しかし、CAPICOM では正しく復号することができる。また、出力される暗号データが Java に比べて大きいので、この中に一緒に含まれている可能性がある。