RC4加密java

时间:2012-09-05 21:01:29

标签: java encryption rc4-cipher

您好,我正在尝试用Java实现RC4算法。我发现这个code是一个帮助我理解这个想法的例子:

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

我几乎没有问题:

  1. 为什么上述代码中的纯文本为int数组?

  2. 当我测试这段代码时,我得到了奇怪的结果,有人可以向我解释一下吗?这是我的代码测试:

    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();
        }
    }
    
  3. 结果如下:(原文和回文不是SAME)为什么???

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

6 个答案:

答案 0 :(得分:9)

有几点需要注意:

  • 当您需要无符号字节(例如索引)时,Java不是很容易使用;
  • 如果您在ST中创建状态,您应该注意到这些值会发生变化,当您使用相同的实例解密 时,您会使用以下状态加密;
  • 上面的代码在内存方面效率不高,你可以轻松地重写它以取字节数组;
  • 使用字符串,在重构byte[]的参数后,首先需要先使用,例如使用String.getBytes(Charset charset);

为了让生活更轻松,并有一些有趣的深夜黑客攻击,我改​​进了你的代码并使用零字节数组在rfc6229中针对单个向量进行了测试。

更新:正如micahk指出的那样,使用的邪恶C XOR交换阻止了此代码加密Java中输入的最后一个字节。使用常规旧交换修复它。

警告:以下代码应视为编码练习。请使用经过严格审查的库而不是下面的代码片段在您的应用程序中执行RC4(或Ron的Code 4,ARC4等)。这意味着在Bouncy Castle中使用Cipher.getInstance("RC4");或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);
    }
}

快乐的编码。

答案 1 :(得分:3)

尚未构建整数数组ST。因此,只要您尝试使用它们,就会得到NullPointerException

查看剩下的代码,我猜他们应该是256项数组:

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

答案 2 :(得分:3)

由于使用了xor-swap技术,Java代码存在错误:

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

而不是这个,你将需要使用如下所示的临时变量。我没有深入研究为什么结果不像xor swap那样预期,但是我有解密错误,只需做一个直接的交换即可解决。我怀疑这是为了执行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);
    }
}

答案 3 :(得分:2)

1)int数组:可能是因为Java不支持无符号字节。

2)空例外:​​我认为第12行是这一行:S[i] = i;看起来S数组在使用之前没有被构造。

答案 4 :(得分:1)

(我知道这是一个老话题,但也许我的答案可以帮助谁正在阅读它)

问题不在RC4代码中,而在于您如何使用它。 您必须了解的是每次调用encript方法时,都会修改S数组以生成伪随机密钥。

在此代码中,您在使用相同的RC4类实例之后使用decript方法。但是RC4类在构造函数中有密钥创建,所以当你执行decript方法时,最近没有创建密钥,因为它已被前一个encript修改过。而不是这段代码:

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

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

因此,decript方法将具有一个干净的S数组,并且它能够以与前一个encript方法相同的顺序获得S序列。

答案 5 :(得分:1)

RC4是一个坏算法,如果要高度保护数据,建议不要再使用它。

如果仍然需要有效的实现,则无需在代码中重新创建算法。 Java API javax.crypto可以为您完成此任务。只需生成一个密钥,然后将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);
}

输出:

enter image description here

如果您需要将加密的数据作为url的一部分发送,请使用Base64Encoding然后发送。

例如

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

提示:如上所示,使用Hex.decode从base64解码的字符串中获取字节,否则会出现编码问题。尽可能使用十六进制进行转换,并使用Bouncycastle方法转换为字节数组。

需要进口:

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;

此外,如果您是根据自己的字符串生成密钥的,则可以使用MD5Hashing进行加密。

有关如何使用自定义字符串创建密钥的信息,请参考此内容: https://stackoverflow.com/a/52463858/5912424