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()]));
我正在用 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()]));