XOR operation with two strings in java

JavaStringOperatorsXor

Java Problem Overview


How to do bitwise XOR operation to two strings in java.

Java Solutions


Solution 1 - 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.

Solution 2 - Java

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]

Solution 3 - Java

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).

Solution 4 - Java

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));

}
}

Solution 5 - Java

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.

Solution 6 - Java

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;
}

Solution 7 - Java

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();
}
 

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionyasithaView Question on Stackoverflow
Solution 1 - Javauser467257View Answer on Stackoverflow
Solution 2 - JavaPeter LawreyView Answer on Stackoverflow
Solution 3 - JavaPaŭlo EbermannView Answer on Stackoverflow
Solution 4 - JavaJWLView Answer on Stackoverflow
Solution 5 - JavaBrian AgnewView Answer on Stackoverflow
Solution 6 - Javayegor256View Answer on Stackoverflow
Solution 7 - Javauser3514540View Answer on Stackoverflow