RC4加密java

您好我在尝试用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  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(); } } 

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

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

有几点需要注意:

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

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

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

警告 :以下代码应被视为编码练习。 请使用经过严格审查的库而不是下面的代码片段在您的应用程序中执行RC4(或Ron的Code 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); } } 

快乐的编码。

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

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

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

由于使用了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); } } 

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

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

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

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

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方法转换为bytes数组。

需要import:

 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