Java RC4 Encryption

Hi, I am trying to implement the RC4 algorithm in Java. I found this code as an example that will help me 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 questions:

  • Why does the above code use an a int plaintext array?

  • When I check this code, I get a strange result, can someone explain to me? Here is my code to check:

     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: (the original and the text back are NOT ONLY) why ???

 original text: 12345 cipher: 1483188254174 back to text: 391501310217 
+4
source share
5 answers

There are a few comments:

  • Java is not very easy to use when you need unsigned bytes (for example, for indexing);
  • If you create a state in S and T , you should really notice that these values ​​change when you decrypt with the same instance, that you take the state used for encryption;
  • the above code is not very memory efficient, and you can easily rewrite it to receive byte arrays;
  • to use String after refactoring the byte[] arguments, you first need to use first, for example using String.getBytes(Charset charset) ;

To make life easier and have fun, you improved your code and tested it against a single vector in rfc6229 using a zero byte array.

UPDATE: As Miyak points out, the evil C XOR replacement that was used did not allow this code to encrypt the final input byte in Java. Using regular old swaps corrects it.

Warning : the code below should be considered a coding exercise. Please use a well-tested library instead of the code snippet below to execute RC4 (or Ron Code 4, ARC4, etc.) in your application. This means using Cipher.getInstance("RC4"); or 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.

+9
source

Entire arrays of S and T were not created. Therefore, you get a NullPointerException as soon as you try to use them.

Looking at the rest of the code, I assume that they were supposed to be arrays of 256 elements:

 private int[] S = new int[256]; private int[] T = new int[256]; 
+3
source

There is an error in the Java code due to the use of xor-swap technology:

  S[i] ^= S[j]; S[j] ^= S[i]; S[i] ^= S[j]; 

Instead, you will want to use a temporary variable as shown below. I did not delve into why the result was not so expected with the replacement xor, but I had decryption errors with this, which were solved by a simple direct exchange. I suspect this is a subtle effect of an implicit translation from byte to int, which happens to perform the xor operation.

 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; for (int i = 0; i < 256; i++) { j = (j + S[i] + T[i]) & 0xFF; byte temp = S[i]; S[i] = S[j]; S[j] = temp; } } } public byte[] encrypt(final byte[] plaintext) { final byte[] ciphertext = new byte[plaintext.length]; int i = 0, j = 0, k, t; for (int counter = 0; counter < plaintext.length; counter++) { i = (i + 1) & 0xFF; j = (j + S[i]) & 0xFF; byte temp = S[i]; S[i] = S[j]; S[j] = temp; 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); } } 
+3
source

1) int array: perhaps because Java does not support unsigned bytes.

2) Null exception: I calculated that line 12 is as follows: S[i] = i; It appears that the S array is not created before it is used.

+2
source

(I know this is an old thread, but maybe my answer might help someone who reads it)

The problem is not RC4 code, but how you use it. What you need to understand, every time the encript method is called, the S array is changed to generate a pseudo-random key.

In this code, you are using the decript method after encript on the same instance of the RC4 class. But the RC4 class has a key creation in the constructor, so when you execute the decript method, the key is not created recently, because it was changed by the previous encript. Instead of this code:

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

Use a new rc4 instance before decript:

 int[] cipher = rc4.encrypt(text); //encryption System.out.print("\ncipher: "); for (int i = 0; i < cipher.length; i++) { System.out.print(cipher[i]); } rc4 = new RC4(keytest); 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]); } 

Thus, the decript method will have a pure S array, and it will be able to get the S-sequence in the same order as the previous encript method.

+1
source

Source: https://habr.com/ru/post/1432579/


All Articles