- PR -

暗号化/復号化について。

投稿者投稿内容
Hongliang
ぬし
会議室デビュー日: 2004/12/25
投稿数: 576
投稿日時: 2006-01-16 14:55
引用:

イノキイズムさんの書き込み (2006-01-16 11:13) より:
引用:

Jittaさんの書き込み (2006-01-13 19:19) より:
MemoryStream で、長さが足りない、っうことだったら、これかな?
http://www.atmarkit.co.jp/bbs/phpBB/viewtopic.php?topic=4155&forum=7


リンク参考にさせて頂きました。
じっくり読んでみたのですが、あまり理解できませんでした。すみません…。

解決策としては以下の処理を追加すれば良いということなのでしょうか?

「ICryptoTransform.InputBlockSizeに満たないサイズのブロックを最後に出力したい場合は、crypto.FlushFinalBlock();をすればいいです。 」

もし、そうだとした場合、”ICryptoTransform.InputBlockSizeに満たないサイズ”
とはどういうことなのでしょうか?


いえ、今回のケースではこれは関係ありません。

TripleDESを含む一部の暗号形式では、暗号化する際、元データを何ビットかずつブロックに分けて、それを暗号化していきます。このブロックのサイズはTripleDESCryptoServiceProvider.BlockSizeで取得できます。
この仕組みのため、元データをストリームに書き込む際、このブロックサイズ分ずつしか暗号化できないので、例えばブロックサイズ64ビットのときにCryptoStreamに10バイト(80ビット)書き込んだとき、初めの8バイトはそのまま(暗号化して)書き込まれますが、残りの2バイトは一旦保留されます。続けて6バイト書き込めばその保留していた2バイトとともにブロックが足りるようになるので暗号化でき、書き込まれます。
なお、書き込む先は大体の場合フレームワークが用意するバッファであって、元のストリームに直接書き込みはしません。バッファにデータが十分に溜まったころを見計らって一気に元ストリームに書き出します(フレームワークが)。ユーザが強制的にバッファから元ストリームに書き出すよう指示するのがFlushメソッドです。
データ全体の量が10バイトだった場合、最後の2バイトはそのままでは暗号化できないため、なんらかのデータを埋め込む必要があります。埋め込みデータはフレームワークが自動的に補完してくれますが(SymmetricAlgiruthm.Paddingで埋め込みモードを指定できます)、「いつがデータの終わりなのか」はユーザが指定してやらなければなりません。これを特に指示するのがCryptoStream.FlushFinalBlockメソッドです。通常のFlushメソッドではブロックサイズごとにしかフラッシュされません。
が、当然のことながらCryptoStream.Closeメソッドの呼び出しにはFlushFinalBlockメソッドと同じ処理が含まれます。今回のサンプルではWrite直後にCloseが呼び出されているため、この暗号化ブロックに関係する問題はクリアされています。

ブロックごとに暗号化し、最後の端数データもダミーを埋め込んでブロックのサイズに合うよう調整するため、暗号化されたデータの長さはブロックサイズの倍数になります。CryptoStreamをCloseしたあと、MemoryStreamあるいはそれをToArrayしたバイト配列の長さを確認してみてください。そして、それをUnicodeでデコードしたときの文字列の長さを確認してみてください。
Unicodeでは1文字2バイトですから通常はMemoryStreamの長さのちょうど半分になりますが、私の前回の投稿で書いたような問題が発生すると、文字列の長さが不足するようになります。
失われたデータはデコード済み文字列からは再現できないため、バイト配列に再エンコードしたときブロックサイズの倍数ではなくなります。そのため「復号化するデータの長さが無効です」という例外が発生しているわけです。
//まれに、例えばブロックサイズが64ビット=8バイトで、9から16バイトのデータを書き込んで、うち8バイト4文字分が失われて、ブロックサイズの倍数にはなっている、なんて状況もあるでしょうが。ま、別の例外になるだけですけど。
解決策は、暗号化済みデータに対してUnicodeを使わない、というか文字列エンコーディングを使わない、と言うことになるかと思います。何らかの手段でバイト配列をそのまま保持してください。あるいはBase64エンコーディングと言うのも手ですね。
Jitta
ぬし
会議室デビュー日: 2002/07/05
投稿数: 6267
お住まい・勤務地: 兵庫県・海手
投稿日時: 2006-01-16 22:15
フィードバックしておきます
___________________________________
□ written by Jitta on 2006/01/16
元イノキイズム
常連さん
会議室デビュー日: 2005/12/05
投稿数: 24
投稿日時: 2006-01-17 09:24
イノキイズムです。
Hongliang様ご回答ありがとうございます。

引用:

Hongliangさんの書き込み (2006-01-16 14:55) より:

