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);
}
}
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.
Related
I have a String in Java that contains 32 characters:
String tempHash = "123456789ABCDEF123456789ABCDEF12";
Each character in the String above represents a hex value. I need to convert it to another String, that contains 8-bytes calculated by each hex from the string above. So in the example from above, the output string would be:
"00000001 00000010 00000011 000001000 000001001 000001011 ..."
how can I do that?
I tried to do:
byte[] bytes1 = toByteArray(tempHash);
where
public static byte[] toByteArray(String s) {
return DatatypeConverter.parseHexBinary(s);
}
but when I iterate over this array:
for (byte singleByte: bytes1) {
System.out.println(singleByte);
}
for the first character I'm getting 18 instead of 00000001.
I'm a little bit lost over here. Could you please help me with that?
One solution is to use a Stream:
String tempHash = "123456789ABCDEF123456789ABCDEF12";
String binary = tempHash.chars() // Get stream of chars
.map(c -> Character.digit(c, 16)) // Convert to hex digit
.mapToObj(Integer::toBinaryString) // Convert to binary
.map(s -> "0".repeat(8 - s.length()) + s) // Pad left with zeros
.collect(Collectors.joining(" ")); // Collect to String
System.out.println(binary);
Output:
00000001 00000010 00000011 00000100 00000101 ...
As Kevin pointed out in his comment below, a pre-Java 11 solution would be to replace the call to String#repeat:
String binary = tempHash.chars() // Get stream of chars
.map(c -> Character.digit(c, 16)) // Convert to hex digit
.mapToObj(Integer::toBinaryString) // Convert to binary
.map(s -> new String(new char[8 - s.length()]).replace('\0', '0') + s) // Pad left with zeros
.collect(Collectors.joining(" ")); // Collect to String
You can use Long.parseLong(String,16);
Once you have a long value, you can get the bytes by doing
long val = ...;
ByteBuffer buf = new ByteBuffer();
buf.put(0, val);
If your string is too long you will need to use a BigInteger. It's essentially the same thing, but a little more complicated
public byte hexToByte(String hexString) {
int firstDigit = toDigit(hexString.charAt(0));
int secondDigit = toDigit(hexString.charAt(1));
return (byte) ((firstDigit << 4) + secondDigit);
}
private int toDigit(char hexChar) {
int digit = Character.digit(hexChar, 16);
if(digit == -1) {
throw new IllegalArgumentException(
"Invalid Hexadecimal Character: "+ hexChar);
}
return digit;
}
Here is the reference
I am working on a caesar cipher program as my final project in java I. I have a method for the encryption that handles lower and uppercase characters. However I have hit a wall trying to adjust the algorithm to work the shift factor in the opposite direction to decrypt text already run through the program.
Another issue is that I cannot make the program work with spaces, either ignoring them or encrypting them as well. So far it only encrypts the first word and ignores the rest.
Here is my encryption method thus far, with the message passed from main() as a parameter.
//Caesar Cipher
public class EncryptionClass1 {
protected static String encryptedMessageString;
public static String setEncryptedMessage(String message) {
StringBuffer encryptedMessageBuffer = new StringBuffer();
char newCharacter;
int shiftFactor = 1;
int i;
//for length of secret message
for (i = 0; i<message.length(); i++) {
//uppercase encoder
if (Character.isUpperCase(message.charAt(i))) {
newCharacter = (char)(((int)message.charAt(i) -
shiftFactor - 65) % 26 + 65);
encryptedMessageBuffer.append(newCharacter);
}//if close
//lowercase encoder
else {
newCharacter = (char)(((int)message.charAt(i) -
shiftFactor - 97) % 26 + 97);
encryptedMessageBuffer.append(newCharacter);
}//else close
}//for close
//convert StringBuffer to string
encryptedMessageString = encryptedMessageBuffer.toString();
return encryptedMessageString;
}//setEncryptedMessage close
}//EncryptionClass1 close
For the decryption, I figure that I only need to adjust some of the operations for the encryption method, but I cant pinpoint what.
as for the spaces, an input and output example should be as follows:
Input: “my secret message”
Output: rp dbxbbf rbddtfb
To shift in the opposite direction, shift further in the current direction. Visualize the 26 letters of the alphabet on a 360° dial. If encryption is to rotate (shift) the dial 5 letters to the right, you'd normally think of decryption as rotating the dial 5 letters to the left. But you could also rotate the dial 21 (26 - 5) letters to the right. So, if encryption is shifting shiftFactor up, then decryption is shifting 26 - shiftFactor up. By only shifting up, the % 26 operation will correctly wrap the shift around.
Since the encrypted message is always exactly the same length as the original message, and you only replace one letter at a time, the code will be simpler if you just work on a char[] from the original message. If you do that, then simply skip characters in the array that are not ASCII letters.
Instead of using magic numbers 65 and 97 , use their char values to make the code more readable.
All of the above means that your code could be written like this:
private static final int SHIFT_FACTOR = 5;
public static String encryptMessage(String message) {
return applyShift(message, SHIFT_FACTOR);
}
public static String decryptMessage(String message) {
return applyShift(message, 26 - SHIFT_FACTOR);
}
private static String applyShift(String message, int shiftFactor) {
char[] chars = message.toCharArray();
for (int i = 0; i < chars.length; i++) {
char c = chars[i];
if (c >= 'A' && c <= 'Z')
chars[i] = (char) ((c - 'A' + shiftFactor) % 26 + 'A');
else if (c >= 'a' && c <= 'z')
chars[i] = (char) ((c - 'a' + shiftFactor) % 26 + 'a');
}
return new String(chars);
}
Test
public static void main(String[] args) {
test("my secret message");
test("Hello World!");
}
private static void test(String message) {
String encrypted = encryptMessage(message);
System.out.println("Input : " + message);
System.out.println("Encrypted: " + encrypted);
System.out.println("Decrypted: " + decryptMessage(encrypted));
System.out.println();
}
Output
Input : my secret message
Encrypted: rd xjhwjy rjxxflj
Decrypted: my secret message
Input : Hello World!
Encrypted: Mjqqt Btwqi!
Decrypted: Hello World!
I have the following code trying to convert between byte and bit arrays, somehow it's not converting correctly, what is wrong and how to correct it ?
String getBitsFromBytes(byte[] Byte_Array) // 129
{
String Bits="";
for (int i=0;i<Byte_Array.length;i++) Bits+=String.format("%8s",Integer.toBinaryString(Byte_Array[i] & 0xFF)).replace(' ','0');
System.out.println(Bits); // 10000001
return Bits;
}
byte[] getBytesFromBits(int[] bits)
{
byte[] results=new byte[(bits.length+7)/8];
int byteValue=0;
int index;
for (index=0;index<bits.length;index++)
{
byteValue=(byteValue<<1)|bits[index];
if (index%8==7) results[index/8]=(byte)byteValue;
}
if (index%8!=0) results[index/8]=(byte)((byte)byteValue<<(8-(index%8)));
System.out.println(Arrays.toString(results));
return results;
}
...
String bit_string=getBitsFromBytes("ab".getBytes()); // 0110000101100010 : 01100001 + 01100010 --> ab
int[] bits=new int[bit_string.length()];
for (int i=0;i<bits.length;i++) bits[i]=Integer.parseInt(bit_string.substring(i,i+1));
getBytesFromBits(bits);
When I ran it, I got the following :
0110000101100010
[97, 98]
I was expecting this :
0110000101100010
[a, b]
You need to convert from byte to char if you plan to display numeric values as their corresponding ASCII character:
char[] chars = new char[results.length];
for (int i = 0; i < results.length; i++) {
chars[i] = (char) results[i];
}
System.out.println(Arrays.toString(chars));
To convert from byte[] to String you should use new String(byte[]) constructor and specify the right charset. Arrays.toString() exists only to print a sequence of elements.
How to do bitwise XOR operation to two strings in java.
You want something like this:
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;
import java.io.IOException;
public class StringXORer {
public String encode(String s, String key) {
return base64Encode(xorWithKey(s.getBytes(), key.getBytes()));
}
public String decode(String s, String key) {
return new String(xorWithKey(base64Decode(s), key.getBytes()));
}
private byte[] xorWithKey(byte[] a, byte[] key) {
byte[] out = new byte[a.length];
for (int i = 0; i < a.length; i++) {
out[i] = (byte) (a[i] ^ key[i%key.length]);
}
return out;
}
private byte[] base64Decode(String s) {
try {
BASE64Decoder d = new BASE64Decoder();
return d.decodeBuffer(s);
} catch (IOException e) {throw new RuntimeException(e);}
}
private String base64Encode(byte[] bytes) {
BASE64Encoder enc = new BASE64Encoder();
return enc.encode(bytes).replaceAll("\\s", "");
}
}
The base64 encoding is done because xor'ing the bytes of a string may not give valid bytes back for a string.
Note: this only works for low characters i.e. below 0x8000, This works for all ASCII characters.
I would do an XOR each charAt() to create a new String. Like
String s, key;
StringBuilder sb = new StringBuilder();
for(int i = 0; i < s.length(); i++)
sb.append((char)(s.charAt(i) ^ key.charAt(i % key.length())));
String result = sb.toString();
In response to #user467257's comment
If your input/output is utf-8 and you xor "a" and "æ", you are left with an invalid utf-8 string consisting of one character (decimal 135, a continuation character).
It is the char values which are being xor'ed, but the byte values and this produces a character whichc an be UTF-8 encoded.
public static void main(String... args) throws UnsupportedEncodingException {
char ch1 = 'a';
char ch2 = 'æ';
char ch3 = (char) (ch1 ^ ch2);
System.out.println((int) ch3 + " UTF-8 encoded is " + Arrays.toString(String.valueOf(ch3).getBytes("UTF-8")));
}
prints
135 UTF-8 encoded is [-62, -121]
Pay attention:
A Java char corresponds to a UTF-16 code unit, and in some cases two consecutive chars (a so-called surrogate pair) are needed for one real Unicode character (codepoint).
XORing two valid UTF-16 sequences (i.e. Java Strings char by char, or byte by byte after encoding to UTF-16) does not necessarily give you another valid UTF-16 string - you may have unpaired surrogates as a result. (It would still be a perfectly usable Java String, just the codepoint-concerning methods could get confused, and the ones that convert to other encodings for output and similar.)
The same is valid if you first convert your Strings to UTF-8 and then XOR these bytes - here you quite probably will end up with a byte sequence which is not valid UTF-8, if your Strings were not already both pure ASCII strings.
Even if you try to do it right and iterate over your two Strings by codepoint and try to XOR the codepoints, you can end up with codepoints outside the valid range (for example, U+FFFFF (plane 15) XOR U+10000 (plane 16) = U+1FFFFF (which would the last character of plane 31), way above the range of existing codepoints. And you could also end up this way with codepoints reserved for surrogates (= not valid ones).
If your strings only contain chars < 128, 256, 512, 1024, 2048, 4096, 8192, 16384, or 32768, then the (char-wise) XORed strings will be in the same range, and thus certainly not contain any surrogates. In the first two cases you could also encode your String as ASCII or Latin-1, respectively, and have the same XOR-result for the bytes. (You still can end up with control chars, which may be a problem for you.)
What I'm finally saying here: don't expect the result of encrypting Strings to be a valid string again - instead, simply store and transmit it as a byte[] (or a stream of bytes). (And yes, convert to UTF-8 before encrypting, and from UTF-8 after decrypting).
This solution is compatible with Android (I've tested and used it myself). Thanks to #user467257 whose solution I adapted this from.
import android.util.Base64;
public class StringXORer {
public String encode(String s, String key) {
return new String(Base64.encode(xorWithKey(s.getBytes(), key.getBytes()), Base64.DEFAULT));
}
public String decode(String s, String key) {
return new String(xorWithKey(base64Decode(s), key.getBytes()));
}
private byte[] xorWithKey(byte[] a, byte[] key) {
byte[] out = new byte[a.length];
for (int i = 0; i < a.length; i++) {
out[i] = (byte) (a[i] ^ key[i%key.length]);
}
return out;
}
private byte[] base64Decode(String s) {
return Base64.decode(s,Base64.DEFAULT);
}
private String base64Encode(byte[] bytes) {
return new String(Base64.encode(bytes,Base64.DEFAULT));
}
}
Assuming (!) the strings are of equal length, why not convert the strings to byte arrays and then XOR the bytes. The resultant byte arrays may be of different lengths too depending on your encoding (e.g. UTF8 will expand to different byte lengths for different characters).
You should be careful to specify the character encoding to ensure consistent/reliable string/byte conversion.
This is the code I'm using:
private static byte[] xor(final byte[] input, final byte[] secret) {
final byte[] output = new byte[input.length];
if (secret.length == 0) {
throw new IllegalArgumentException("empty security key");
}
int spos = 0;
for (int pos = 0; pos < input.length; ++pos) {
output[pos] = (byte) (input[pos] ^ secret[spos]);
++spos;
if (spos >= secret.length) {
spos = 0;
}
}
return output;
}
the abs function is when the Strings are not the same length so the legth of the result will be the same as the min lenght of the two String a and b
public String xor(String a, String b){
StringBuilder sb = new StringBuilder();
for(int k=0; k < a.length(); k++)
sb.append((a.charAt(k) ^ b.charAt(k + (Math.abs(a.length() - b.length()))))) ;
return sb.toString();
}
I am struggling trying to convert a hex number string back to the original string. I convert the string using the following method:
private static String hex(String binStr) {
String newStr = new String();
try {
String hexStr = "0123456789ABCDEF";
byte [] p = binStr.getBytes();
for(int k=0; k < p.length; k++ ){
int j = ( p[k] >> 4 )&0xF;
newStr = newStr + hexStr.charAt( j );
j = p[k]&0xF;
newStr = newStr + hexStr.charAt( j ) + " ";
}
} catch (Exception e) {
System.out.println("Failed to convert into hex values: " + e);
}
return newStr;
}
I am really stuck, and any advice would be greatly appreciated.
Thank you in advance
Consider this:
String hexStr = "0123456789ABCDEF";
long i = Long.valueOf(hexStr, 16);
System.out.println(Long.toHexString(i));
The code in the question destroys information. Only the most significant two bits and the least significant four bits of each input byte contribute to the result. That means it cannot, in general, be reversed.
If the right shift had been by four bits, instead of 6:
int j = ( p[k] >> 4 )&0xF;
all the input would have been preserved, and the original string could have been recovered from the hex string. Maybe you really meant the four bit shift?