はじめに
MSXML2ライブラリを使用するときは、通常は、IXMLDOMDocument
のインスタンスを生成し、そのload
関数を呼び出す(ここでファイル名のBSTR
表現を引き渡す)ことで、XMLファイルをディスクからDOM(Document Object Model)オブジェクトにロードします。しかし、私は最近、セキュリティ上の問題から、まずメモリ内でXMLデータを復号してからそのメモリを(ディスクに書き出さずに)DOMオブジェクトにロードしなければならない、という事態に遭遇しました。私はオープンソースの例の中からこれを行う方法を探したのですが、意外にも見つけることができませんでした。そこで、このタスクを行うためのいくつかのヘルパー関数を作成しました。私と同じような状況に置かれた他の人々にも役立つと思うので、ここで紹介させてもらいます。
DecryptFile2Bstrヘルパー関数
説明を続ける前に、1993年にBruce Schneier氏がBlowfish暗号化アルゴリズムを発表して以来、多くの実装が登場していることを述べなければなりません。私は、高速で信頼できるデータ暗号化/復号のための暗号化手段を必要としていたのですが、その目的にはGeorge Anescu氏による実装を使用するのが特に簡単であると思いました。そのため、私の関数は、George Anescu氏によるCBlowFish
クラスを使用して暗号化されたデータを扱うようにできています。
私が解決策を探しているときに最初に学んだことは、IXMLDOMDocument
オブジェクトがloadXML
という関数をサポートしており、この関数は解析対象のXMLを表すBSTR
値を取るということです。クライアント側をできるだけ簡潔にする、というのが私のコーディングスタイルなので、今回も、クライアント側から呼び出せる1つの関数で、ほとんどすべてのことを行わせたいと考えていました。そこで作成したのが、入力ファイル名とパスワード(どちらもchar
ポインタ)を取り、BSTR
値を戻すDecryptFile2Bstr
関数です(下記参照)。戻されたBSTR
値は、IXMLDOMDocument::loadXML
関数で使用することができます。
下記のコードからわかるとおり、DecryptFile2Bstr
関数はCBlowFish
オブジェクトをインスタンス化した後、CBlowFish::Decrypt
を呼び出す前に、私が作成したもう1つのヘルパー関数であるGetFormattedFileContent
を呼び出します。GetFormattedFileContent
関数とCBlowFish::Decrypt
関数の両方に使用されるデータがchar
バッファという形式なので、DecryptFile2Bstr
関数は呼び出し元に戻る前に標準COMユーティリティ関数ConvertStringToBSTR
を呼び出します。
BSTR DecryptFile2Bstr(char* inputFileName, char* password) { try { int requiredFileSize; CBlowFish oBlowFish((unsigned char*)password, sizeof(password)); char *buffer = GetFormattedFileContent( inputFileName, requiredFileSize ); oBlowFish.Decrypt((unsigned char *)buffer, requiredFileSize); return _com_util::ConvertStringToBSTR(buffer); } catch ( char *ex ) { throw ex; } }
Blowfishアルゴリズムは8バイトのブロックをベースとするため、GetFormattedFileContent
関数はそのブロック単位でデータをメモリに読み込みます。このルールに適合させるために、関数の最後でパディングを行っていることに注目してください。
char* GetFormattedFileContent(char *filePath, int &requiredFileSize) { FILE *fp = fopen(filePath, "r+b"); int fileSize = FileSize(fp); int index = fileSize; if ( (fileSize % 8) != 0 ) requiredFileSize = ((fileSize / 8) + 1) * 8; else requiredFileSize = fileSize; char *buffer = new char[requiredFileSize + 1]; fread(buffer, sizeof(char), fileSize, fp); buffer[fileSize] = 0; fclose(fp); while (index < requiredFileSize) buffer[index++] = 0; return buffer; } int FileSize(FILE *fp) { char buffer[1]; int count = 0; fseek(fp, 0, SEEK_SET); while (fread(buffer, sizeof(buffer), 1, fp) != 0) count++; fseek(fp, 0, SEEK_SET); return count; }
DecryptFile2Bstr関数の使用
データがCBlowFish
クラスを使用して暗号化されている場合は、下記の太字で示した2行のコードで、そのデータをXML DOMオブジェクトにロードすることができます。
#import <msxml4.dll& named_guids using namespace MSXML2; ... ::CoInitialize(NULL); MSXML2::IXMLDOMDocumentPtr plDomDocument; HRESULT hr = plDomDocument.CreateInstance(MSXML2::CLSID_DOMDocument); if (SUCCEEDED(hr)) { // load the file as an XML document BSTR = DecryptFile2Bstr(L"MyFile.xml", DEFS_ENC_PASSWORD); variant_t vResult = plDomDocument->loadXML(xmlfile); ...
今後について
MSMXL2 IXMLDOMDocument::loadXML
を私のヘルパー関数と組み合わせることにより、ディスクへの復号を行うことなく、クライアントの機密データをメモリに読み込み、復号することができました。また、これらの関数を拡張し、CBlowFish
で暗号化されたデータを持つファイルを指定ファイルに復号するヘルパー関数(DecryptFile2File
)や、プレーンASCIIデータをCBlowFish
で暗号化されたファイルにするヘルパー関数(EncryptFile2File
)などを組み込めるようにしました。もしこれらの関数を必要としている方、あるいは興味をお持ちの方がいましたら、これらも公開しようと思います。