6

您好,我正在尝试在 Java 中实现 RC4 算法。我发现这段代码作为一个示例,可以帮助我理解这个想法:

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

结果如下:(原文和回文不一样)为什么???

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

6 回答 6

10

有几点需要注意:

  • 当您需要无符号字节(例如用于索引)时,Java 不是很容易使用;
  • 如果您在Sand中创建一个状态T,您应该真正注意到这些值发生了变化,当您使用相同的实例进行解密时,您会采用用于加密的状态;
  • 上面的代码在内存方面不是很有效,你可以很容易地重写它来获取字节数组;
  • 要使用字符串,在将参数重构为 之后byte[],您首先需要先使用,例如使用String.getBytes(Charset charset);

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

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

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

快乐编码。

于 2012-09-05T22:53:48.707 回答
3

你的整数数组ST没有被构造。因此NullPointerException,一旦您尝试使用它们,您就会得到一个。

查看其余代码,我猜它们应该是 256 项数组:

private int[] S = new int[256];
private int[] T = new int[256];
于 2012-09-05T21:14:12.923 回答
3

由于使用了 xor-swap 技术,Java 代码有一个错误:

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

取而代之的是,您需要使用如下所示的临时变量。我没有深入研究为什么 xor 交换的结果不像预期的那样,但是我遇到了解密错误,通过简单地进行直接交换就可以解决。我怀疑这是为了执行 xor 操作而发生的从 byte 到 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);
    }
}
于 2013-11-27T02:29:44.637 回答
2

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

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

于 2012-09-05T21:18:56.403 回答
2

(我知道这是一个旧线程,但也许我的回答可以帮助谁正在阅读它)

问题不在于 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 序列。

于 2015-04-19T19:03:40.537 回答
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);
}

输出:

在此处输入图像描述

如果您需要将加密数据作为 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 解码字符串中获取字节,否则您将遇到编码问题。尽可能使用 Hex 进行转换,并使用 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

于 2018-09-23T06:52:52.623 回答