2012-09-05 84 views
4

嗨,我正试图在Java中实现RC4算法。我发现这个code为帮助我理解这个想法的例子:RC4加密java

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 

回答

9

有几件事情需要注意:

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

为了使生活更轻松,并有一些乐趣深夜黑客,我提高了你的代码,并使用zero'd出字节数组测试它针对单个向量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); 
    } 
} 

快乐编码。

+0

你好!非常感谢您帮助我,但是您能否提供一段简单的代码,用于测试您的上述代码。我想要以二进制模式从文件(密钥文件和纯文本文件)中读取的“密钥”和“纯文本”(是否意味着将文件转换为字节?)。但现在我只需要一个示例代码来测试这个类,你能帮助我吗? –

+0

请帮助我如何使用您的代码,我测试了它,但给我很奇怪的结果.... –

+0

我测试了代码,创建了2个分开的RC4对象(用于加密和解密)。解密后的结果与输入相同纯文本=>代码也许很好,但我无法显示密文(在将字节转换为字符串后给了我奇怪的字符),我的意思是如何验证密文是正确(与其他语言的C#或C++实现相比),如何显示密文? –

3

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

看代码的其余部分,我想他们应该是256项数组:

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

是的我修正了上面的代码,它现在可以工作,但为什么纯文本必须是整数数组,我该如何使用字符串呢? –

2

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

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

+0

你是什么意思的“无符号字节”?我的意思是文本应该是任何东西(至少它应该是字符串)为什么作者将它作为一个int数组来实现,我只是不明白这一点? –

+0

我只是猜测,因为我没有写代码。为了加密,你将把字符串视为一个字节序列,并对这些字节进行数学运算。无论出于何种历史原因,Java都缺乏无符号的字节类型,并且通常人们使用整数来对Java字节执行数学运算。所提供的代码似乎不完整。 – theglauber

+0

我编辑了我的问题,我测试了代码,但加密和解密给了我不同的结果,你知道为什么吗? –

3

Java的代码有缺陷,由于使用XOR交换技术:

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

取而代之的是,你需要使用一个临时变量如下面。我还没有深入研究为什么xor swap的结果不如预期,但是我的解密错误只通过简单的交换来解决。我怀疑这是隐式强制转换从字节到int的细微副作用,这是为了执行xor操作而发生的。

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

(我知道这是一个古老的线程,但也许我的回答能帮助谁正在读它)

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

在这段代码中,你使用了脚本之后的脚本方法,通过RC4类的同一个实例。但是RC4类在构造函数中有关键的创建,所以当你执行decript方法时,关键字最近没有被创建,因为它已经被前面的脚本修改过了。取而代之的是代码:

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方法将有一个干净■数组,这将是能够在获得小号序列与前面的引文方法相同的顺序。