C#实现RC4加解密

更新于 2023-12-20

纯C#实现的RC4加解密,使用流模式。
由于使用流模式,加密和解密不能在同一个Cipher上进行。
可以使用CreateCipher或Reset方法快捷创建新的Cipher。

测试代码

byte[] clearTextBytes = Encoding.UTF8.GetBytes("hello world!");
byte[] password = new byte[128];
new Random().NextBytes(password);
RC4BlockCipher encryptor = new RC4BlockCipher(password, RC4CipherMode.Encrypt);
Console.WriteLine("ClearText Bytes: {0}", string.Join(",", clearTextBytes));

encryptor.Encrypt(clearTextBytes, 0, clearTextBytes.Length);
Console.WriteLine("Encrypted Bytes: {0}", string.Join(",", clearTextBytes));

RC4BlockCipher decryptor = new RC4BlockCipher(password, RC4CipherMode.Decrypt);
//或者使用CreateCipher创建新的解密Cipher
//RC4BlockCipher decryptor = encryptor.CreateCipher(RC4CipherMode.Decrypt);
decryptor.Decrypt(clearTextBytes, 0, clearTextBytes.Length);

Console.WriteLine("Decrypted Bytes: {0}", string.Join(",", clearTextBytes));
Console.WriteLine("Clear Text: {0}", Encoding.UTF8.GetString(clearTextBytes));

输出

根据随机秘钥不同,密文会有差异。

ClearText Bytes: 104,101,108,108,111,32,119,111,114,108,100,33
Encrypted Bytes: 27,254,98,227,96,143,162,87,250,242,89,88
Decrypted Bytes: 104,101,108,108,111,32,119,111,114,108,100,33
Clear Text: hello world!

源代码

public enum RC4CipherMode
{
    /// <summary>
    /// 加密
    /// </summary>
    Encrypt,

    /// <summary>
    /// 解密
    /// </summary>
    Decrypt
}
public class RC4BlockCipher : IDisposable
{
    private byte[] _ciphers = null;
    private byte[] _ciphers_copy = null;
    private RC4CipherMode _mode;
    private int _offsetX = 0;
    private int _offsetY = 0;

    public void Dispose()
    {
        _ciphers = null;
        _ciphers_copy = null;
    }

    /// <summary>
    /// 使用密码和指定加解密模式创建新的Cipher
    /// </summary>
    /// <param name="password">密码,最大有效长度256字节。</param>
    /// <param name="mode">加解密模式</param>
    public RC4BlockCipher(byte[] password, RC4CipherMode mode)
    {
        _mode = mode;
        _ciphers = InitCiphers(password ?? throw new ArgumentNullException(nameof(password)));
        _ciphers_copy = new byte[256];
        _ciphers.CopyTo(_ciphers_copy, 0);
    }

    private RC4BlockCipher(RC4CipherMode mode)
    {
        _mode = mode;
    }

    /// <summary>
    /// 生成加解密秘钥
    /// </summary>
    /// <param name="password"></param>
    /// <returns></returns>
    private byte[] InitCiphers(byte[] password)
    {
        byte j = 0;
        int len = password.Length;
        byte[] k = new byte[256];
        byte[] s = new byte[256];
        int i;
        for (i = 0; i < 256; i++)
        {
            s[i] = (byte)i;
            k[i] = password[i % len];
        }
        for (i = 0; i < 256; i++)
        {
            j = (byte)((j + s[i] + k[i]) & 0xff);
            byte tmp = s[i];
            s[i] = s[j];
            s[j] = tmp;
        }
        return s;
    }

    /// <summary>
    /// 使用当前Cipher的秘钥创建新的Cipher
    /// </summary>
    /// <param name="mode">新的Cipher加解密模式</param>
    /// <returns></returns>
    public RC4BlockCipher CreateCipher(RC4CipherMode mode)
    {
        var cipher = new RC4BlockCipher(mode);
        cipher._ciphers = new byte[256];
        cipher._ciphers_copy = new byte[256];

        _ciphers_copy.CopyTo(cipher._ciphers, 0);
        _ciphers_copy.CopyTo(cipher._ciphers_copy, 0);

        return cipher;
    }

    /// <summary>
    /// 重置当前Cipher
    /// </summary>
    public void Reset()
    {
        _ciphers_copy.CopyTo(_ciphers, 0);
    }

    /// <summary>
    /// 使用新的加解密模式重置当前Cipher
    /// </summary>
    public void Reset(RC4CipherMode mode)
    {
        _ciphers_copy.CopyTo(_ciphers, 0);
        _mode = mode;
    }

