学习Golang的加密和解密

1,829 阅读9分钟

鉴于安全并不总是100%的保证,总是需要保护你的信息,特别是在线数据。通过使用加密技术,我们可以将信息转化为计算机代码,从而防止未经授权的访问。

对于开发者来说,加密对于保护我们应用程序中的数据至关重要。想象一下,如果我们把用户的密码以纯文本形式留在数据库中,而数据库被破坏了;这可能是灾难性的,导致错误的人获得你的信息。

有了加密技术,这种情况就可以避免了。

在本教程中,我们将了解如何在Go中加密和解密数据,使我们的数据在落入坏人手中时难以使用,从而保证数据安全。

Golang加密教程的前提条件

要学习本教程,你必须具备以下条件。

设置Golang项目

为了开始工作,让我们快速设置我们的 Go 项目。

如果你在你的机器上全局安装了 Golang,你可以创建一个文件夹来存放你的 Go 项目。如果你没有全局安装Golang,请在你的Go安装的根文件夹中创建一个文件夹。

这一切都取决于你使用的操作系统和你的 Go 安装方法。

为了确保你的Go在你所在的文件夹中正常工作,在终端运行以下命令。

go version

你会在终端中看到你正在运行的Go的版本。

Current Go Version Running On Local Machine Shown In The Terminal

接下来,创建一个文件夹,并将其插入。

mkdir Encrypt
cd Encrypt

然后你可以通过运行以下命令来启用依赖性跟踪。

go mod init code/encrypt

这将创建一个go.mod 文件。可以把它看作是JavaScript中的package.json 或PHP中的composer.json 。这个go.mod 文件是列出任何Go项目中使用的所有外部模块的地方。

在本教程中,我们不一定需要安装外部依赖,因为Go自带了许多模块,可以生成、加密和解密数据。

在Golang中生成随机数

生成随机数或字符串在编程中很重要,是加密的基础。没有生成随机数,加密就没有用处,加密的数据也是可预测的。

为了在Go中生成随机数,让我们在项目目录中创建一个新的Go文件。

touch numbers.go

接下来,复制并粘贴以下代码到新创建的文件中。

package main
import (
    "fmt"
    "math/rand"
)
func main() {
    fmt.Println(rand.Intn(100))
}

在这里,我们导入了 [fmt](https://pkg.go.dev/fmt) 包来格式化数据,以及math/rand 包来生成随机数。虽然这两个包是Go中内置的,但要注意的是,如果有一个导入的包在你的程序中没有使用,Go将无法成功运行。

额外的main() 函数,这是每个可执行文件的入口,使用rand.Intn() 函数打印一个从0到99的随机整数。

要做到这一点,让我们运行以下程序。

run go numbers.go

在我的例子中,我得到了81。然而,现在的问题是,当我重新运行该程序时,我总是得到81。虽然这在技术上不是一个问题,但它确实违背了每次运行代码时生成一个随机数的目的。

Repeating Random Number, 81, Shown In Terminal

计算机所做的一切都不是简单的随机,它遵循算法。为了解决这个问题,我们必须使用 [Seed()](https://pkg.go.dev/math/rand#Seed) 方法rand 。这在引擎盖下运行,但它把1 作为默认参数。

main() 函数的开头添加以下代码。

rand.Seed(time.Now().UnixNano())

由于我们使用的是时间,所以我们必须导入时间包time.Now().UnixNano() ,它可以为我们提供精确到秒的当前时间,从而改变Seed() 的参数。

因此,当我们现在运行numbers.go 文件时,我们总是得到一个不同的随机数。

我们的代码现在应该看起来像这样。

package main
import (
    "fmt"
    "math/rand"
     "time"
)
func main() {
    rand.Seed(time.Now().UnixNano())
    fmt.Println(rand.Intn(100))
}

然后,我们可以再次运行该代码,最后得到一个在0到99之间的不同的随机数,而不会重复。

run go numbers.go

在Golang中生成随机字符串

为了在Go中生成随机字符串,我们将使用Base64编码和一个外部包,因为这是一种更实用和安全的生成随机数的方式。

首先,在项目的根目录下创建一个名为strings.go 的文件。然后,在说明package main ,告诉Go这是一个可执行文件,接着导入encoding/base64fmt 模块。

package main

import (
    "encoding/base64"
    "fmt"
)

func main() {

    StringToEncode := "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"

    Encoding := base64.StdEncoding.EncodeToString([]byte(StringToEncode))
    fmt.Println(Encoding)                                        
}

通过使用Base64编码,我们现在可以对字符串进行编码和解码。

然后我们接着使用main() 函数,该函数有StringToEncode 变量,也就是我们要加密的字符串。之后,我们调用Base64包中的方法,并传递所创建的需要编码的变量。

运行这个程序会产生以下结果。

为了确保每次都能返回不同的字符串,我们可以使用一个名为randstr 的第三方包。

[randstr](https://github.com/thanhpk/randstr)与使用Seed() 方法相比,它能更快、更好地解决这个问题。要使用该软件包,请下载以下内容。

go get -u github.com/thanhpk/randstr

这增加了一个go.sum 文件,这意味着我们不需要重新安装以前安装的包,因为它把包缓存在里面,并把下载的包的路径提供给go.mod 文件。

为了生成一个随机数,使字符串的长度始终为20个字符,例如,创建一个新文件并粘贴以下代码。

 package main
import(
  "github.com/thanhpk/randstr"
  "fmt"
)
func main() {
    MyString := randstr.String(20)
    fmt.Println(MyString)

}

每次我们运行这个,代码都会重现不同的随机字符串,长度为20个字符。容易吗?当我们生成随机数时,软件包已经处理了大量的播种工作,提供了更干净的代码。

Code Reproduces Random Strings For Encryption, All Are 20 Characters Long

在Golang中加密和解密数据

我们学会了如何生成随机数和字符串,所以我们现在可以学习如何加密和解密数据。

几乎在所有情况下,安全是我们需要了解的主要原因。因此,我们将使用以下模块。 [crypto/aes](https://pkg.go.dev/crypto/aes), [crypto/cipher](https://pkg.go.dev/crypto/cipher),encoding/base64, 和fmt *。*然而,crypto 模块专门借其安全功能来帮助我们的工作。

加密

加密是一种简单的隐藏数据的方法,这样如果数据落入坏人手中就没有用了。为了在Go中进行加密,我们将使用高级加密标准,该标准 [crypto/aes](https://blog.logrocket.com/cryptography-in-go-today/) 提供

首先,创建文件encrypt.go ,并将以下代码粘贴到文件中。

package main
import (
 "crypto/aes"
 "crypto/cipher"
 "encoding/base64"
 "fmt"
)

var bytes = []byte{35, 46, 57, 24, 85, 35, 24, 74, 87, 35, 88, 98, 66, 32, 14, 05}
// This should be in an env file in production
const MySecret string = "abc&1*~#^2^#s0^=)^^7%b34"
func Encode(b []byte) string {
 return base64.StdEncoding.EncodeToString(b)
}


// Encrypt method is to encrypt or hide any classified text
func Encrypt(text, MySecret string) (string, error) {
 block, err := aes.NewCipher([]byte(MySecret))
 if err != nil {
  return "", err
 }
 plainText := []byte(text)
 cfb := cipher.NewCFBEncrypter(block, bytes)
 cipherText := make([]byte, len(plainText))
 cfb.XORKeyStream(cipherText, plainText)
 return Encode(cipherText), nil
}

func main() {
    StringToEncrypt := "Encrypting this string"

    // To encrypt the StringToEncrypt
    encText, err := Encrypt(StringToEncrypt, MySecret)
    if err != nil {
     fmt.Println("error encrypting your classified text: ", err)
    }
    fmt.Println(encText)
}

通过添加随机字节,我们可以把它们作为crypto/cipher 模块方法的参数,NewCFBEncrypter() 。然后,在对字符串进行编码并返回到Base64的Encode 函数之前,有一个MySecret 常数,其中包含加密的秘密。

Encrypt 函数,它需要两个参数,提供要编码的文本和编码的秘密。然后,这将返回Encode() 函数,并传递以Encrypt 为范围定义的cipherText 变量。

通过运行该文件,main 函数与包含要加密的字符串的StringToEncrypt 变量一起执行。当主函数执行时,Encrypt() 函数也会执行,现在有两个参数:StringToEncryptMySecret

运行这段代码会产生以下结果。

StringToEncrypt And MySecret Produce In The Terminal

解密

在成功加密我们的字符串后,我们可以将其解密为原始状态。但为什么我们首先要这样做呢?

其中一个常见的用例是用户的密码,在保存到数据库之前应该对其进行加密。然而,在我们的应用程序中给予用户访问权之前,我们必须始终将其解密。

要做到这一点,我们必须把我们从上一个代码块中得到的加密字符串,Li5E8RFcV/EPZY/neyCXQYjrfa/atA== ,并通过在encrypt.go 文件中添加以下函数来解密它。

func Decode(s string) []byte {
 data, err := base64.StdEncoding.DecodeString(s)
 if err != nil {
  panic(err)
 }
 return data
} 

由于Decode 函数只取一个参数,我们可以在下面的Decrypt 函数中调用它。

// Decrypt method is to extract back the encrypted text
func Decrypt(text, MySecret string) (string, error) {
 block, err := aes.NewCipher([]byte(MySecret))
 if err != nil {
  return "", err
 }
 cipherText := Decode(text)
 cfb := cipher.NewCFBDecrypter(block, bytes)
 plainText := make([]byte, len(cipherText))
 cfb.XORKeyStream(plainText, cipherText)
 return string(plainText), nil
}

Decrypt 函数需要两个字符串参数:text ,这是来自加密数据的文本,以及MySecret ,这是我们已经定义并给定值的变量。

main() 函数中,在fmt.Println(encText) 下面添加以下代码,在下一行打印出加密的文本。

decText, err := Decrypt("Li5E8RFcV/EPZY/neyCXQYjrfa/atA==", MySecret)
 if err != nil {
  fmt.Println("error decrypting your encrypted text: ", err)
 }
 fmt.Println(decText)

最后,我们应该有encrypt.go 中的完整代码。

package main
import (
 "crypto/aes"
 "crypto/cipher"
 "encoding/base64"
 "fmt"
)

var bytes = []byte{35, 46, 57, 24, 85, 35, 24, 74, 87, 35, 88, 98, 66, 32, 14, 05}
// This should be in an env file in production
const MySecret string = "abc&1*~#^2^#s0^=)^^7%b34"
func Encode(b []byte) string {
 return base64.StdEncoding.EncodeToString(b)
}
func Decode(s string) []byte {
 data, err := base64.StdEncoding.DecodeString(s)
 if err != nil {
  panic(err)
 }
 return data
}
// Encrypt method is to encrypt or hide any classified text
func Encrypt(text, MySecret string) (string, error) {
 block, err := aes.NewCipher([]byte(MySecret))
 if err != nil {
  return "", err
 }
 plainText := []byte(text)
 cfb := cipher.NewCFBEncrypter(block, bytes)
 cipherText := make([]byte, len(plainText))
 cfb.XORKeyStream(cipherText, plainText)
 return Encode(cipherText), nil
}
// Decrypt method is to extract back the encrypted text
func Decrypt(text, MySecret string) (string, error) {
 block, err := aes.NewCipher([]byte(MySecret))
 if err != nil {
  return "", err
 }
 cipherText := Decode(text)
 cfb := cipher.NewCFBDecrypter(block, bytes)
 plainText := make([]byte, len(cipherText))
 cfb.XORKeyStream(plainText, cipherText)
 return string(plainText), nil
}
func main() {
 StringToEncrypt := "Encrypting this string"
 // To encrypt the StringToEncrypt
 encText, err := Encrypt(StringToEncrypt, MySecret)
 if err != nil {
  fmt.Println("error encrypting your classified text: ", err)
 }
 fmt.Println(encText)
 // To decrypt the original StringToEncrypt
 decText, err := Decrypt("Li5E8RFcV/EPZY/neyCXQYjrfa/atA==", MySecret)
 if err != nil {
  fmt.Println("error decrypting your encrypted text: ", err)
 }
 fmt.Println(decText)
} 

运行这个程序会对数据进行加密和解密,并会打印出以下内容。

Final Encryption And Decryption In The Terminal

结论

你已经成功地看完了这个。我们涵盖了诸如生成字符串和数字等随机数据的内容,研究了如何使用高级加密标准与Go模块(如crypto/aes,crypto/cipher,encoding/base64 )进行加密。

而且,我们不仅加密了数据,还解密了加密后的数据。

你可以在这里找到整个源代码

The postLearn Golang encryption and decryptionappeared first onLogRocket Blog.