ブロックごとに暗号化し、最後の端数データもダミーを埋め込んでブロックのサイズに合うよう調整するため、暗号化されたデータの長さはブロックサイズの倍数になります。CryptoStreamをCloseしたあと、MemoryStreamあるいはそれをToArrayしたバイト配列の長さを確認してみてください。そして、それをUnicodeでデコードしたときの文字列の長さを確認してみてください。
Unicodeでは1文字2バイトですから通常はMemoryStreamの長さのちょうど半分になりますが、私の前回の投稿で書いたような問題が発生すると、文字列の長さが不足するようになります。
失われたデータはデコード済み文字列からは再現できないため、バイト配列に再エンコードしたときブロックサイズの倍数ではなくなります。そのため「復号化するデータの長さが無効です」という例外が発生しているわけです。
//まれに、例えばブロックサイズが64ビット=8バイトで、9から16バイトのデータを書き込んで、うち8バイト4文字分が失われて、ブロックサイズの倍数にはなっている、なんて状況もあるでしょうが。ま、別の例外になるだけですけど。
解決策は、暗号化済みデータに対してUnicodeを使わない、というか文字列エンコーディングを使わない、と言うことになるかと思います。何らかの手段でバイト配列をそのまま保持してください。あるいはBase64エンコーディングと言うのも手ですね。


ご丁寧な説明ありがとうございました。

1つ疑問がなのですが、Webでこの暗号化/復号化のサンプルをいろいろと
検索すると、どのサンプルでも大体Unicodeでエンコードしてあるように思います。
暗号化/復号化はパスワードなどに対して行われると思いますが、
その場合、一般的(半角英数字と一部の記号を使用)なパスワードに対しての
暗号化/復号化はみなさんどのように行っているのでしょうか?
解決策にありましたように、文字列エンコーディングかBase64エンコーディングで
対応しているのでしょうか。
Hongliang
ぬし
会議室デビュー日: 2004/12/25
投稿数: 576
投稿日時: 2006-01-17 14:47
引用:

1つ疑問がなのですが、Webでこの暗号化/復号化のサンプルをいろいろと
検索すると、どのサンプルでも大体Unicodeでエンコードしてあるように思います。
暗号化/復号化はパスワードなどに対して行われると思いますが、
その場合、一般的(半角英数字と一部の記号を使用)なパスワードに対しての
暗号化/復号化はみなさんどのように行っているのでしょうか?
解決策にありましたように、文字列エンコーディングかBase64エンコーディングで
対応しているのでしょうか。



私の投稿をよく読んでください。
引用:

暗号化されたバイト列をわざわざUnicodeで文字列に変換してるからですね。

引用:

暗号化済みデータに対してUnicodeを使わない、というか文字列エンコーディングを使わない、と言うことになるかと思います。


使うと駄目だといっているのは飽くまで暗号化済みデータに対してです。暗号化する文字列に対しては何も言っていません。
.NETでは文字列をUnicodeで管理していますから、元文字列がどんなものであれUnicodeで問題なくデコードできます(だから各種の例もUnicode(UTF-16)やUTF-8を使っているんでしょう)。問題なのは、バイト配列から文字列にエンコードする場合です。この際も、バイト配列が文字列をUnicodeでデコードして得られたものなら問題ありません。しかし、暗号化して得られたバイト配列はUnicode文字列用のバイト配列ではありません。ですからこれに対してUnicodeエンコードするのは不適切です。
暗号化作業には性質が異なる二種類のバイト配列が現れることに注意してください。

元の文字列

元の文字列をデコードしたバイト配列 //Encoding派生クラスを使用する

(暗号化してストリームに書き込み)

暗号化済みデータのバイト配列 //文字列にはなりえない


普通暗号化済みデータはユーザが目にすることはないものです。
大抵はそのまま送信したりファイルやデータベースに書き込むだけですから、わざわざ文字列に変換する、つまりユーザの判読できる形にする必要もまず無いでしょう。
そもそも文字列に変換されてもユーザは理解できませんし。
元イノキイズム
常連さん
会議室デビュー日: 2005/12/05
投稿数: 24
投稿日時: 2006-01-18 10:33
イノキイズムです。
返事遅くなってしまい申し訳ありません。
Hongliang様ご回答ありがとうございました。

引用:

Hongliangさんの書き込み (2006-01-17 14:47) より:

使うと駄目だといっているのは飽くまで暗号化済みデータに対してです。暗号化する文字列に対しては何も言っていません。
.NETでは文字列をUnicodeで管理していますから、元文字列がどんなものであれUnicodeで問題なくデコードできます(だから各種の例もUnicode(UTF-16)やUTF-8を使っているんでしょう)。問題なのは、バイト配列から文字列にエンコードする場合です。この際も、バイト配列が文字列をUnicodeでデコードして得られたものなら問題ありません。しかし、暗号化して得られたバイト配列はUnicode文字列用のバイト配列ではありません。ですからこれに対してUnicodeエンコードするのは不適切です。
暗号化作業には性質が異なる二種類のバイト配列が現れることに注意してください。

元の文字列

元の文字列をデコードしたバイト配列 //Encoding派生クラスを使用する

(暗号化してストリームに書き込み)

暗号化済みデータのバイト配列 //文字列にはなりえない


普通暗号化済みデータはユーザが目にすることはないものです。
大抵はそのまま送信したりファイルやデータベースに書き込むだけですから、わざわざ文字列に変換する、つまりユーザの判読できる形にする必要もまず無いでしょう。
そもそも文字列に変換されてもユーザは理解できませんし。


ご丁寧に説明して頂きありがとうございます。
ようやく理解できました。

スキルアップ/キャリアアップ(JOB@IT)