CSharp中的密码代码类似于Go(AES,CFB,XorKeyStream)中的代码

I have cryptographic code in Go but I can't hard find similar code in CSharp. I am debating to make my own implementation of XorKeyStream but I am told that there is legal issue if I write my own cryptographic code. I am sure there must be similar code in CSharp.

package main

import (
    "crypto/aes"
    "crypto/cipher"
    "fmt"
)

func main() {

    k1 := []byte("0123456789abcdef")
    r1 := []byte("1234567890abcdef")
    data := []byte("0123456789")
    fmt.Printf("original %x %s
", data, string(data))

    {
        block, _ := aes.NewCipher(k1)
        stream := cipher.NewCFBEncrypter(block, r1)
        stream.XORKeyStream(data, data)
        fmt.Printf("crypted %x
", data)
    }
    {
        block, _ := aes.NewCipher(k1)
        stream := cipher.NewCFBDecrypter(block, r1)
        stream.XORKeyStream(data, data)
        fmt.Printf("decrypted %x %s
", data, string(data))
    }

}

http://play.golang.org/p/EnJ56dYX_-

output

original 30313233343536373839 0123456789
crypted 762b6dcea9c2a7460db7
decrypted 30313233343536373839 0123456789

PS Some people marked that question as possible duplicate of question: "C# AES: Encrypt a file causes “Length of the data to encrypt is invalid.” error" I look for identical code in CSharp for existing code in Go. That question is about padding. This algorithm needs "Key stream" that will xor text. It is different questions.

Here is your code

using System;
using System.Text;
using System.Security.Cryptography;
using System.IO;

class AES_CFB_XorKeyStream
{
    static void Main(string[] args)
    {
        byte[] data = Encoding.UTF8.GetBytes("0123456789");
        byte [] k1 = Encoding.UTF8.GetBytes("0123456789abcdef");
        byte [] r1 = Encoding.UTF8.GetBytes("1234567890abcdef");

        Console.WriteLine("original " + BitConverter.ToString(data));

        using (RijndaelManaged Aes128 = new RijndaelManaged())
        {
            Aes128.BlockSize = 128;
            Aes128.KeySize = 128;
            Aes128.Mode = CipherMode.CFB;
            Aes128.FeedbackSize = 128;
            Aes128.Padding = PaddingMode.None;
            Aes128.Key = k1;
            Aes128.IV = r1;

            using (var encryptor = Aes128.CreateEncryptor())
            using (var msEncrypt = new MemoryStream())
            using (var csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
            using (var bw = new BinaryWriter(csEncrypt, Encoding.UTF8))
            {
                bw.Write(data);
                bw.Close();

                data = msEncrypt.ToArray();
                Console.WriteLine("crypted " + BitConverter.ToString(data));
            }
        }

        using (RijndaelManaged Aes128 = new RijndaelManaged())
        {
            Aes128.BlockSize = 128;
            Aes128.KeySize = 128;
            Aes128.Mode = CipherMode.CFB;
            Aes128.FeedbackSize = 128;
            Aes128.Padding = PaddingMode.None;

            Aes128.Key = k1;
            Aes128.IV = r1;

            using (var decryptor = Aes128.CreateDecryptor())
            using (var msEncrypt = new MemoryStream())
            using (var csEncrypt = new CryptoStream(msEncrypt, decryptor, CryptoStreamMode.Write))
            using (var bw = new BinaryWriter(csEncrypt, Encoding.UTF8))
            {
                bw.Write(data);
                bw.Close();

                data = msEncrypt.ToArray();
                Console.WriteLine("decrypted " + BitConverter.ToString(data));
            }
        }
    }
}

output

original 30-31-32-33-34-35-36-37-38-39
crypted 76-2B-6D-CE-A9-C2-A7-46-0D-B7
decrypted 30-31-32-33-34-35-36-37-38-39

I had this exact same issue with only the first byte of each decrypted block being correct, but I did not have the luxury of being able to change source on the Go program.

I ended up implementing my own padding. Just pad the encrypted bytes with 0 bytes to make it divisible by the block size of 128, then after running through the decryption routine, chop that number of bytes off the end.

Example code:

using System;
using System.Text;
using System.Security.Cryptography;
using System.Linq;

public static class Program
{
    static RijndaelManaged aes = new RijndaelManaged(){
        Mode = CipherMode.CFB,
        BlockSize = 128,
        KeySize = 128,
        FeedbackSize = 128,
        Padding = PaddingMode.None
    };

    public static void Main(){
        byte[] key = Encoding.UTF8.GetBytes("0123456789abcdef");
        byte[] iv = Encoding.UTF8.GetBytes("1234567890abcdef");
        byte[] encryptedBytes = new byte[]{0x76, 0x2b, 0x6d, 0xce, 0xa9, 0xc2, 0xa7, 0x46, 0x0d, 0xb7};

        // Custom pad the bytes
        int padded;
        encryptedBytes = PadBytes(encryptedBytes, aes.BlockSize, out padded);

        // Decrypt bytes
        byte[] decryptedBytes = DecryptBytesAES(encryptedBytes, key, iv, encryptedBytes.Length);

        // Check for successful decrypt
        if(decryptedBytes != null){
            // Unpad
            decryptedBytes = UnpadBytes(decryptedBytes, padded);
            Console.Write("Decrypted: " + Encoding.UTF8.GetString(decryptedBytes));
        }

    }

    // Just an elegant way of initializing an array with bytes
    public static byte[] Initialize(this byte[] array, byte value, int length)
    {
        for (int i = 0; i < array.Length; i++)
        {
            array[i] = value;
        }
        return array;
    }

    // Custom padding to get around the issue of how Go uses CFB mode without padding differently than C#
    public static byte[] PadBytes(byte[] encryptedBytes, int blockSize, out int numPadded)
    {
        numPadded = 0;

        // Check modulus of block size
        int mod = encryptedBytes.Length % blockSize;
        if (mod != 0)
        {
            // Calculate number to pad
            numPadded = blockSize - mod;

            // Build array
            return encryptedBytes.Concat(new byte[numPadded].Initialize(0, numPadded)).ToArray();
        }
        else {
            // No padding needed
            return encryptedBytes;
        }
    }
    public static byte[] UnpadBytes(byte[] decryptedBytes, int numPadded)
    {
        if(numPadded != 0)
        {
            byte[] unpaddedBytes = new byte[decryptedBytes.Length - numPadded];
            Array.Copy(decryptedBytes, unpaddedBytes, unpaddedBytes.Length);
            return unpaddedBytes;
        }
        else
        {
            return decryptedBytes;
        }
    }

    public static byte[] DecryptBytesAES(byte[] cipherText, byte[] Key, byte[] IV, int size)
    {
        byte[] array = new byte[size];
        try{
            aes.Key = Key;
            aes.IV = IV;
            ICryptoTransform transform = aes.CreateDecryptor(aes.Key, aes.IV);
            using (System.IO.MemoryStream memoryStream = new System.IO.MemoryStream(cipherText))
            {
                using (CryptoStream cryptoStream = new CryptoStream(memoryStream, transform, CryptoStreamMode.Read))
                {
                    cryptoStream.Read(array, 0, size);
                }
            }
        }
        catch(Exception e){
            return null;
        }
        return array;
    }
}

.NET Fiddle: https://dotnetfiddle.net/NPHKN3