javaencryptionrc4-cipher

RC4 encryption java


Hi there I am trying to implement the RC4 algorithm in Java. I found this code as an example that help me to understand the idea:

public class RC4 {
  private int[] S = new int[256];
  private int[] T = new int[256];
  private int keylen;

  public RC4(byte[] key) throws Exception {
    if (key.length < 1 || key.length > 256) {
      throw new Exception("key must be between 1 and 256 bytes");
    } else {
      keylen = key.length;
      for (int i = 0; i < 256; i++) {
        S[i] = i;
        T[i] = key[i % keylen];
      }
      int j = 0;
      for (int i = 0; i < 256; i++) {
        j = (j + S[i] + T[i]) % 256;
        S[i] ^= S[j];
        S[j] ^= S[i];
        S[i] ^= S[j];
      }
    }
  }

  public int[] encrypt(int[] plaintext) {
    int[] ciphertext = new int[plaintext.length];
    int i = 0, j = 0, k, t;
    for (int counter = 0; counter < plaintext.length; counter++) {
      i = (i + 1) % 256;
      j = (j + S[i]) % 256;
      S[i] ^= S[j];
      S[j] ^= S[i];
      S[i] ^= S[j];
      t = (S[i] + S[j]) % 256;
      k = S[t];
      ciphertext[counter] = plaintext[counter] ^ k;
    }
    return ciphertext;
  }

  public int[] decrypt(int[] ciphertext) {
    return encrypt(ciphertext);
  }
}

I have few question:

  1. Why is the plain-text an int array in the above code?

  2. When I test this code I get strange result, can somebody explain to me? Here my code to test:

    public class RC4_Main {
    
        public static void main(String args[]) throws Exception {
            String keyword = "hello";
            byte[] keytest = keyword.getBytes(); //convert keyword to byte
    
            int[] text = {1, 2, 3, 4, 5}; // text as 12345
    
            RC4 rc4 = new RC4(keytest);
    
            System.out.print("\noriginal text: ");
            for (int i = 0; i < text.length; i++) {          
                System.out.print(text[i]);          
            }    
    
            int[] cipher = rc4.encrypt(text); //encryption      
            System.out.print("\ncipher: ");
            for (int i = 0; i < cipher.length; i++) {          
                System.out.print(cipher[i]);          
            }    
    
            int[] backtext = rc4.decrypt(cipher); //decryption
            System.out.print("\nback to text: ");
            for (int i = 0; i < backtext.length; i++) {          
                System.out.print(backtext[i]);            
            } 
            System.out.println();
        }
    }
    

Here is the result: (original and back to text are not SAME) why???

original text: 12345
cipher: 1483188254174
back to text: 391501310217

Solution

  • There are a few things to notice:

    To make life easier, and to have some fun late night hacking, I improved your code and tested it against a single vector in rfc6229 using a zero'd out byte array.

    UPDATE: As micahk points out below, the evil C XOR swap that was used prevented this code from encrypting the final byte of input in Java. Using regular old swaps fixes it.

    Warning: the code below should be considered a coding exercise. Please use a well vetted library instead of the code snippet below to perform RC4 (or Ron's Code 4, ARC4 etc.) in your application. That means using Cipher.getInstance("RC4"); or the ARC4 classes in Bouncy Castle.

    public class RC4 {
        private final byte[] S = new byte[256];
        private final byte[] T = new byte[256];
        private final int keylen;
    
        public RC4(final byte[] key) {
            if (key.length < 1 || key.length > 256) {
                throw new IllegalArgumentException(
                        "key must be between 1 and 256 bytes");
            } else {
                keylen = key.length;
                for (int i = 0; i < 256; i++) {
                    S[i] = (byte) i;
                    T[i] = key[i % keylen];
                }
                int j = 0;
                byte tmp;
                for (int i = 0; i < 256; i++) {
                    j = (j + S[i] + T[i]) & 0xFF;
                    tmp = S[j];
                    S[j] = S[i];
                    S[i] = tmp;
                }
            }
        }
    
        public byte[] encrypt(final byte[] plaintext) {
            final byte[] ciphertext = new byte[plaintext.length];
            int i = 0, j = 0, k, t;
            byte tmp;
            for (int counter = 0; counter < plaintext.length; counter++) {
                i = (i + 1) & 0xFF;
                j = (j + S[i]) & 0xFF;
                tmp = S[j];
                S[j] = S[i];
                S[i] = tmp;
                t = (S[i] + S[j]) & 0xFF;
                k = S[t];
                ciphertext[counter] = (byte) (plaintext[counter] ^ k);
            }
            return ciphertext;
        }
    
        public byte[] decrypt(final byte[] ciphertext) {
            return encrypt(ciphertext);
        }
    }
    

    Happy coding.