1

我需要与需要使用 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 河豚加密是一种在其他任何地方都不使用的格式。

笔记:

在我的实际应用程序中,我通过表单字段将加密字符串发送到 Web 服务。加密字符串采用 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/”&gt;
<input type=”hidden” name=”t” value=”adfinoidfhdfnsdfnoihoweirhqwdnd2394yuhealsnkxc234rwef45324fvsdf2” />
<input type=”submit” name=”submit” value=”Proceed to take the Test” />
</form>

我迷路了,我哪里错了?

4

1 回答 1

1

有几件事:

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

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

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

3) 但是 Perl 应用程序仍然没有运气。原因之一是(取自 Crypt 来源):

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)是不好的做法(而且 Crypt 无论如何也不会这样做)。这样做可以将第一个块与随机区分开来,并使您容易受到BEAST之类的攻击。除了完全罕见的极端情况外,使用 ECB 也是不好的做法。所以让我们忘掉那个 Perl 应用程序,专注于手头的事情吧。

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

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

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

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

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

7)如果您的生产密钥看起来像示例中的那个,那么还有另一个弱点。这样的字符串中没有足够的熵。您应该再次使用加密安全的随机生成器来生成您的密钥,这在 Ruby OpenSSL 中非常简单:

key = cipher.random_key

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

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

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

但是你仍然必须考虑重放这些代币,代币劫持,......你看,这并不容易。

于 2012-05-27T01:46:34.197 回答