Go 中 encryption/decryption 的源文本、键大小关系
Source text, key size relationship for encryption/decryption in Go
在下面的代码中(也在 http://play.golang.org/p/77fRvrDa4A 但在浏览器中使用 "too long to process")sourceText 的 124 字节版本不会加密因为:"message too long for RSA public key size" of 1024。它和更长的 124 字节 sourceText 版本,使用 2048 位密钥大小。
我的问题是如何在给定源文本的字节长度的情况下准确计算 rsa.GenerateKey 中的密钥大小? (一小段文字在4096键大小下需要将近10秒,直到运行时我才知道sourceText的长度。)
在 上对此进行了非常简短的讨论,但我不太清楚,因为我不是加密专家。
我的目标是加密、存储在数据库中并解密大约 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
}
人们通常不会根据负载计算 RSA 密钥大小。人们只需要 select 一个基于安全性(越大越好)和性能(越小越好)之间的折衷的 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
以下是一些实验结果,一些 languages/frameworks 接受(不是性能方面的)作为密钥大小:
- Golang:1 位的倍数并且 >= 1001(原文如此!)[使用 ideone.com]
- PyCrypto:256 位的倍数且 >= 1024 [本地安装]
- C#:16 位的倍数且 >= 512 [已使用 ideone.com]
- Groovy: 1 位的倍数且 >= 512 [本地安装]
- Java: 1 位的倍数且 >= 512 [used ideone.com: Java & Java7]
- PHP/OpenSSL 分机:128 位的倍数且 >= 640 [已使用 ideone.com]
- Crypto++:1 位的倍数且 >= 16 [本地安装,最大验证强度为
3
]
在您决定使用某种特定的密钥大小之前,您应该检查是否所有框架都支持该大小。如您所见,结果差异很大。
我尝试写了一些密钥生成、加密和解密的性能测试,使用不同的密钥大小:512、513、514、516、520、528、544、576。由于我不知道任何去,它很难把握好时机。所以我选择了 Java 和 Crypto++。 Crypto++ 代码可能有很多错误,因为 520 位和 528 位密钥的密钥生成比其他密钥大小快七个数量级,而其他密钥大小对于小密钥大小来说或多或少是恒定的 window .
在 Java 中,密钥生成非常清楚,因为 513 位密钥的生成比 512 位密钥的生成慢 2-3 倍 .除此之外,结果几乎是线性的。该图已标准化,整个 keygen-enc-dec 循环的迭代次数为 1000。
解密在 544 位处略有下降,这是 32 位的倍数。由于它是在 32 位 debian 上执行的,这可能意味着确实有一些性能改进,但另一方面,对于该密钥大小,加密速度较慢。
由于这个基准不是在 Go 中完成的,所以我不会就开销可以有多小给出任何建议。
在下面的代码中(也在 http://play.golang.org/p/77fRvrDa4A 但在浏览器中使用 "too long to process")sourceText 的 124 字节版本不会加密因为:"message too long for RSA public key size" of 1024。它和更长的 124 字节 sourceText 版本,使用 2048 位密钥大小。
我的问题是如何在给定源文本的字节长度的情况下准确计算 rsa.GenerateKey 中的密钥大小? (一小段文字在4096键大小下需要将近10秒,直到运行时我才知道sourceText的长度。)
在 上对此进行了非常简短的讨论,但我不太清楚,因为我不是加密专家。
我的目标是加密、存储在数据库中并解密大约 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
}
人们通常不会根据负载计算 RSA 密钥大小。人们只需要 select 一个基于安全性(越大越好)和性能(越小越好)之间的折衷的 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
以下是一些实验结果,一些 languages/frameworks 接受(不是性能方面的)作为密钥大小:
- Golang:1 位的倍数并且 >= 1001(原文如此!)[使用 ideone.com]
- PyCrypto:256 位的倍数且 >= 1024 [本地安装]
- C#:16 位的倍数且 >= 512 [已使用 ideone.com]
- Groovy: 1 位的倍数且 >= 512 [本地安装]
- Java: 1 位的倍数且 >= 512 [used ideone.com: Java & Java7]
- PHP/OpenSSL 分机:128 位的倍数且 >= 640 [已使用 ideone.com]
- Crypto++:1 位的倍数且 >= 16 [本地安装,最大验证强度为
3
]
在您决定使用某种特定的密钥大小之前,您应该检查是否所有框架都支持该大小。如您所见,结果差异很大。
我尝试写了一些密钥生成、加密和解密的性能测试,使用不同的密钥大小:512、513、514、516、520、528、544、576。由于我不知道任何去,它很难把握好时机。所以我选择了 Java 和 Crypto++。 Crypto++ 代码可能有很多错误,因为 520 位和 528 位密钥的密钥生成比其他密钥大小快七个数量级,而其他密钥大小对于小密钥大小来说或多或少是恒定的 window .
在 Java 中,密钥生成非常清楚,因为 513 位密钥的生成比 512 位密钥的生成慢 2-3 倍 .除此之外,结果几乎是线性的。该图已标准化,整个 keygen-enc-dec 循环的迭代次数为 1000。
解密在 544 位处略有下降,这是 32 位的倍数。由于它是在 32 位 debian 上执行的,这可能意味着确实有一些性能改进,但另一方面,对于该密钥大小,加密速度较慢。
由于这个基准不是在 Go 中完成的,所以我不会就开销可以有多小给出任何建议。