当前位置: 首页 > 知识库问答 >
问题:

Blowfish引擎的Bouncy Castle CTS模式未按预期工作

袁文景
2023-03-14

也许我的期望是错误的。我不是密码学专家,我只是一个简单的用户。我已经尽了最大的努力使这项工作,但到目前为止还没有成功。

背景资料:

我正在尝试从Delphi Encryption Compendium移植一个遗留加密,它使用Blowfish引擎(TCIpher_Blowfish_)和CTS操作模式(CMCTS)。私钥由RipeMD256散列(thash_RIPEMD256)。

输入的纯文本字节数组需要与cipher_block的大小相同。据我所知不应该。

来自维基百科:

在密码学中,密文窃取(CTS)是使用分组密码操作模式的一种通用方法,它允许处理不能均匀地分割成块的消息,而不会导致密文的任何扩展,代价是略微增加复杂性。

我正在使用:

  • 相同IV
  • 相同密码
  • 相同输入纯文本

旧的应用程序使用ANSI字符串,新的应用程序使用Unicode,因此对于每个输入字符串,我都调用encoding.ascii.getBytes(“plaintext”)encoding.ascii.getBytes(“privatePassword”)

私有密码字节然后由RipeMD256散列,我检查了输出字节,它们是相同的。

我可以确认该问题是在Bouncy Clastle(操作模式或缺少配置/步骤)中出现的,因为我下载了第二个库blowfish.cs,并且使用了8字节的输入(与密码块大小相同),并且使用了encrypt_cbc(bytes[]),其IV结果与旧格式的输出相同。

这是我用于blowfish.csbouncy castle的代码草图:

var 
  IV: Array [0..7] of Byte (1,2,3,4,5,6,7,8);
  Key: String = '12345678';
with TCipher_Blowfish.Create('', nil) do
begin
  try
    InitKey(Key, @IV); //Key is auto hashed using RIPE256 here;
    Result:= CodeString('12345678', paEncode, -1); //Output bytes is later encoded as MIME64 here, the result is the hash.
  finally
    Free;  
  end;
end;
var hashOfPrivateKey = HashValue(Encoding.ASCII.GetBytes("12345678"));
Blowfish b = new BlowFish(hashOfPrivateKey);

b.IV = new byte[8] { 1, 2, 3, 4, 5, 6, 7, 8};

var input = Encoding.ASCII.GetBytes("12345678");
var output = b.Encrypt_CBC(input);

我假设如果输入长度为8位,那么CTS和CBC的结果总是相同的。这仅仅是幸运/巧合还是根本上是事实?

IBufferedCipher inCipher = CipherUtilities.GetCipher("BLOWFISH/CTS");
var hashOfPrivateKey = HashValue(Encoding.ASCII.GetBytes("12345678"));
var key = new KeyParameter(hashOfPrivateKey);
var IV = new byte[8] { 1, 2, 3, 4, 5, 6, 7, 8};
var cipherParams = new ParametersWithIV(key, IV); 
inCipher.Init(true, cipherParams);
var input = Encoding.ASCII.GetBytes("12345678");

//try one: direct with DoFinal
var output = inCipher.DoFinal(input);
// output bytes different from expected

inCipher.Reset();

//try two: ProcessBytes then DoFinal
var outBytes = new byte[input.Length];
var res = inCipher.ProcessBytes(input, 0, input.Length, outBytes, 0);
var r = inCipher.DoFinal(outBytes, res);
// outBytes bytes different from expected

正如我所说的,我比较CBC和CTS的前提是,给定8个字节的输入,输出将是相同的。如果即使有相同的输入,输出也不相同,我也不能用Bouncy Castle转发实现。

    null

共有1个答案

容俊豪
2023-03-14

我假设如果输入长度为8位,那么CTS和CBC的结果总是相同的。这仅仅是幸运/巧合还是根本上是事实?

不,这是假的说法。

以下是维基百科中的一段话:

static byte[] EncryptData(byte[] input, string algorithm)
{
    IBufferedCipher inCipher = CipherUtilities.GetCipher(algorithm);
    var hashOfPrivateKey = HashValue(Encoding.ASCII.GetBytes("12345678"));
    var key = new KeyParameter(hashOfPrivateKey);
    var IV = new byte[8] { 1, 2, 3, 4, 5, 6, 7, 8 };
    var cipherParams = new ParametersWithIV(key, IV);
    inCipher.Init(true, cipherParams);

    return inCipher.DoFinal(input);
}

static void Main(string[] args)
{
    var data = Encoding.ASCII.GetBytes("0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF");
    var ctsResult = EncryptData(data, "BLOWFISH/CTS");
    var cbcResult = EncryptData(data, "BLOWFISH/CBC");
    var equalPartLength = data.Length - 2 * 8;
    var equal = ctsResult.Take(equalPartLength).SequenceEqual(cbcResult.Take(equalPartLength));
}

所以这基本上是你主要问题的答案。您不应该期望8字节输入的CTS和CBC输出相同。

