简体   繁体   English

RC4加密Java

[英]RC4 encryption java

Hi there I am trying to implement the RC4 algorithm in Java. 嗨,我正在尝试用Java实现RC4算法。 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? 为什么上述代码中的纯文本为int数组?

  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

There are a few things to notice: 有几件事要注意:

  • Java is not very easy to use when you require unsigned bytes (eg for indexing); 当您需要无符号字节(例如用于索引)时,Java并不是很容易使用。
  • if you create a state in S and T , you should really notice that these values change, when you decrypt with the same instance you take the state used for encryption; 如果您在ST创建一个状态,您应该真正注意到这些值会发生变化,当您使用相同的实例解密时您会采用用于加密的状态;
  • the above code is not very efficient memory wise, and you can easily rewrite it to take byte arrays; 上面的代码在内存方面不是很有效,您可以轻松地重写它以获取字节数组;
  • to use a String, after refactoring the arguments to byte[] , you first need to use first, eg using String.getBytes(Charset charset) ; 要使用字符串,将参数重构为byte[] ,首先需要首先使用 ,例如,使用String.getBytes(Charset charset)

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. 为了使生活更轻松,并有一些有趣的深夜黑客,我改进了您的代码,并使用零进位字节数组对rfc6229中的单个向量进行了测试。

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. 更新:正如micahk在下面指出的那样,使用了邪恶的C XOR交换,阻止了此代码对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. 请使用经过严格审查的库而不是下面的代码片段,以在您的应用程序中执行RC4(或Ron的代码4,ARC4等)。 That means using Cipher.getInstance("RC4"); 这意味着使用Cipher.getInstance("RC4"); or the ARC4 classes in Bouncy Castle. 或Bouncy Castle中的ARC4类。

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. 快乐的编码。

Your integer arrays S and T have not been constructed. 您的整数数组ST尚未构造。 Hence you get a NullPointerException as soon as you attempt to use them. 因此,当您尝试使用它们时,将立即获得NullPointerException

Looking at the rest of the code, I guess they should have been 256-item arrays: 查看其余的代码,我想它们应该是256个项目的数组:

private int[] S = new int[256];
private int[] T = new int[256];

The Java code has a bug due to the use of the xor-swap technique: 由于使用了xor交换技术,因此Java代码存在一个错误:

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

Instead of this, you'll want to use a temp variable as in the below. 取而代之的是,您将需要使用一个temp变量,如下所示。 I haven't delved into why the result isn't as expected with the xor swap, but I had decryption errors with this that were resolved by simply doing a straight-forward swap. 我还没有深入研究为什么xor交换的结果不如预期,但是我遇到了解密错误,只需执行直接交换即可解决。 I suspect it to be a subtle-side effect of the implicit cast from byte to int that occurs in order to do the xor operation. 我怀疑这是为了执行xor操作而发生的从字节到int的隐式强制转换的细微副作用。

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

1) int array: probably because Java doesn't support unsigned bytes. 1)int array:可能是因为Java不支持无符号字节。

2) Null exception: I counted line 12 being this one: S[i] = i; 2)空异常:我算出第12行是: S[i] = i; It looks like the S array is not being constructed before it's used. 看起来S数组在使用之前尚未构建。

(I know this is a old thread, but maybe my answer can help who is reading it) (我知道这是一个旧主题,但是也许我的回答可以帮助正在阅读的人)

The problem is not in the RC4 code but in how you are using it. 问题不在于RC4代码,而在于您如何使用它。 What you have to understand is every time that encript method is invoked, the S array is modified to generate a pseudo random key. 您需要了解的是,每次调用encript方法时,都会修改S数组以生成伪随机密钥。

In this code your are using the decript method after encript over the same instance of RC4 class. 在这段代码中,您在对RC4类的相同实例进行加密之后使用了decript方法。 But RC4 class have the key creation in the constructor, so when you execute decript method, the key is not recently created as it has been modified by the previous encript. 但是RC4类在构造函数中具有密钥创建功能,因此,当您执行decript方法时,该密钥不是最近创建的,因为它已被先前的加密方法修改。 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 rc4 new instance before decript: 抄写之前使用rc4新实例:

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

So the decript method will be have a clean S array, and it will be able to obtain S sequence in the same order than the previous encript method. 因此,解密方法将具有干净的S数组,并且它将能够以与先前的加密方法相同的顺序获得S序列。

