【问题标题】:Source text, key size relationship for encryption/decryption in GoGo中加密/解密的源文本,密钥大小关系
【发布时间】:2015-06-01 15:39:46
【问题描述】:

在下面的代码中(也在http://play.golang.org/p/77fRvrDa4A,但在浏览器中“处理时间太长”),sourceText 的 124 字节版本不会加密,因为:“消息太长RSA 公钥大小”为 1024。它和更长的 124 字节 sourceText 版本使用 2048 位密钥大小。

我的问题是,在给定源文本的字节长度的情况下,如何准确计算 rsa.GenerateKey 中的密钥大小? (一小段文字在 4096 键大小下需要将近 10 秒,直到运行时我才知道 sourceText 的长度。)

https://stackoverflow.com/a/11750658/3691075 对此进行了非常简短的讨论,但我不清楚,因为我不是加密专家。

我的目标是加密、存储在数据库中并解密大约 300 字节长的 JSON 字符串。我控制发送端和接收端。文本加密一次,多次解密。任何策略提示将不胜感激。

package main

import (
    "crypto/md5"
    "crypto/rand"
    "crypto/rsa"
    "fmt"
    "hash"
    "log"
    "time"
)

func main() {
     startingTime := time.Now()
     var err error
     var privateKey *rsa.PrivateKey
     var publicKey *rsa.PublicKey
     var sourceText, encryptedText, decryptedText, label []byte

    // SHORT TEXT 92 bytes
     sourceText = []byte(`{347,7,3,8,7,0,7,5,6,4,1,6,5,6,7,3,7,7,7,6,5,3,5,3,3,5,4,3,2,10,3,7,5,6,65,350914,760415,33}`)
     fmt.Printf("\nsourceText byte length:\n%d\n", len(sourceText))

    // LONGER TEXT 124 bytes
    // sourceText = []byte(`{347,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,65,350914,760415,33}`)
    // fmt.Printf("\nsourceText byte length:\n%d\n", len(sourceText))

    if privateKey, err = rsa.GenerateKey(rand.Reader, 1024); err != nil {
         log.Fatal(err)
    }

    // fmt.Printf("\nprivateKey:\n%s\n", privateKey)

    privateKey.Precompute()

    if err = privateKey.Validate(); err != nil {
         log.Fatal(err)
    }

     publicKey = &privateKey.PublicKey

     encryptedText = encrypt(publicKey, sourceText, label)
     decryptedText = decrypt(privateKey, encryptedText, label)

     fmt.Printf("\nsourceText: \n%s\n", string(sourceText))
     fmt.Printf("\nencryptedText: \n%x\n", encryptedText)
     fmt.Printf("\ndecryptedText: \n%s\n", decryptedText)

     fmt.Printf("\nDone in %v.\n\n", time.Now().Sub(startingTime))
}

func encrypt(publicKey *rsa.PublicKey, sourceText, label []byte) (encryptedText []byte) {
     var err error
     var md5_hash hash.Hash
     md5_hash = md5.New()
     if encryptedText, err = rsa.EncryptOAEP(md5_hash, rand.Reader,           publicKey, sourceText, label); err != nil {
         log.Fatal(err)
     }
     return
}

func decrypt(privateKey *rsa.PrivateKey, encryptedText, label []byte) (decryptedText []byte) {
     var err error
     var md5_hash hash.Hash
     md5_hash = md5.New()
     if decryptedText, err = rsa.DecryptOAEP(md5_hash, rand.Reader, privateKey, encryptedText, label); err != nil {
         log.Fatal(err)
     }
     return
}

【问题讨论】:

    标签: encryption go cryptography aes rsa


    【解决方案1】:

    通常不会根据有效负载计算 RSA 密钥大小。只需根据安全性(越大越好)和性能(越小越好)之间的折衷选择一种 RSA 密钥大小。如果这样做了,请结合使用 hybrid encryption 与 AES 或其他对称密码来实际加密数据。

    如果负载不超过 300 字节并且您使用的是 OAEP(至少 42 字节的填充),那么您可以轻松计算最小密钥大小:

    (300 + 42) * 8 = 2736 bit
    

    这已经是一个合理大小的键了。根据当今的规范,它提供了良好的安全性,而且速度相当快。无需为此应用混合加密方案。

    现在,您可能会注意到密钥大小不是 2 的幂。This is not a problem。但是,您应该使用 64 位倍数的密钥大小,因为处理器使用 32 位和 64 位原语进行实际计算,因此您可以提高安全性而不会降低性能。下一个这样的密钥大小将是:

    ceil((300 + 42) * 8 / 64.0) * 64 = 2752 bit
    

    以下是某些语言/框架接受(不是性能方面)作为密钥大小的一些实验结果:

    • Golang:1 位的倍数并且 >= 1001(原文如此!)[使用 ideone.com]
    • PyCrypto:256 位的倍数且 >= 1024 [本地安装]
    • C#:16 位的倍数且 >= 512 [使用 ideone.com]
    • Groovy:1 位的倍数且 >= 512 [本地安装]
    • Java:1 位的倍数且 >= 512 [使用 ideone.com:Java 和 Java7]
    • PHP/OpenSSL Ext:128 位的倍数且 >= 640 [使用 ideone.com]
    • Crypto++:1 位的倍数且 >= 16 [本地安装,最大验证强度为 3]

    在您决定使用某种特定的密钥大小之前,您应该检查所有框架是否支持该大小。如您所见,结果差异很大。

    我尝试写了一些密钥生成、加密和解密的性能测试,使用不同的密钥大小:512、513、514、516、520、528、544、576。由于我不知道怎么做,那就是很难把握好时机。所以我选择了 Java 和 Crypto++。 Crypto++ 代码可能有很多错误,因为 520 位和 528 位密钥的密钥生成速度比其他密钥大小快七个数量级,而其他密钥大小对于小密钥大小窗口或多或少是恒定的。

    在 Java 中,密钥生成非常明确,513 位密钥的生成速度比 512 位密钥慢 2-3。除此之外,结果几乎是线性的。该图已标准化,整个 keygen-enc-dec 循环的迭代次数为 1000。

    解密在 544 位时略有下降,这是 32 位的倍数。由于它是在 32 位 debian 上执行的,这可能意味着确实有一些性能改进,但另一方面,对于该密钥大小,加密速度较慢。

    由于这个基准测试不是在 Go 中完成的,所以我不会就开销有多小给出任何建议。

    【讨论】:

    • 抱歉,最后一段完全不正确。我觉得它一定是不正确的,我已经用 Java 对其进行了测试以确定(所以我必须快速进食,否则我的健康青豆会变冷)。
    • 为了与现有实现 100% 兼容,我将使用 64 位甚至 3072 的倍数(2048 + 1024,因此 2^x + 2^(x-1))。请注意,某些(硬件)运行时也被限制为 2048 位。
    • Maarten,你的两个cmets有点矛盾。首先你说我的最后一段是错误的(我怀疑你的意思是 64 位的倍数),然后你说 64 位的倍数是可以的。这有点奇怪。也许你可以澄清你的意思。无论如何,我用你的批评做了一些实验,结果在不同的语言/框架之间有很大的不同。我也想做 Ruby 和 PhpSecLib,但是 ruby​​ 在 ideone 上有问题,我无法从 sourceforge 下载 phpseclib。
    • e.g. 之间存在明显的性能差异。 Java 平台上的 1025/1057 位和 1088 位。对于 1000 次迭代,我得到的平均时间为 4.744/4.874 毫秒和 5.150 毫秒。但是,出于兼容性原因,8 字节的差异是一个非常的好主意。在字节边界上使用其他东西只是自找麻烦。
    • PyCrypto 确实有点过分了。 32 字节 步骤?这对于协议来说很好,但对于底层原语则不然。我个人认为对于 API 8 位步骤很好,但 1 位步骤仍然是可取的。
    猜你喜欢
    • 2013-01-02
    • 2014-10-24
    • 2014-05-26
    • 2014-02-21
    • 1970-01-01
    • 1970-01-01
    • 2012-06-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多