以下是你其他问题的答案(我希望):

如果Delphi加密简编中使用的CTS模式与CTS一起使用CBC。我到处都找不到文件。

这样看来CTS模式在Delphi加密简编中是以自定义的方式实现的,与Bouncy Castle的标准实现不兼容。

在Bouncy Castle中只调用DoFinal()和ProcessBytes()然后调用DoFinal()之间的区别,我想这是当输入块大于引擎块大小时需要的,在本例中,它们的大小相同。

如果按顺序加密数据,则需要调用对processBytes()/DoFinal()。例如,如果流式传输庞大的数据,则可能需要这样做。但是,如果您的例程采用整个字节数组进行加密,则只需调用dofinal()的以下方便重载一次:

var encryptedData = inCipher.DoFinal(plainText);

doFinal()重载将计算输出缓冲区的大小,并在Hood下调用processBytes()doFinal()

如果Delphi加密简编是正确的/错误的,或者如果Bouncy Castle是正确的/错误的。我在密码学方面没有足够的知识来理解实现,否则我不会在这里提出一个问题(我需要指导)。

这里总结一下:

  1. 对于8字节输入,CTS和CBC的输出不应相同。
  2. 似乎Delphi加密简编为CTS使用了自定义算法。由于Bouncy Castle是按照标准来实现的,这些库会产生不同的结果。如果您的新应用程序不需要支持传统Delphi应用程序生成的加密数据,那么您可以使用Bouncy Castle就可以了。在其他情况下,您应该使用与Delphi加密简编相同的自定义CTS算法,不幸的是,这将需要将其源代码端口移植到C#。

更新

(关于3.0版中Delphi加密简编实现的更多细节)

下面是DEC 3.0版的CTS编码代码:

S := @Source;
D := @Dest;

// ...

begin
    while DataSize >= FBufSize do
    begin
        XORBuffers(S, FFeedback, FBufSize, D);
        Encode(D);
        XORBuffers(D, FFeedback, FBufSize, FFeedback);
        Inc(S, FBufSize);
        Inc(D, FBufSize);
        Dec(DataSize, FBufSize);
    end;
    if DataSize > 0 then
    begin
        Move(FFeedback^, FBuffer^, FBufSize);
        Encode(FBuffer);
        XORBuffers(S, FBuffer, DataSize, D);
        XORBuffers(FBuffer, FFeedback, FBufSize, FFeedback);
    end;
end;

这里我们看到DEC文档中提到的双异或。基本上,这段代码实现了以下算法:

C[i] = Encrypt( P[i] xor F[i-1] )
F[i] = F[i-1] xor C[i]
F[0] = IV

而标准算法是:

C[i] = Encrypt( P[i] xor C[i-1] )
C[0] = IV

下面是DEC v 3.0 Readme.txt的注释,它描述了作者为什么要添加这样的修改:

cmCTS模式,对加密前和加密后的数据进行异或。当使用InitVector时,这具有更好的安全性,当使用错误的InitVector时,输出是安全的,会损失1%的速度

当安全库的作者试图通过这种幼稚的修改来使底层算法“更安全”时,这是一个非常常见的错误。在许多情况下,这种变化具有相反的效果并降低保护强度。当然,另一个缺点是加密的数据不能被根据标准实现的其他库解密,就像您的情况一样。

 类似资料:
  • 我使用的是角反应形式。在表单组的窗体控件中,我使用无法正常工作的正则表达式设置了验证器.模式,我不明白为什么。我已经验证了这个网站上的正则表达式 https://www.regextester.com/99144,以检查它,它工作正常 正则表达式(最少八个字符,至少一个大写字母,一个小写字母,一个数字和一个特殊字符) 这是表单组 因此,当我键入“World@20”时,它验证为真,但当我开始键入“W

  • 我正在使用spring Roo并希望访问Controller类中的一个bean,该类在ApplicationContext.xml中具有以下配置: 配置类本身是: 在我的Controller中,我认为一个简单的Autowired注释应该可以完成这项工作 在启动过程中,spring在setSkipWeeks方法中打印消息。不幸的是,每当我在控制器中调用config.getSkipWeeks()时,它

  • 当我运行以下程序时,它只打印 然而,从Java 8的equalsIgnoreCase文档中我们发现: 如果以下至少一项为真,则两个字符c1和c2被视为相同的忽略情况: •对每个字符应用java.lang.character.ToUpperCase(char)方法会产生相同的结果 所以我的问题是为什么这个程序不打印 在这两种操作中,都使用了大写字符。

  • 我试图使用来传输我根据前面的问题设置的自定义标头。 我在文件中读到... 我的属性包括:

  • 我正在和selenium一起工作,刮一些数据。 有一个按钮在页面上,我正在点击说“Custom_Cols”。这个按钮为我打开了一个窗口,我可以在那里选择我的列。 我的问题是为什么新窗口上的元素不可见,即使我正在等待元素的可见。补充一下,我已经尝试增加延迟时间,但我还是会偶尔出现这个错误。 我的密码在这里