RC4 is a broken algorithm and recommendation is to not use the same anymore if the data is to be kept highly secure. RC4是一种坏算法,如果要高度保护数据,建议不要再使用它。

If you still need a working implementation, you don't need to recreate the algorithm in your code. 如果仍然需要有效的实现,则无需在代码中重新创建算法。 Java API javax.crypto can do it for you. Java API javax.crypto可以为您完成此任务。 Just generate a key and call the init method with mode set to encryption/decryption. 只需生成一个密钥,然后将init方法设置为加密/解密模式即可。

static String decryptRC4() throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException, InvalidAlgorithmParameterException{

    byte[] testDataBytes = "testString".getBytes();

    KeyGenerator rc4KeyGenerator = KeyGenerator.getInstance("RC4");
    SecretKey key = rc4KeyGenerator.generateKey();

    // Create Cipher instance and initialize it to encrytion mode
    Cipher cipher = Cipher.getInstance("RC4");  // Transformation of the algorithm
    cipher.init(Cipher.ENCRYPT_MODE, key);
    byte[] cipherBytes = cipher.doFinal(testDataBytes);

    // Reinitialize the Cipher to decryption mode
    cipher.init(Cipher.DECRYPT_MODE,key, cipher.getParameters());
    byte[] testDataBytesDecrypted = cipher.doFinal(cipherBytes);

    System.out.println("Decrypted Data : "+new String(testDataBytesDecrypted));
    return new String(testDataBytesDecrypted);
}

Output: 输出:

在此处输入图片说明

If you need to send the encrypted data as part of a url then use Base64Encoding and then send. 如果您需要将加密的数据作为url的一部分发送,请使用Base64Encoding然后发送。

eg 例如

    static String decryptRC4() throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException, InvalidAlgorithmParameterException{

    byte[] plainBytes = "testString".getBytes();

    KeyGenerator rc4KeyGenerator = KeyGenerator.getInstance("RC4");
    SecretKey key = rc4KeyGenerator.generateKey();

    // Create Cipher instance and initialize it to encrytion mode
    Cipher cipher = Cipher.getInstance("RC4");  // Transformation of the algorithm
    cipher.init(Cipher.ENCRYPT_MODE, key);
    byte[] cipherBytes = cipher.doFinal(plainBytes);

    String encoded = encodeBase64(cipherBytes);

    String decoded = decodeBase64(encoded);

    // Reinitialize the Cipher to decryption mode
    cipher.init(Cipher.DECRYPT_MODE,key, cipher.getParameters());
    byte[] plainBytesDecrypted = cipher.doFinal(Hex.decode(decoded));

    System.out.println("Decrypted Data : "+new String(plainBytesDecrypted));
    return new String(plainBytesDecrypted);
}

static String decodeBase64(String encodedData){
    byte[] b = Base64.getDecoder().decode(encodedData);
    String decodedData = DatatypeConverter.printHexBinary(b);
    return decodedData;
}

static String encodeBase64(byte[] data){
    byte[] b = Base64.getEncoder().encode(data);
    String encodedData = new String(b);
    /*String encodedData = DatatypeConverter.printHexBinary(b);*/
    return encodedData;
}

Tip: Use Hex.decode as shown above to get bytes from the base64 decoded string or else you will get encoding issues. 提示:如上所示,使用Hex.decode从base64解码的字符串中获取字节,否则会出现编码问题。 As much as possible do the conversions using Hex and convert to bytes array using bouncycastle methods. 尽可能使用十六进制进行转换,并使用bouncycastle方法转换为字节数组。

Imports needed: 需要进口:

import java.io.IOException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.KeyGenerator;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.xml.bind.DatatypeConverter;

import org.apache.commons.codec.DecoderException;
import org.bouncycastle.util.encoders.Hex;

Also if you are generating a key from your own string you can use MD5Hashing for the same. 同样,如果您是根据自己的字符串生成密钥的,则可以使用MD5Hashing。

Please refer this for help on how to create a key using custom String: https://stackoverflow.com/a/52463858/5912424 请参考此以获取有关如何使用自定义字符串创建密钥的帮助: https : //stackoverflow.com/a/52463858/5912424

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM