とタイプの間には微妙な(しかし非常に重要な)違いがchar
ありbyte
ます。このことを考慮:
class Krypte {
public static void main (String [] args) {
int i = 12345;
String k = Integer.toString(i);
System.out.println("Before: " + k);
String G = secure(k.toCharArray());
System.out.println("Encrypted: " + G);
String U = secure(G.toCharArray());
System.out.println("Decrypted: " + U);
int X = Integer.parseInt(U);
System.out.println("As an int: " + X);
}
public static String secure(char[] msg) {
// Variables
int outLength = msg.length;
byte secret = (byte) 0xAC; // same as 10101100b (Key)
// XOR kryptering
for (int i = 0; i < outLength; i++) {
// encrypting each byte with XOR (^)
System.out.println("Byte before: " + msg[i]);
msg[i] = (char) (msg[i] ^ secret);
System.out.println("Byte after: " + msg[i]);
}
return new String(msg);
}
}
ある文字値をバイトとXORすると(おそらく)有効な文字が得られるため、これは機能します(証明)。
元のスニペットで何が起こるかを見てみましょう-このデバッグ出力をsecure
メソッドのメインループに追加します:
System.out.println("Byte before: " + msg[i]);
msg[i] = (byte) (msg[i] ^ secret);
System.out.println("Byte after: " + msg[i]);
そして、出力は次のようになります。
Byte before: 49
Byte after: -99
Byte before: 50
Byte after: -98
Byte before: 51
Byte after: -97
Byte before: 52
Byte after: -104
Byte before: 53
Byte after: -103
それはまったく問題ありません。最初のgetBytes
関数は、プラットフォームのデフォルトの文字セットを使用して、指定された文字列をバイトの配列にエンコードしました。文字はバイト値にエンコードされます。などになります。'1'
49
'2'
50
次に、これらの値をキーとXORし、次のバイトシーケンスを取得します。
-99 -98 -97 -104 -103
最後のステップは簡単なようです。このバイトシーケンスから新しい文字列を作成(および返す)するだけですが、ここで何がうまくいかない可能性がありますか?しかし実際には、それはファンが打撃を受けるまさにそのステップです。)。
コンストラクターは、プラットフォームのデフォルトの文字セットを使用して、String
このバイトシーケンスを処理しようとします。実際、一部の文字セットでは、これらのバイトは有効な文字のシーケンスを適切に表しますが、UTF-8ではそうではありません。
...あなたはおそらく次に何が起こるかをすでに推測しています。ここで説明するように、「デコードできない」バイトシーケンスごとに、最初のバイトがいわゆる置換文字に変換され、他のバイトが再試行されます。この特定の例では、最初の呼び出しによって返された文字列に、これらの失敗の兆候が5つありますsecure
。
この文字列をデコードすることは、ターゲット文字列に関する情報(長さを除く)を格納しないため、まったく意味がありません。そのため、元のコードは最終的に失敗しました。