草庐IT

c# - Blowfish 引擎的 Bouncy CaSTLe CTS 模式未按预期工作

coder 2024-05-19 原文

也许我的期望是错误的。我不是密码学专家,我只是一个普通用户。到目前为止,我已经竭尽全力地尝试使这项工作成功。

背景信息:

我正在尝试从使用 Blowfish 引擎 (TCipher_Blowfish_) 和 CTS 操作模式 (cmCTS) 的 Delphi Encryption Compendium 移植旧版加密。私钥通过RipeMD256(THash_RipeMD256)散列。

问题:

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

来自维基百科:

In cryptography, ciphertext stealing (CTS) is a general method of using a block cipher mode of operation that allows for processing of messages that are not evenly divisible into blocks without resulting in any expansion of the ciphertext, at the cost of slightly increased complexity.

输出与老套路不一样:

我正在使用:

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

旧应用程序使用 ANSI 字符串,新应用程序使用 Unicode,因此对于我调用的每个输入字符串 Encoding.ASCII.GetBytes("plainText")Encoding.ASCII .GetBytes("privatepassword").

然后用 RipeMD256 对私有(private)密码字节进行哈希处理,我检查了输出字节,它们是相同的。

我可以确认问题在 Bouncy ClaSTLe 中是特定的(操作模式或缺少配置/步骤),因为我已经下载了第二个库 Blowfish.cs 并使用了 8 字节的输入(与密码 block 大小相同)并使用了具有相同 IV 的 Encrypt_CBC(bytes[]) 产生与旧格式相同的输出。

这是我用于 Blowfish.csBouncy CaSTLe 的代码草图:

Delphi 加密纲要

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;

Blofish.cs

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);

I assume that CTS and CBC will always have the same result if the input is 8 bits length. Is this just lucky/coincidence or is fundamentally truth?

充气城堡

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 转发实现。

我不知道:

  • 如果 Delphi Encryption Compendium 中使用的 CTS 模式使用 CBC 和 CTS。我无法在任何地方找到记录。
  • 在 Bouncy CaSTLe 中仅调用 DoFinal() 和 ProcessBytes() 然后调用 DoFinal() 之间的区别,我认为当输入 block 大于引擎 block 大小时需要这样做,在这种情况下它们的大小相同。
  • 如果 Delphi Encryption Compendium 是正确的/错误的,或者如果 Bouncy CaSTLe 是正确的/错误的。我没有足够的密码学知识来理解实现,否则我不会在这里提问(我需要指导)。

最佳答案

I assume that CTS and CBC will always have the same result if the input is 8 bits length. Is this just lucky/coincidence or is fundamentally truth?

不,这是一个错误的陈述。

这是来自 Wikipedia 的引述:

Ciphertext stealing for CBC mode doesn't necessarily require the plaintext to be longer than one block. In the case where the plaintext is one block long or less, the Initialization vector (IV) can act as the prior block of ciphertext.

因此,即使对于 8 字节输入的情况,CTS 算法也会发挥作用并影响输出。基本上你关于 CTS 和 CBS 平等的陈述可以颠倒过来:

CTS and CBC will always have the same result up to last two blocks.

您可以使用以下示例进行验证:

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));
}

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

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

If the CTS Mode used in Delphi Encryption Compendium uses CBC along with CTS. I Couldn't find documented anywhere.

我也没有在 Delphi Encryption Compendium 中找到任何关于 CTS 模式的文档,但是在 source code 中有这样的评论:

cmCTSx = double CBC, with CFS8 padding of truncated final block

Modes cmCTSx, cmCFSx, cmCFS8 are proprietary modes developed by me. These modes works such as cmCBCx, cmCFBx, cmCFB8 but with double XOR'ing of the inputstream into Feedback register.

所以 CTS 模式似乎是在 Delphi Encryption Compendium 中以自定义方式实现的,这与 Bouncy CaSTLe 的标准实现不兼容。

The difference between calling just DoFinal() and ProcessBytes() then DoFinal() in Bouncy Castle, I imagine that is required when the input block is larger than engine block size, in this case they are the same size.

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

var encryptedData = inCipher.DoFinal(plainText);

DoFinal() 重载将计算输出缓冲区的大小并进行所需的 ProcessBytes()DoFinal() under the hood 调用。

If Delphi Encryption Compendium is correct/wrong or If Bouncy Castle is correct/wrong. I don't have enough knowledge in cryptography to understand the implementation, otherwise I wouldn't ask a question here (I need guidance).

总结一下:

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

更新

(有关 Delphi Encryption Compendium 在 3.0 版中实现的更多详细信息)

这是来自 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

步骤F[i] = F[i-1] xor C[i]是DEC作者发明的,使得加密结果不同。对于 CTS 模式至关重要的最后两个 block 的处理也不是通过标准实现的。

这是来自 DEC v 3.0 ReadMe.txt 的评论,描述了作者添加此类修改的原因:

cmCTS Mode, XOR's the Data before and now after the encryption. This has better Securityeffect when using a InitVector, the Output is secure when a bad InitVector is used, ca 1% Speed lossed

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

关于c# - Blowfish 引擎的 Bouncy CaSTLe CTS 模式未按预期工作,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42249867/

