使用AES的秘密密钥加密
#安全 #encryption #go #cryptography

我需要使用秘密密钥来加密和解密任意数据(字符串或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/base64encoding/hex或类似的东西对其进行编码。

当然,本示例不应像真实的任何东西一样使用。在一个现实世界项目中,人们可能应该使用更好/更容易的库来加密/解密敏感信息,但是通过弄清楚如何使用GO的标准库以及使用对称的加密原始图(例如块密码和块模式),您可以理解关于他们彼此相关的更多信息。

再次,我绝不是安全专家。这篇文章只是我分享我的学习,所以如果我错过了什么,您不了解或其他一般反馈!

,请在评论中告诉我!

感谢您的阅读和美好的一天!