Blowfishの使い方

解決


昔から犬  2008-10-08 13:34:46  No: 32149  IP: 192.*.*.*

テキストフィールドの値を暗号化/復号化したくて
別スレで教えていただいたBlowfishを試しています。
サンプルを元に暗号化はそれらしいものが出力されたのですが
復号化がどうにもうまくいきません。

---------------------------
implementation
uses Blowfish, CryptUtils;

{$R *.dfm}

//暗号化
procedure TForm1.Button1Click(Sender: TObject);
const
   Key = 'ABCDEFG';
   IV: array [0..1 - 1] of Int64 = ($1234567890123456);
var
   Src, Dst: string;
   Size: Integer;
begin
   Src := Edit1.Text;
   with (TCryptCBC.Create(Key, TBlowFish.Create, IV, SizeOf(IV), pmStandard)) do
      try
        Size := Length(Src);
        SetLength(Dst, OutputSize(Size));
        Size := Encode(Src[1], Dst[1], Size);
        SetLength(Dst, Size);
         Edit1.Text := Dst;
         finally
            Free;
         end;
end;

//復号化
procedure TForm1.Button2Click(Sender: TObject);
const
   Key = 'ABCDEFG';
   IV: array [0..1 - 1] of Int64 = ($1234567890123456);
var
   Src, Dst: string;
   Size: Integer;
begin
   Src := Edit1.Text;
   with (TCryptCBC.Create(Key, TBlowFish.Create, IV, SizeOf(IV), pmStandard)) do
      try
        Size := OutputSize(Length(Src));
        SetLength(Dst, Size);
        Size := Decode(Src[1], Dst[1], Size);
        SetLength(Dst, Size);
         Edit2.Text := Dst;
         finally
            Free;
         end;
end;
---------------------------

この場合、ボタン2を押しても何も出力されません。
根本的な勘違いをしてるような気がするのですが
原因をご教示いただきたく、よろしくお願いいたします。

編集 削除
ofZ  2008-10-08 15:01:26  No: 32150  IP: 192.*.*.*

以下の二点かな?

Size := Encode(Src[1], Dst[1], Size);
  ↓
Size := Encode(Src[1], Dst[1], Length(Src));

Size := Decode(Src[1], Dst[1], Size);
  ↓
Size := Decode(Src[1], Dst[1], Length(Src));

Encode, Decodeの第三引数は、暗号化・復号化したあとのバッファサイズじゃなく、
暗号化・復号化すべきバッファのサイズと思われる。

編集 削除
鯖雄  2008-10-08 16:18:01  No: 32151  IP: 192.*.*.*

>oftさん
前スレからお付き合いいただいてありがとうございます。

>Size := Decode(Src[1], Dst[1], Length(Src));
一応Encodeのほうは、その2行前でLengthを取っています。
しかし、Decodeのほうは、Lengthを使うとCryptUtilsのDecodeの
Assertでエラーが発生します。
    Assert(Size mod BlockSize = 0);

何を入れればいいのやら・・・

編集 削除
昔から犬  2008-10-08 16:19:55  No: 32152  IP: 192.*.*.*

>鯖雄さん
鯖雄さんは名前を間違えました。
昔から犬と同一人物です。

編集 削除
Manbon  2008-10-08 16:24:55  No: 32153  IP: 192.*.*.*

良くは見てませんが、ここは参考になりませんか?
https://www.petitmonte.com/bbs/answers?question_id=2555

編集 削除
Manbon  2008-10-08 16:27:01  No: 32154  IP: 192.*.*.*

あと、ここにサンプルがありました。
http://kuma.webj.net/

編集 削除
ttt  2008-10-08 16:33:38  No: 32155  IP: 192.*.*.*

Encodeした結果のサイズは OutputSize(Size)
で、これはBlockSizeの倍数になっているはずなので、
本来はそのままDecodeに渡せば問題はないはずです。

> Assertでエラーが発生します。

