在Go语言中,加密/解密的密钥大小关系如下:

huangapple go评论77阅读模式
英文:

Source text, key size relationship for encryption/decryption in Go

问题

在下面的代码中(也可以在http://play.golang.org/p/77fRvrDa4A中找到,但在浏览器中处理时间太长),124字节版本的sourceText无法加密,因为它的长度超过了1024位的RSA公钥大小。它和更长的124字节sourceText版本可以与2048位的密钥大小一起使用。

我的问题是,如何根据源文本的字节长度准确计算rsa.GenerateKey中的密钥大小?(一个小段文字的长度在4096位密钥大小下需要近10秒钟,而我在运行时不知道源文本的长度。)

在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,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
}
英文:

In the code below (also at http://play.golang.org/p/77fRvrDa4A but takes "too long to process" in the browser there) the 124 byte version of the sourceText won't encrypt because: "message too long for RSA public key size" of 1024. It, and the longer 124 byte sourceText version, work with 2048 bit key size.

My question is how does one exactly calculate the key size in rsa.GenerateKey given the byte length of the source text? (A small paragraph size of text takes nearly 10 seconds at 4096 key size, and I don't know the length of the sourceText until runtime.)

There's a very brief discussion of this at https://stackoverflow.com/a/11750658/3691075, but it's not clear to me as I'm not a crypto guy.

My goal is to encrypt, store in a DB and decrypt about 300-byte long JSON strings. I control both the sending and the receiving end. Text is encrypted once, and decrypted many times. Any hints of strategy would be appreciated.

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
}

答案1

得分: 4

通常情况下,人们不会根据有效载荷计算RSA密钥的大小。只需要根据安全性(越大越好)和性能(越小越好)之间的折衷选择一个RSA密钥的大小。如果这样做了,可以与AES或其他对称密码一起使用混合加密来实际加密数据。

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

(300 + 42) * 8 = 2736位

这已经是一个合理的密钥大小。根据今天的标准,它提供了良好的安全性并且速度相对较快。对于这种情况,没有必要应用混合加密方案。

现在,你可能会注意到密钥大小不是2的幂次方。这不是一个问题。但是,你应该使用64位的倍数作为密钥大小,因为处理器使用32位和64位的原语来进行实际计算,这样可以提高安全性而不会影响性能。下一个这样的密钥大小将是:

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

以下是一些实验结果,显示一些语言/框架接受的密钥大小(不考虑性能):

  • Golang:1位的倍数且>= 1001(sic!)[使用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。由于我不了解Go语言,很难正确计时。所以我选择了Java和Crypto++。Crypto++的代码可能非常有问题,因为520位和528位密钥的密钥生成速度比其他密钥大小快了七个数量级,而对于小密钥大小窗口来说,这个速度几乎是恒定的。

在Java中,密钥生成相对清晰,生成513位密钥的速度比生成512位密钥慢2-3倍。除此之外,结果几乎是线性的。图表是归一化的,迭代次数为完整的密钥生成-加密-解密循环的1000次。

在Go语言中,加密/解密的密钥大小关系如下:

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

由于这个基准测试没有在Go语言中进行,所以我不会给出任何关于开销可以多小的建议。

英文:

One does not usually calculate the RSA key size based on payload. One simply needs to select one RSA key size based on a compromise between security (bigger is better) and performance (smaller is better). If that is done, use hybrid encryption in conjunction with AES or another symmetric cipher to actually encrypt the data.

If the payload doesn't exceed 300 bytes and you're using OAEP (at least 42 bytes of padding), then you can easily calculate the minimum key size:

(300 + 42) * 8 = 2736 bit

That's already a reasonable size key. It provides good security according to today's norms and is fairly fast. There is no need to apply a hybrid encryption scheme for this.

Now, you may notice that the key size isn't a power of 2. This is not a problem. You should however use a key size that is a multiple of 64 bit, because processors use 32-bit and 64-bit primitives to do the actual calculation, so you can increase the security without a performance penalty. The next such key size would be:

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

Here are some experimental results what some languages/frameworks accept (not performance-wise) as the key size:

  • Golang: multiple of 1 bit and >= 1001 (sic!) [used ideone.com]
  • PyCrypto: multiple of 256 bit and >= 1024 [local install]
  • C#: multiple of 16 bit and >= 512 [used ideone.com]
  • Groovy: multiple of 1 bit and >= 512 [local install]
  • Java: multiple of 1 bit and >= 512 [used ideone.com: Java & Java7]
  • PHP/OpenSSL Ext: multiple of 128 bit and >= 640 [used ideone.com]
  • Crypto++: multiple of 1 bit and >= 16 [local install with maximal validation toughness of 3]

Before you decide to use some kind of specific key size, you should check that all frameworks support that size. As you see, there are vastly varying results.

I tried to write some performance tests of key generation, encryption and decryption with different key sizes: 512, 513, 514, 516, 520, 528, 544, 576. Since I don't know any go, it would be hard to get the timing right. So I settled for Java and Crypto++. The Crypto++ code is probably very buggy, because key generation for 520-bit and 528-bit keys is up to seven orders of magnitude faster than for the other key sizes which is more or less constant for the small key size window.

In Java the key generation was pretty clear in that the generation of a 513-bit key was 2-3 times slower than for a 512-bit key. Other than that the results are nearly linear. The graph is normalized and the numbers of iterations is 1000 for the full keygen-enc-dec cycle.

在Go语言中,加密/解密的密钥大小关系如下:

The decryption makes a little dip at 544-bit which is a multiple of 32-bit. Since it was executed on a 32-bit debian, this might mean that indeed there are some performance improvements, but on the other hand the encryption was slower for that key size.

Since this benchmark wasn't done in Go, I won't give any advice on how small the overhead can be.

huangapple
  • 本文由 发表于 2015年6月1日 23:39:46
  • 转载请务必保留本文链接:https://go.coder-hub.com/30577630.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定