Reputation: 87
I'm trying to convert an Integer to a String, and then encrypt the String with a XOR encryption. But when i'm decrypting my Strin again, i get a different answer, that the String i typed before the encryption, and i don't know what i'm doing wrong?
public class Krypte {
public static void main (String [] args) {
int i = 12345;
String k = Integer.toString(i);
String G = secure(k.getBytes());
System.out.println("Encrypted: " + G);
String U = secure(G.getBytes());
System.out.println("Decrypted: " + U);
int X = Integer.parseInt(U);
System.out.println("As an int: " + X);
}
public static String secure(byte[] 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 (^)
msg[i] = (byte) (msg[i] ^ secret);
}
return new String(msg);
}
}
Upvotes: 2
Views: 1396
Reputation: 106385
There's a subtle (yet very important) difference between char
and byte
types. Consider this:
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);
}
}
This works (proof), because XORing some character value with a byte will (most probably) give you a valid character.
Not let's see what happens in the original snippet - by adding this debugging output into the main loop of secure
method:
System.out.println("Byte before: " + msg[i]);
msg[i] = (byte) (msg[i] ^ secret);
System.out.println("Byte after: " + msg[i]);
And the output would be:
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
It's quite ok: first getBytes
function encoded the string given into an array of bytes using the platform's default charset. Character '1'
gets encoded into 49
byte value; '2'
becomes 50
, etc.
Then we're XORing these values with our key - and get this sequence of bytes:
-99 -98 -97 -104 -103
The final step seems easy: we just make (and return) a new String from this sequence of bytes, what can go wrong here? But in fact it's the very step where, well, the fan get hit. )
See, String
constructor tries to process this sequence of bytes using the platform's default charset. Indeed, for some charsets these bytes represent a sequence of valid characters just fine - but not for UTF-8!
...You probably already guessed what happens next. For each 'undecodable' sequence of bytes, as described here, the first byte is transformed into so-called Replacement character, and others are retried. In this particular example there would be five of these signs of failure in the string returned by the first secure
invokation.
Decoding this string is, well, quite meaningless - as it doesn't store any information (except length) about the target string. That's why the original code ultimately failed.
Upvotes: 5