【问题标题】:Strength of PBKDF based AES key基于 PBKDF 的 AES 密钥的强度
【发布时间】:2014-01-28 19:33:54
【问题描述】:

这是一个系统的存根,它将在 OpenSSL 中使用 AES 256 CBC 生成密钥对。下面代码的目的是生成两个随机密钥、一个 AES 密钥和一些其他公共数据。 AES 密钥将用于交换共享秘密。

免责声明:我不是密码学或安全系统方面的专家。我确实意识到了危险,但这个练习的重点是学术兴趣。如果有新手错误或完全不正确且危险的错误,请指出以帮助我学习。

// The key_generator() will produce the following public keys in addition
// to a couple of other private keys.
// public_identifier
// public_salt
// public_composite_identifier
// public_aes_key

int key_generator(/*some args*/)
{
    // Step 1
    //Obtain public_identifier. Possibly a hashed value of an unique ASCII string.
    unsigned char *public_identifier;


    // Step 2
    //Generate 256 bit private_primary_random_passkey which is secret. 
    //This random key is generated once and reused later.
    unsigned char *private_primary_random_passkey;

    if(RAND_bytes(private_primary_random_passkey, 256) == 0)     
        return FAILURE;


    // Step 3
    //Generate private_composite_identifier using public_identifier
    //and private_primary_random_passkey.
    //IMPORTANT - The method to obtain private_composite_identifier 
    //may be publicly known.  
    //The public_identifier is also publicly known but the 
    //private_primary_random_passkey is secret.
    unsigned char *private_composite_identifier;

    //<Some code for generating private_composite_identifier>
    //.....
    //</code>


    // Step 4     
    //Generate temporary temp_private_aes_key and temp_private_aes_IV;
    //NOTE - Used dummy vars wherever key length is required. 
    //Assume correct length is passed in.

    int aes_rounds = 25000;

    unsigned char *temp_private_aes_key;
    unsigned char *temp_private_aes_IV;

    if(EVP_BytesToKey(EVP_aes_256_cbc(), 
                      EVP_sha512(), 
                      private_composite_identifier, 
                      private_primary_random_passkey, 
                      private_composite_identifier_length/8, 
                      aes_rounds, 
                      temp_private_aes_key, 
                      temp_private_aes_IV) == 0)     
        return FAILURE;    


    // Step 5
    //Generate 128 bit random salt which is public.
    unsigned char *public_salt;

    if(RAND_bytes(public_salt, 128) == 0)     
        return FAILURE;


    // Step 6
    //Generate private_composite_identifier and public_composite_identifier 
    //using temp_private_aes_key and public_salt.
    unsigned char *public_composite_identifier;
    unsigned char *private_composite_identifier;

    if(EVP_BytesToKey(EVP_aes_256_cbc(), 
                      EVP_sha512(), 
                      temp_private_aes_key,
                      public_salt,
                      temp_private_aes_key_length/8, 
                      aes_rounds, 
                      private_composite_identifier, 
                      public_composite_identifier) == 0)     
        return FAILURE;    


    // Step 7
    //Generate 128 bit private_secondary_random_passkey which is secret. 
    //This random key is generated once and reused later.
    unsigned char *private_secondary_random_passkey;

    if(RAND_bytes(private_secondary_random_passkey, 128) == 0)     
        return FAILURE;

    unsigned char *private_aes_key;
    unsigned char *public_aes_key;

    if(EVP_BytesToKey(EVP_aes_256_cbc(), 
                      EVP_sha512(), 
                      private_composite_identifier,
                      private_secondary_random_passkey,
                      private_composite_identifier_length/8, 
                      aes_rounds, 
                      private_aes_key, 
                     public_aes_key) == 0)     
        return FAILURE
}

这是我的问题:

  • 是否应该使用 RSA 密钥对而不是 AES 密钥?为什么一个比另一个更受欢迎?
  • 由于用于生成密钥对的密钥很长、随机生成和加盐,以后使用相同的 AES/RSA 密钥对是否安全?我了解 Rainbow 表和其他措施的风险,但这些担忧是否通过随机盐和密钥以及随后的三级密钥生成得到缓解?
  • 恶意攻击者可以通过哪些方式重新创建密钥对或使用公开数据破坏此系统?
  • 您能想到的任何其他可以阻止或增强此系统的要点。

感谢您的宝贵时间。

【问题讨论】:

  • AES 是对称的。 RSA 是不对称的。
  • @JonathonReinhart - 感谢您指出 n00b 错误。 :) 如果在第 7 步中我们从可用信息生成 RSA 密钥对并使用该密钥对进行未来加密,您能否重新考虑这些问题?

标签: c security encryption cryptography openssl


【解决方案1】:

您真的很喜欢密码学工程,Crypto Stack Exchange 上的人可能更适合解决这类设计问题。

此外,对于任何有用的回复,您都缺乏很多细节。 (当问题更面向实施和具体时再回来)。


基于 PBKDF 的 AES 密钥的强度

首先是标题。

这个问题与猜测密码一样困难,而对于过去数据泄露的数百万字词列表之类的问题,可能会更容易。也就是说,您的攻击者不会尝试破解 AES 密钥 - 他或她会尝试猜测您用于派生密钥的密码。

要使用具有 128 位安全性的 AES-128 密钥,您需要在密码中包含 128 位熵。密码不是随机的,英文每个字母大约有 1.3 位的熵,因此预计 AES-128 使用大约 14+ 个字符的密码。如果您键入的是 AES-256,请使用 28 个以上字符的密码。


