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

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

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

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库来实现相同的function吗?

遗憾的是, SicBlockCipher本身并未实现为流密码,因此(实际上)此function无法直接使用。

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

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

我看到如何做到这一点的唯一方法是创建自己的IBlockCipher实现并实现所述function。


这是计数器模式作为流密码……

 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类,就像在Java中一样,但在其ctor或初始化程序中有一个保护,底层密码的块大小应为1。

为了使用StreamBlockCipher,我创建了一个SicBlockCipher的子类,它在内部缓冲了keystream块。 我把它命名为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的实例来获得块大小。 使用’Cipher’作为AESE发动的实例:

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