Java BC SicBlockCipher 直接输出等价于c#

Java BC SicBlockCipher direct output equivalent in c#

我正在用 C# 实现一些东西,对此我有一个单独的规范并且相当清楚地了解我需要做什么,但同时作为参考,我有一个 Java 实现并且想要在这种情况下尽可能遵循 Java 实施。

代码涉及加密流,Java 来源是 here 相关行在这里:

  private final StreamCipher enc;
...
  BlockCipher cipher;
  enc = new SICBlockCipher(cipher = new AESEngine());
  enc.init(true, new ParametersWithIV(new KeyParameter(secrets.aes), new byte[cipher.getBlockSize()]));
...
...
byte[] ptype = RLP.encodeInt((int) frame.type); //Result can be a single byte long
...
...
enc.processBytes(ptype, 0, ptype.length, buff, 0);
out.write(buff, 0, ptype.length); //encrypt and write a single byte from the SICBlockCipher stream

上面的 Java BouncyCastle SicBlockCipher 是一个 StreamCipher 并且允许处理小于 Aes 块大小的单个或少量字节。

在 c# BouncyCastle 中,SicBlockCipher 仅提供 ProcessBlock,而 BufferedBlockCipher 似乎不提供使用 ProcessBytes 保证输出的方法。

我需要用 C# BouncyCastle 库做什么才能实现等效功能?

不幸的是,SicBlockCipher 本身并未实现为流密码,因此此功能(确实)无法直接使用。

BufferedBlockCipher 的创建考虑了许多不同的操作模式。它缓冲 输入 ,而对于 SicBlockCipher 实现的计数器 (CTR) 模式,您需要缓冲加密的计数器块。

加密的计数器块构成密钥流,然后可以将其与明文进行异或运算以创建密文流(或者实际上,用密文再次检索明文,加密是计数器模式的解密)。

我看到的唯一方法是创建您自己的 IBlockCipher 实现并实现上述功能。


这是流密码的计数器模式...

using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Modes;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace SicStream
{
    public class SicStreamCipher : IStreamCipher
    {
        private SicBlockCipher parent;
        private int blockSize;

        private byte[] zeroBlock;

        private byte[] blockBuffer;
        private int processed;

        public SicStreamCipher(SicBlockCipher parent)
        {
            this.parent = parent;
            this.blockSize = parent.GetBlockSize();

            this.zeroBlock = new byte[blockSize];

            this.blockBuffer = new byte[blockSize];
            // indicates that no bytes are available: lazy generation of counter blocks (they may not be needed)
            this.processed = blockSize;
        }

        public string AlgorithmName
        {
            get
            {
                return parent.AlgorithmName;
            }
        }

        public void Init(bool forEncryption, ICipherParameters parameters)
        {
            parent.Init(forEncryption, parameters);

            Array.Clear(blockBuffer, 0, blockBuffer.Length);
            processed = blockSize;
        }

        public void ProcessBytes(byte[] input, int inOff, int length, byte[] output, int outOff)
        {
            int inputProcessed = 0;
            while (inputProcessed < length)
            {
                // NOTE can be optimized further
                // the number of available bytes can be pre-calculated; too much branching
                if (processed == blockSize)
                {
                    // lazilly create a new block of key stream
                    parent.ProcessBlock(zeroBlock, 0, blockBuffer, 0);
                    processed = 0;
                }

                output[outOff + inputProcessed] = (byte)(input[inOff + inputProcessed] ^ blockBuffer[processed]);

                processed++;
                inputProcessed++;
            }
        }

        public void Reset()
        {
            parent.Reset();

            Array.Clear(blockBuffer, 0, blockBuffer.Length);
            this.processed = blockSize;
        }

        public byte ReturnByte(byte input)
        {
            if (processed == blockSize)
            {
                // lazily create a new block of key stream
                parent.ProcessBlock(zeroBlock, 0, blockBuffer, 0);
                processed = 0;
            }
            return (byte)(input ^ blockBuffer[processed++]);
        }
    }
}

...在这里它被包装,以便可以在使用分组密码操作模式的代码中进行改造...

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Modes;

