我需要使用秘密密钥来加密和解密任意数据(字符串或JSON有效载荷或其他任何)的能力时,正在从事个人GO项目。本文是我在尝试实现这一目标的最后几天学习的结果。
tl; dr
秘密键(或对称)加密需要使用块等密码,例如AES。 AES本身只能加密/解密16字节长数据(这是其块大小),因此需要使用块密码模式。
GCM块密码模式是众多模式之一。这是一种使用AE的方法,可以使我们能够对任意大小的数据进行加密/解密。它还添加了消息身份验证(Integrition)。
完整的工作代码示例是here。
加密引物
潜水之前,我想定义一些密码学术语。
-
secret key
基本上是交流方(机器,您和您的朋友...或任何组合)之间的价值。 )。
-
Plaintext
是您要加密的文本,因此除非有人拥有秘密钥匙,否则没有人可以阅读它。 -
Ciphertext
是明文的加密结果。 -
cipher
是完成加密和解密数据的实际工作的算法。
我们在这里谈论的是称为Symmetric encryption
(也称为Secret key encryption
),我们使用相同的密钥将我们的纯文本加密到密文,并将密文将ciphertext恢复为宣传。
密码类型
现在让我们谈谈密码。对称密码属于两个主要类别:
- 流密封器:通过一次加密授权的每个位(或字节),生成Ciphertext。
- 和块密码:通过加密明文的固定尺寸块进行操作。
流和块密码是更复杂和偏见的加密公用事业(例如Mac,Hash函数,对称键数字签名方案等)的构建块(无需双关语)。
现在让我们以一个块密码的一个例子,然后进一步深入研究:AES,Advanded Gypryption Standard。
aes是一个块密码,可采用固定尺寸的钥匙和固定尺寸的明文,并返回固定大小的密文。 AES具有基于秘密钥匙长度的三个变体,所有这些变体。
秘密钥匙长度 | aes变体 | 块大小 |
---|---|---|
16个字节(128位) | AES-128 | 16个字节(128位) |
24个字节(192位) | AES-192 | 16个字节(128位) |
32个字节(25 6bits) | AES-256 | 16个字节(128位) |
由于AES的块大小设置为16个字节,因此明文必须至少16个字节。这给我们带来了问题,因为我们希望能够加密/解密任意大小的数据。
让我们现在把它放在一边,让我们看看一个使用AES加密数据的示例。
让我们尝试一下。
package main
import (
"crypto/aes"
"fmt"
)
var (
// We're using a 32 byte long secret key
secretKey string = "N1PCdw3M2B1TfJhoaY2mL736p2vCUc47"
)
func encrypt(plaintext string) (string) {
aes, err := aes.NewCipher([]byte(secretKey))
if err != nil {
panic(err)
}
// Make a buffer the same length as plaintext
ciphertext := make([]byte, len(plaintext))
aes.Encrypt(ciphertext, []byte(plaintext))
return string(ciphertext)
}
func main() {
// This will successfully encrypt.
ciphertext := encrypt("This is some sensitive information")
fmt.Printf("Ciphertext: %x \n", ciphertext)
// This will cause an error since the
// plaintext is less than 16 bytes.
ciphertext = encrypt("Hello")
}
块密码模式
现在回到我们的问题:仅使用AES就不足以获得我们想要的东西:加密任意大小的消息。
这是块密码模式(或操作模式)进入的地方。块密码模式是一种使用块密码来解决特定问题的方法。有很多可用的块密码模式,其中大多数解决了“如何加密任意大小的消息” 的问题。那正是我们想要的!
我绝不是密码学专家,所以我不能,也不会比较块密码模式或深入研究他们的细节。但这是我们需要承受的东西:
单独阻止密码有两个限制:
- 他们只能加密/解密固定尺寸的数据
- 他们只提供机密性,这意味着一个看密文的人不可能在不知道秘密钥匙 的情况下将其恢复为宣传。
创建了块密码模式来解决这两个限制:
- 所有块密码模式都求解尺寸的限制。
- 有些模式只能解决尺寸的限制,而没有其他方式,例如欧洲央行,CBC和Ctr。
- 创建了其他模式以添加身份验证或消息完整性(这些称为组合模式),例如:CCM,GCM&TKW。
通过使用也提供身份验证的一种模式,我们实际上使用了身份验证的加密,而不是块加密。这是获取完整图片的回顾:
- AES是一个块密码,可以使我们机密。这是 *块加密 *。
- 单独使用AES时,您只能对16个字节长(AES块的大小)进行加密/解密数据。
- 例如,将AES与CBC模式使用,例如,ALEVIAT尺寸限制。这也是块加密。
- 使用与GCM(组合模式)使用AES的AES可实现尺寸限制,但也为我们提供了消息身份验证(完整性)。这是身份验证的加密。
好吧,我们将使用GCM模式BACASE,这是最广泛采用的对称块密码模式之一。 GCM需要应随机生成的IV(初始化向量)(此处使用的术语是nounce
,几乎相同)。我们只是在示例中使用一个随机字符串。
使用AES与GCM
让我们看看我们如何做到这一点:
package main
import (
"crypto/aes"
"crypto/cipher"
"fmt"
)
var (
// We're using a 32 byte long secret key
secretKey string = "N1PCdw3M2B1TfJhoaY2mL736p2vCUc47"
// We need a nounce for GCM. Standard size is 12 bytes.
nounce string = "rqhv3CDSDz5G"
)
func encrypt(plaintext string) (string) {
aes, err := aes.NewCipher([]byte(secretKey))
if err != nil {
panic(err)
}
gcm, err := cipher.NewGCM(aes)
if err != nil {
panic(err)
}
ciphertext := gcm.Seal(nil, []byte(nounce), []byte(plaintext), nil)
return string(ciphertext)
}
func decrypt(ciphertext string) (string) {
aes, err := aes.NewCipher([]byte(secretKey))
if err != nil {
panic(err)
}
gcm, err := cipher.NewGCM(aes)
if err != nil {
panic(err)
}
plaintext, err := gcm.Open(nil, []byte(nounce), []byte(ciphertext), nil)
if err != nil {
panic(err)
}
return string(plaintext)
}
func main() {
// This will successfully encrypt & decrypt
ciphertext1 := encrypt("This is some sensitive information")
fmt.Printf("Encrypted ciphertext 1: %x \n", ciphertext1)
plaintext1 := decrypt(ciphertext1)
fmt.Printf("Decrypted plaintext 1: %s \n", plaintext1)
// This will successfully encrypt & decrypt as well.
ciphertext2 := encrypt("Hello")
fmt.Printf("Encrypted ciphertext 2: %x \n", ciphertext2)
plaintext2 := decrypt(ciphertext2)
fmt.Printf("Decrypted plaintext 2: %s \n", plaintext2)
}
您可以看到,我们现在可以对任何任意大小的数据进行加密和解密!如果要将Ciphertext存储在某个地方,则必须用encoding/base64
或encoding/hex
或类似的东西对其进行编码。
当然,本示例不应像真实的任何东西一样使用。在一个现实世界项目中,人们可能应该使用更好/更容易的库来加密/解密敏感信息,但是通过弄清楚如何使用GO的标准库以及使用对称的加密原始图(例如块密码和块模式),您可以理解关于他们彼此相关的更多信息。
再次,我绝不是安全专家。这篇文章只是我分享我的学习,所以如果我错过了什么,您不了解或其他一般反馈!
,请在评论中告诉我!感谢您的阅读和美好的一天!