ということは、Length(Decodeしようとしている文字列)≠Length(Encodeされた文字列) ということです。
例によって、EditBoxを経由したために文字が化けているのではないでしょうか。
Encodeの前と後、Decodeの前と後それぞれで(EditBoxで目視確認するのではなく)実際の変数の中身、サイズがそれぞれどうなっているか調べてみましたか?

編集 削除
昔から犬  2008-10-08 17:35:55  No: 32156  IP: 192.*.*.*

>Manbonさん
わざわざ探していただいて、ありがとうございます。
そのスレは事前にチェックしたのですが、確かにEncodeとDecodeを
一箇所で行う場合は問題ありませんでした。
それを分割した場合にSizeに問題があってダメなんだろうなあ、という
漠然とした感じです。

>tttさん
Encodeした結果のサイズをグローバル変数で保持して、Decodeで使ってみました。
Decode後の戻り値がとてもおかしい。
ここから辿ってみたいと思います。
ありがとうございました。

編集 削除
ofZ  2008-10-08 18:40:02  No: 32157  IP: 192.*.*.*

アーカイブに含まれるサンプルを見てテストしたけど、これで動いた。

      Size := OutputSize(Length(Src));
      SetLength(Dst, Size);
      Size := Encode(Src[1], Dst[1], Length(Src));
      SetLength(Dst, Size);
      Edit1.Text := Dst;

      Size := OutputSize(Length(Src));
      SetLength(Dst, Size);
      Size := Decode(Src[1], Dst[1], Length(Src));
      SetLength(Dst, Size);
      Edit2.Text := Dst;

Editに"漢字"を入力して、実行し、OutputSize=8, Encode結果も8
さらに変換してOutputSize=16,Decodeの結果が4になりました。

編集 削除
昔から犬  2008-10-08 18:59:40  No: 32158  IP: 192.*.*.*

>oftさん
ありがとうございます。
やはりDecode時にCryptUtilsのAssertで落ちました。
何が違うのか・・・少し頭を冷やします。

---------------------------
procedure TForm1.Button1Click(Sender: TObject);
const
   Key = 'ABCDEFG';
   IV: array [0..1 - 1] of Int64 = ($1234567890123456);
var
   Size: Integer;
   Src, Dst: string;
begin
   Src := Edit1.Text;
   with (TCryptCBC.Create(Key, TBlowFish.Create, IV, SizeOf(IV), pmStandard)) do
      try
         Size := OutputSize(Length(Src));
         SetLength(Dst, Size);
         Size := Encode(Src[1], Dst[1], Length(Src));
         SetLength(Dst, Size);
         Edit1.Text := Dst;
         finally
            Free;
         end;
end;

procedure TForm1.Button2Click(Sender: TObject);
const
   Key = 'ABCDEFG';
   IV: array [0..1 - 1] of Int64 = ($1234567890123456);
var
   Size: Integer;
   Src, Dst: string;
begin
   Src := Edit1.Text;
   with (TCryptCBC.Create(Key, TBlowFish.Create, IV, SizeOf(IV), pmStandard)) do
      try
         Size := OutputSize(Length(Src));
         SetLength(Dst, Size);
         Size := Decode(Src[1], Dst[1], Length(Src));
         SetLength(Dst, Size);
         Edit2.Text := Dst;
         finally
            Free;
         end;
end;
---------------------------

編集 削除
ofZ  2008-10-08 22:02:28  No: 32159  IP: 192.*.*.*

XPMan 貼り付けとかしていない?

編集 削除
原因はEdit  2008-10-09 08:06:04  No: 32160  IP: 192.*.*.*

>   Src := Edit1.Text;
Decordの際にエラーになるのは、既に指摘されてるように、
Editに入れた文字列を使って復号してることに原因がある。
  '1234567'
  'abcdef'
  '暗号化'
  'よろしく'
  '昔から犬'
これら↑を暗号化した文字列をEditに入れると、その末尾の
表示不可キャラクタ(1Byte)が欠落する。
そのため、Edit1.Textの文字列からDecodeするとAssertエラーで落ちる。
MemoやEditに入れた表示不可キャラクタがどう処理されるかは OSによって
少し違うけど。