    /// <summary>
    /// 加密数据
    /// </summary>
    /// <param name="buffer">待加密数据,加密完成后,原数据会被覆写</param>
    /// <param name="offset">数据偏移</param>
    /// <param name="count">加密长度</param>
    /// <returns></returns>
    public virtual byte[] Encrypt(byte[] buffer, int offset, int count)
    {
        if (_mode != RC4CipherMode.Encrypt) throw new InvalidOperationException();
        ProcessBlock(buffer, offset, count);
        return buffer;
    }

    /// <summary>
    /// 解密数据
    /// </summary>
    /// <param name="buffer">待解密数据,解密完成后,原数据会被覆写</param>
    /// <param name="offset">数据偏移</param>
    /// <param name="count">解密长度</param>
    /// <returns></returns>
    public virtual byte[] Decrypt(byte[] buffer, int offset, int count)
    {
        if (_mode != RC4CipherMode.Decrypt) throw new InvalidOperationException();
        ProcessBlock(buffer, offset, count);
        return buffer;
    }

    private void ProcessBlock(byte[] buffer, int offset, int count)
    {
        byte m;
        int end = offset + count;
        byte[] s = _ciphers;

        for (int k = offset; k < end; k++)
        {
            int x = (_offsetX + 1) & 0xff;
            int y = (_offsetY + s[x]) & 0xff;

            m = s[x]; s[x] = s[y]; s[y] = m;

            buffer[k] ^= s[(s[x] + s[y]) & 0xff];

            _offsetX = x;
            _offsetY = y;
        }
    }
}

RC4的Stream实现

实现在Stream上的加解密,建议同时实现Begin/End的系列方法。

public enum RC4StreamMode
{
    Read, Write
}

public class RC4Stream : Stream
{
    private byte[] _ciphers = null;
    private Stream _innerStream;
    private readonly RC4StreamMode _mode;
    private int _offsetX = 0;
    private int _offsetY = 0;

    protected override void Dispose(bool disposing)
    {
        _ciphers = null;
        _innerStream = null;
        base.Dispose(disposing);
    }

    public override bool CanRead => _mode == RC4StreamMode.Read;

    public override bool CanSeek => false;

    public override bool CanWrite => _mode == RC4StreamMode.Write;

    public override long Length => _innerStream.Length;

    public override long Position { get => _innerStream.Position; set => throw new NotSupportedException(); }

    public RC4Stream(Stream innerStream, byte[] password, RC4StreamMode mode)
    {
        _innerStream = innerStream ?? throw new ArgumentNullException(nameof(innerStream));
        _mode = mode;
        _ciphers = InitCiphers(password ?? throw new ArgumentNullException(nameof(password)));
    }
    public RC4Stream(Stream innerStream, string password, RC4StreamMode mode)
        : this(innerStream, Convert.FromBase64String(password), mode)
    {
    }

    private byte[] InitCiphers(byte[] password)
    {
        byte j = 0;
        int len = password.Length;
        byte[] k = new byte[256];
        byte[] s = new byte[256];
        int i;
        for (i = 0; i < 256; i++)
        {
            s[i] = (byte)i;
            k[i] = password[i % len];
        }
        for (i = 0; i < 256; i++)
        {
            j = (byte)((j + s[i] + k[i]) & 0xff);
            byte tmp = s[i];
            s[i] = s[j];
            s[j] = tmp;
        }
        return s;
    }

    private void ProcessBlock(byte[] buffer, int offset, int count)
    {
        byte m;
        int end = offset + count;
        byte[] s = _ciphers;

        for (int k = offset; k < end; k++)
        {
            int x = (_offsetX + 1) & 0xff;
            int y = (_offsetY + s[x]) & 0xff;

            m = s[x]; s[x] = s[y]; s[y] = m;

            buffer[k] ^= s[(s[x] + s[y]) & 0xff];

            _offsetX = x;
            _offsetY = y;
        }
    }
    public override void Flush() => throw new NotImplementedException();

    public override long Seek(long offset, SeekOrigin origin) => throw new NotImplementedException();

    public override void SetLength(long value) => throw new NotImplementedException();

    public override int Read(byte[] buffer, int offset, int count)
    {
        if (_mode != RC4StreamMode.Read) throw new InvalidOperationException();
        int rec = _innerStream.Read(buffer, offset, count);
        if (rec == 0) return 0;

        ProcessBlock(buffer, offset, rec);

        return rec;
    }
    public override void Write(byte[] buffer, int offset, int count)
    {
        if (_mode != RC4StreamMode.Write) throw new InvalidOperationException();
        if (count == 0)
        {
            _innerStream.Write(buffer, offset, count);
            return;
        }
        byte[] _buffer = new byte[count];

        Array.Copy(buffer, offset, _buffer, 0, count);

        ProcessBlock(_buffer, 0, count);

        _innerStream.Write(_buffer, 0, count);
    }
}