有关c# - Blowfish 引擎的 Bouncy CaSTLe CTS 模式未按预期工作的更多相关文章

  1. ruby-on-rails - 由于 "wkhtmltopdf",PDFKIT 显然无法正常工作 - 2

    我在从html页面生成PDF时遇到问题。我正在使用PDFkit。在安装它的过程中,我注意到我需要wkhtmltopdf。所以我也安装了它。我做了PDFkit的文档所说的一切......现在我在尝试加载PDF时遇到了这个错误。这里是错误:commandfailed:"/usr/local/bin/wkhtmltopdf""--margin-right""0.75in""--page-size""Letter""--margin-top""0.75in""--margin-bottom""0.75in""--encoding""UTF-8""--margin-left""0.75in""-

  2. ruby-on-rails - Rails - 子类化模型的设计模式是什么? - 2

    我有一个模型:classItem项目有一个属性“商店”基于存储的值,我希望Item对象对特定方法具有不同的行为。Rails中是否有针对此的通用设计模式?如果方法中没有大的if-else语句,这是如何干净利落地完成的? 最佳答案 通常通过Single-TableInheritance. 关于ruby-on-rails-Rails-子类化模型的设计模式是什么?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.co

  3. ruby-on-rails - 'compass watch' 是如何工作的/它是如何与 rails 一起使用的 - 2

    我在我的项目目录中完成了compasscreate.和compassinitrails。几个问题:我已将我的.sass文件放在public/stylesheets中。这是放置它们的正确位置吗?当我运行compasswatch时,它不会自动编译这些.sass文件。我必须手动指定文件:compasswatchpublic/stylesheets/myfile.sass等。如何让它自动运行?文件ie.css、print.css和screen.css已放在stylesheets/compiled。如何在编译后不让它们重新出现的情况下删除它们?我自己编译的.sass文件编译成compiled/t

  4. ruby - 解析 RDFa、微数据等的最佳方式是什么,使用统一的模式/词汇(例如 schema.org)存储和显示信息 - 2

    我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i

  5. ruby - 如何在续集中重新加载表模式? - 2

    鉴于我有以下迁移:Sequel.migrationdoupdoalter_table:usersdoadd_column:is_admin,:default=>falseend#SequelrunsaDESCRIBEtablestatement,whenthemodelisloaded.#Atthispoint,itdoesnotknowthatusershaveais_adminflag.#Soitfails.@user=User.find(:email=>"admin@fancy-startup.example")@user.is_admin=true@user.save!ende

  6. ruby - 无法让 RSpec 工作—— 'require' : cannot load such file - 2

    我花了三天的时间用头撞墙,试图弄清楚为什么简单的“rake”不能通过我的规范文件。如果您遇到这种情况:任何文件夹路径中都不要有空格!。严重地。事实上,从现在开始,您命名的任何内容都没有空格。这是我的控制台输出:(在/Users/*****/Desktop/LearningRuby/learn_ruby)$rake/Users/*******/Desktop/LearningRuby/learn_ruby/00_hello/hello_spec.rb:116:in`require':cannotloadsuchfile--hello(LoadError) 最佳

  7. ruby - 在没有 sass 引擎的情况下使用 sass 颜色函数 - 2

    我想在一个没有Sass引擎的类中使用Sass颜色函数。我已经在项目中使用了sassgem,所以我认为搭载会像以下一样简单:classRectangleincludeSass::Script::FunctionsdefcolorSass::Script::Color.new([0x82,0x39,0x06])enddefrender#hamlengineexecutedwithcontextofself#sothatwithintemlateicouldcall#%stop{offset:'0%',stop:{color:lighten(color)}}endend更新:参见上面的#re

  8. ruby-on-rails - rspec should have_select ('cars' , :options => ['volvo' , 'saab' ] 不工作 - 2

    关闭。这个问题需要detailsorclarity.它目前不接受答案。想改进这个问题吗?通过editingthispost添加细节并澄清问题.关闭8年前。Improvethisquestion在首页我有:汽车:VolvoSaabMercedesAudistatic_pages_spec.rb中的测试代码:it"shouldhavetherightselect"dovisithome_pathit{shouldhave_select('cars',:options=>['volvo','saab','mercedes','audi'])}end响应是rspec./spec/request

  9. ruby-on-rails - s3_direct_upload 在生产服务器中不工作 - 2

    在Rails4.0.2中,我使用s3_direct_upload和aws-sdkgems直接为s3存储桶上传文件。在开发环境中它工作正常,但在生产环境中它会抛出如下错误,ActionView::Template::Error(noimplicitconversionofnilintoString)在View中,create_cv_url,:id=>"s3_uploader",:key=>"cv_uploads/{unique_id}/${filename}",:key_starts_with=>"cv_uploads/",:callback_param=>"cv[direct_uplo

  10. c# - 如何在 ruby​​ 中调用 C# dll? - 2

    如何在ruby​​中调用C#dll? 最佳答案 我能想到几种可能性:为您的DLL编写(或找人编写)一个COM包装器,如果它还没有,则使用Ruby的WIN32OLE库来调用它;看看RubyCLR,其中一位作者是JohnLam,他继续在Microsoft从事IronRuby方面的工作。(估计不会再维护了,可能不支持.Net2.0以上的版本);正如其他地方已经提到的,看看使用IronRuby,如果这是您的技术选择。有一个主题是here.请注意,最后一篇文章实际上来自JohnLam(看起来像是2009年3月),他似乎很自在地断言RubyCL

随机推荐