Blowfish加密的字符串变得乱七八糟

时间:2012-05-26 15:32:53

标签: ruby api encryption blowfish

我需要与需要在Blowfish和Base64中编码的API进行通信。

在我的自定义库中,我从:

开始
# encoding: utf-8
require "base64"
require 'crypt/blowfish'

我创建了一个实例:

@blowfish_key = '1234567887654321'
@blowfish     = Crypt::Blowfish.new(@blowfish_key)

然后我开始创建加密字符串(或API调用它的“票证”)

@string_to_encrypt  = "#{@partnerid},#{user.id},#{exam_id},#{return_url},#{time_stamp}"
@enc                = @blowfish.encrypt_string(@string_to_encrypt)

在Rails控制台中,我可以使用@blowfish.decrypt_string(@enc)解密而不会出现任何问题。但API让我胡言乱语:

Invalid ticket:
Decrypted String  :)IŠkó}*Ogû…xÃË-ÖÐHé%q‹×ÎmªÇjEê !©†xRðá=Ͳ    [À}=»ïN)'sïƒJJ=:›õ)¦$ô1X¢

此外,当我在控制台中加密一些简单的东西时,比如“Hello”,并将加密的字符串提供给在线Blowfish解码器,如http://webnet77.com/cgi-bin/helpers/blowfish.pl,我得到同样的乱码。

这就像Ruby blowfish加密是一种在其他任何地方都没有使用过的格式。

注意:

在我的实际应用程序中,我通过表单字段将加密的字符串发送到Webservice。加密的字符串是Base64编码的,前缀为'a'。

@enc = Base64.encode64(@enc)
@enc = 'a' + CGI::escape(@enc)

这在他们的文档中有解释:

故障单格式:

t=’a’ + URL_Encode (
Base64_Encode (
Blowfish_Encrypt ( ‘partnerid,user_id,test_id,URL_Encode(return_URL),ticket_timestamp’, ‘blowfish_key’)
      )
)
  

请注意,Blowfish_Encrypt函数接受两个参数 -   1)要加密的字符串和2)十六进制密钥。还要注意门票有   以小写'a'为前缀(ASCII 97)。

HTML表单的示例:

<form method=”POST” action=”http://www.expertrating.com/partner_site_name/”>
<input type=”hidden” name=”t” value=”adfinoidfhdfnsdfnoihoweirhqwdnd2394yuhealsnkxc234rwef45324fvsdf2” />
<input type=”submit” name=”submit” value=”Proceed to take the Test” />
</form>

我迷路了,我哪里出错了?

1 个答案:

答案 0 :(得分:1)

有几件事情:

1)正如在评论中所说的那样,如果你打印加密的字符串,那么这几乎总是会造成麻烦,因为加密的字符串很可能包含非ASCII字符,这些字符要么是不可打印的,要么是表示的他人不理解。实现广泛理解的表示的最佳方法是使用Base64或Hex编码对结果进行编码,因此您在应用程序中应用的Base64编码就可以了。

2)您链接的Perl应用程序使用例如十六进制编码。因此,它也只接受十六进制编码的加密字符串。这就是为什么它根本不接受你的任何输入。您可以按如下方式获得适合应用程序的十六进制编码:

hex = encrypted.unpack("H*")[0].upcase

3)但Perl应用仍然没有运气。其中一个原因是(取自地穴来源):

def encrypt_stream(plainStream, cryptStream)
  initVector = generate_initialization_vector(block_size() / 4)
  chain = encrypt_block(initVector)
  cryptStream.write(chain)

这意味着Crypt将IV写为加密消息的第一个块。这是完全没问题,但我所知道的大多数现代加密库都不会在IV之前加上,而是假设它是作为两个通信方之间的带外信息进行交换的。

4)但即使知道这一点,你仍然没有运气的Perl应用程序。原因是Perl应用程序使用ECB模式加密,其中Crypt gem使用CBC模式。 CBC模式使用IV,因此即使您使用的是全零IV,即使是单块消息也不会匹配。但是使用全零IV(或任何其他确定性IV)是bad practice(并且Crypt无论如何都不会这样做)。这样做可以将第一个区块与随机区分开,并使您受到BEAST之类的攻击。除了完全罕见的边缘情况之外,使用ECB也是不好的做法。所以,让我们忘记那个Perl应用程序,专注于手头的事情。

5)我当然赞成使用Ruby OpenSSL,但在这种情况下,如果我告诉你使用它而不是Crypt gem更好的整体安全性,我认为我不是主观的。只告诉你这样会很蹩脚,所以这里有两个原因:

  • Crypt使用可预测的随机生成器(srandrand的组合)生成其IV,但这还不够好。它必须是加密安全的随机生成器。

  • Crypt似乎不再被维护,并且在此期间发生了一些事情,或者当时未知或者从未在该项目的范围内。例如,OpenSSL开始处理泄漏弹性加密,以防止针对时间,缓存未命中等的侧通道攻击。这可能从来就不是Crypt的意图,但这种攻击在现实生活中构成了真正的威胁。

6)如果我的讲道已经说服你做出改变,那么我可以继续问你是否真的必须是Blowfish。毫无疑问,算法本身非常出色,但现在有更好的,甚至更安全的选项,例如AES。如果它绝对必须是Blowfish,那么Ruby OpenSSL也支持它:

cipher = OpenSSL::Cipher.new('bf-cbc')

7)如果您的生产密钥与示例中的密钥类似,那么还有另一个弱点。这些字符串中没有足够的熵。你应该做的是再次使用加密安全的随机生成器生成你的密钥,这在Ruby OpenSSL中非常容易:

key = cipher.random_key

好处是它会根据要使用的密码算法自动选择合适的密钥长度。

8)最后,我是否正确地假设您使用该加密结果作为某种形式的身份验证令牌?在将其附加到正在呈现的HTML时,请等待在某些POST请求中将其接收回来并将收到的令牌与原始令牌进行比较以验证某些操作?如果是这种情况,那么这将再次成为不好的做法。这一次更是如此。您在此处未使用经过身份验证的加密,这意味着您的密文 malleable 。这意味着攻击者可以相对容易地伪造该令牌的内容而无需实际知道您的加密密钥。这导致各种攻击,甚至导致涉及密钥恢复的完全妥协。

必须使用经过身份验证的加密模式(GCM,CCM,EAX ...)或使用消息身份验证代码来检测密文是否已被篡改。更好的是,根本不使用加密并使用安全散列函数生成票证。这里的关键是计算安全随机化值的散列,否则再次可以预测结果。您的示例中使用的时间戳是不够的。它必须是加密安全的随机数,可能由SecureRandom生成。

但是你还是要考虑重放这些令牌,令牌劫持......你知道,这并不容易。