編集 削除
ofZ  2008-10-09 08:37:50  No: 32161  IP: 192.*.*.*

> その末尾の表示不可キャラクタ(1Byte)が欠落する。
Edit に入れる前後で、16進表示するようにしたところ、
「表示不可キャラクタで」欠落することを確認しました。
当方では、偶然欠落しない文字列の甘いテストしていたわけですね。
失礼しました。

編集 削除
昔から犬  2008-10-09 10:22:13  No: 32162  IP: 192.*.*.*

>原因はEditさん
ご指摘のとおりでした。
上でtttさんにもご指摘いただきながらちゃんと検証できてなかったです。
「同じstringだからいいじゃん」という考えが甘かったようです。

>oftさん
大変お手間を取らせました。
ほんとうに助かりました。

-----------------------------------
implementation
uses Blowfish, CryptUtils;
var
   BeforEncode, AfterEncode: Integer;
   EncodedStr: string;
{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
const
   Key = 'ABCDEFG';
   IV: array [0..1 - 1] of Int64 = ($1234567890123456);
var
   Src: string;
begin
   Src := Edit1.Text;
   BeforEncode := Length(Edit1.Text);
   with (TCryptCBC.Create(Key, TBlowFish.Create, IV, SizeOf(IV), pmStandard)) do
      try
        SetLength(EncodedStr, OutputSize(BeforEncode));
        AfterEncode := Encode(Src[1], EncodedStr[1], BeforEncode);
         Edit1.Text := EncodedStr;
         finally
            Free;
         end;
end;

procedure TForm1.Button2Click(Sender: TObject);
const
   Key = 'ABCDEFG';
   IV: array [0..1 - 1] of Int64 = ($1234567890123456);
var
   Src, Decoded: string;
   Size: Integer;
begin
   Src := EncodedStr;
   SetLength(Src, AfterEncode);
   SetLength(Decoded, AfterEncode);
   with (TCryptCBC.Create(Key, TBlowFish.Create, IV, SizeOf(IV), pmStandard)) do
      try
        Size := Decode(Src[1], Decoded[1], AfterEncode);
         SetLength(Decoded, Size);
         Edit2.Text := Decoded;
         finally
            Free;
         end;
end;
-----------------------------------

Encodeした文字列をグローバルで保持することで
キレイに動作するようになりました。

ご協力いただいたみなさん、ほんとうにありがとうございました。

編集 削除
昔から犬(実装報告)  2008-10-10 14:17:52  No: 32163  IP: 192.*.*.*

後で見る方のために、アプリに実装してみて「あらら?」っていうことを書いておきます。

実装時に上記のルーチンをfunction化しました。
Editの文字列を暗号化してレジストリへ書き込み、必要時にレジストリから
復号化して読み出そうとしたわけですが、復号時にさんざん見飽きたエラーが発生しました。

調べてみると、レジストリから読み出した値のLengthがEncodeしたものより
1つだけ長くなっています。
原因は私の残り少ない脳細胞では判りません。

対症療法として、TRegistryをすべてTInifileにしてやるとOKでした。

以上、ご参考まで。

編集 削除
原因は同じ  2008-10-10 14:32:48  No: 32164  IP: 192.*.*.*

上でもさんざん言われているように、暗号化したデータが正当な文字列である保証なんてないんだから(動いたとしてもそれはたまたま)
バイナリとして保存するなり、可読な文字列に符号化するなりしないと……

編集 削除
昔から犬  2008-10-10 16:50:06  No: 32165  IP: 192.*.*.*

>原因は同じさん
できました!・・・ような気がする。

暗号化後にBase64エンコードして書き込み。
読み込み時は復号化前にBase64デコードした後で復号化。
これで正解ですか?
ってことは、これでレジストリでも何でも関係ないってことかorz

ほんとうに、ありがとうございました。

編集 削除