以下代码的目的是生成两个随机密钥、一个 AES 密钥和一些其他公共数据。 AES 密钥将用于交换共享机密。

这里的目标是什么?创建安全通道?还是只是交换密钥?

通常您使用公钥传输或协议传输对称或会话密钥。即:(1)使用Diffie-Hellman生成一个共享密钥(“密钥协议”,因为双方都加入了进程),然后在共享密钥下传输对称会话密钥;或 (2) 使用 RSA 传输“主”对称会话密钥(“密钥传输”,因为一方生成秘密,在对等方的 RSA 密钥下对其进行加密,然后将其发送给对等方)。

存在基于共享秘密和密码的经过身份验证的密钥交换。 SSL/TLS 有其中两个 - 预共享密钥 (PSK) 和安全远程密码 (SRP)。 PSK 使用 AES 作为原语,SRP 使用 Diffie-Hellman。

PSK 和 SRP 通常比 DH 和 RSA 更好,因为它们提供 (1) 相互身份验证和 (2) 通道绑定。根据您的陈述“AES 密钥将用于交换共享机密”,PSK 可能非常适合您。但它们是否是一个好的选择并不明显,因为我们不知道您想要完成什么。


是否应该使用 RSA 密钥对而不是 AES 密钥?为什么一个比另一个更受欢迎?

这取决于你的问题领域和你想要做什么。

如果服务器具有公共 RSA 密钥,那么您将使用 Diffie-Hellman 或 RSA。如果您和服务器共享一个秘密,请避免使用公钥内容并使用 PSK 或 SRP。

Diffie-Hellman 和 RSA 的计算成本很高。它们通常用于交换或传输对称密钥。 RSA-2048 可能需要 4 或 500 万条指令,这会削弱性能。

对称密码的计算速度很快。它们一次用于大部分流量 Diffie-Hellman 或 RSA 是完整的。我们称之为批量加密,它们是胖子。如果你有 Ivy Bridge 硬件,那么 AES-NI 可以接近 1 个周期/字节的加密数据。

当您阅读像 nginx 这样的 Web 服务器只能处理 1000 个 SSL/TLS 连接时,您会看到 Diffie-Hellman 和 RSA 的效果。一旦这些通道被设置并加密以进行批量加密,它就可以处理数千个加密流量。

以后使用相同的 AES/RSA 密钥对是否安全?

这些是苹果和橙子。您通常会长期使用 RSA 密钥对,而 AES 密钥仅限于会话。

通常,协议的每次运行都应使用不同的参数。该协议还应该缺乏对称性,以便攻击者无法启动您协议的第二个实例,然后让您的服务器用第二个实例回答第一个实例上出现的问题。


我了解 Rainbow 表和其他措施的风险,但这些担忧是否通过随机盐和密钥以及随后的三级密钥生成得到缓解?

这取决于您的问题领域和您想要做什么。我们无法真正回答,因为我们不知道您想要完成什么。

请参阅 John Steven 在 OWASP 的 Password Storage Cheat Sheet。他将带您了解威胁模型并解释系统的各个部分。详细处理可在Secure Password Storage获取。


恶意攻击者可以通过哪些方式重新创建密钥对或使用公开数据破坏该系统?

这取决于您的问题领域和您想要做什么。我们无法真正回答,因为我们不知道您想要完成什么。

密钥管理是密码学中最难的部分,我们看到的只是一个真空:

int key_generator(/*some args*/)

您需要详细说明所有参数、如何管理它们以及如何使用它们。


您能想到的任何其他可以阻止或增强此系统的要点。

这会跳出:对RAND_byesEVP_BytesToKey 的三个调用。通常,您会这样做:

byte[] master = Random(16) // or 32, or however many you need
byte[] key1 = Derive(master, "<some usage>", <other distinguishing information>)
byte[] key2 = Derive(master, "<some usage>", <other distinguishing information>)
byte[] key3 = Derive(master, "<some usage>", <other distinguishing information>)

master 可能来自密钥交换或密钥协议。

key1key2key3 会有所不同,因为 (1) 调用之间的用法不同,以及 (2) 对等方之间的附加区分信息不同。

Derive 可以是 HMACCMAC,并包含 KDF 的元素。另请查看 HKDF,它是“扩展然后提取”算法的最新技术。

【讨论】:

  • 感谢您非常详细的回复!首先是目标:这是建立一个系统,客户端可以确定地(重新)生成公钥对,该公钥对将用于使用现有的密钥对交换数据。密码不必是英文的。当客户端希望交换数据时,将重新输入十六进制密钥以重新生成密钥对。这就是为什么密码在存根中随机生成并呈现给注册客户端的原因。在初始生成密钥之后,每个客户端都会有一些与公钥和公共盐相关联的唯一标识符。
  • 我理解您关于使用派生方法而不是使用单独调用 EVP_BytesToKey 的观点(RAND_bytes 调用仅用于初始客户端注册)。我将进行充实的测试实施,牢记您所说的要点并返回。话虽如此,我一直无法在 C/C++ 的 OpenSSL 中找到从给定种子(在本例中为 256 位随机种子)确定性地创建 RSA 密钥对(或任何其他非对称密钥对)的好例子。如果您或这里的其他人能指出这样的例子,那将非常有用。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-10-29
  • 2018-05-31
相关资源
最近更新 更多