namespace SicStream
{
    /**
     * A class that implements an online Sic (segmented integer counter mode, or just counter (CTR) mode for short).
     * This class buffers one encrypted counter (representing the key stream) at a time.
     * The encryption of the counter is only performed when required, so that no key stream blocks are generated while they are not required.
     */
    public class StreamingSicBlockCipher : BufferedCipherBase
    {
        private SicStreamCipher parent;
        private int blockSize;

        public StreamingSicBlockCipher(SicBlockCipher parent)
        {
            this.parent = new SicStreamCipher(parent);
            this.blockSize = parent.GetBlockSize();
        }

        public override string AlgorithmName
        {
            get
            {
                return parent.AlgorithmName;
            }
        }

        public override byte[] DoFinal()
        {
            // returns no bytes at all, as there is no input
            return new byte[0];
        }

        public override byte[] DoFinal(byte[] input, int inOff, int length)
        {
            byte[] result = ProcessBytes(input, inOff, length);

            Reset();

            return result;
        }

        public override int GetBlockSize()
        {
            return blockSize;
        }

        public override int GetOutputSize(int inputLen)
        {
            return inputLen;
        }

        public override int GetUpdateOutputSize(int inputLen)
        {
            return inputLen;
        }

        public override void Init(bool forEncryption, ICipherParameters parameters)
        {
            parent.Init(forEncryption, parameters);
        }

        public override byte[] ProcessByte(byte input)
        {
            return new byte[] { parent.ReturnByte(input) };
        }

        public override byte[] ProcessBytes(byte[] input, int inOff, int length)
        {
            byte[] result = new byte[length];
            parent.ProcessBytes(input, inOff, length, result, 0);
            return result;
        }

        public override void Reset()
        {
            parent.Reset();
        }
    }
}

请注意,最后一段代码效率较低,因为需要创建额外的数组。

基于 Maarten Bodewes 有用且信息丰富的答案(非常感谢!),我对流式密码和块密码有了一些顿悟,我还想出了以下方法。

.NET BC 库有一个 StreamBlockCipher class,如 Java,但在其构造函数或初始值设定项中有一个守卫,即底层密码的块大小应为 1。

为了使用 StreamBlockCipher,我创建了一个 SicBlockCipher 的子class,用于在内部缓冲密钥流块。我将其命名为 StreamableSicBlockCipher。尚未测试,但如果有问题,它至少指出了另一个方向。

 public class StreamableSicBlockCipher : SicBlockCipher
{
    private int blockSize;
    private int position = 0;
    private byte[] zeroBlock;
    private byte[] keyStreamBlock;

    public StreamableSicBlockCipher(IBlockCipher cipher) : base(cipher)
    {
        blockSize=cipher.GetBlockSize();
        zeroBlock = new byte[blockSize];
        keyStreamBlock = new byte[blockSize];
    }



    public override int GetBlockSize()
    {
        return 1;
    }

    public override int ProcessBlock(byte[] input, int inOff, byte[] output, int outOff)
    {
        int keyStreamBlockOffset = position % blockSize;

        if (0==keyStreamBlockOffset)
        {

            var cipher = GetUnderlyingCipher();
            cipher.ProcessBlock(zeroBlock, 0, keyStreamBlock, 0);

            // Increment the counter
            int j = zeroBlock.Length;
            while (--j >= 0 && ++zeroBlock[j] == 0)
            {
            }
        }

        output[outOff] = (byte)(input[inOff] ^ keyStreamBlock[keyStreamBlockOffset]);

        position++;

        return 1;

    }
    public override void Reset()
    {
        base.Reset();
        this.position = 0;

    }

然后可以使用适当的包装器调用它,如下所示:

StreamBlockCipher EncCipher = new StreamBlockCipher(new StreamableSicBlockCipher(new AesEngine()));

可以使用 IBlockCipher 的实例完成初始化,获取块大小。下面的示例使用空 IV 初始化,使用 'Cipher' 这是 AESEngine 的一个实例。:

 EncCipher.Init(true, new ParametersWithIV(new KeyParameter(cryptoSecret), new byte[Cipher.GetBlockSize()]));