鉴于安全并不总是100%的保证,总是需要保护你的信息,特别是在线数据。通过使用加密技术,我们可以将信息转化为计算机代码,从而防止未经授权的访问。
对于开发者来说,加密对于保护我们应用程序中的数据至关重要。想象一下,如果我们把用户的密码以纯文本形式留在数据库中,而数据库被破坏了;这可能是灾难性的,导致错误的人获得你的信息。
有了加密技术,这种情况就可以避免了。
在本教程中,我们将了解如何在Go中加密和解密数据,使我们的数据在落入坏人手中时难以使用,从而保证数据安全。
Golang加密教程的前提条件
要学习本教程,你必须具备以下条件。
- 在你的机器上安装了Golang
- 对Go的基本了解
- 一个命令终端
- 一个文本编辑器
设置Golang项目
为了开始工作,让我们快速设置我们的 Go 项目。
如果你在你的机器上全局安装了 Golang,你可以创建一个文件夹来存放你的 Go 项目。如果你没有全局安装Golang,请在你的Go安装的根文件夹中创建一个文件夹。
这一切都取决于你使用的操作系统和你的 Go 安装方法。
为了确保你的Go在你所在的文件夹中正常工作,在终端运行以下命令。
go version
你会在终端中看到你正在运行的Go的版本。

接下来,创建一个文件夹,并将其插入。
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。虽然这在技术上不是一个问题,但它确实违背了每次运行代码时生成一个随机数的目的。

计算机所做的一切都不是简单的随机,它遵循算法。为了解决这个问题,我们必须使用 [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/base64 和fmt 模块。
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个字符。容易吗?当我们生成随机数时,软件包已经处理了大量的播种工作,提供了更干净的代码。

在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() 函数也会执行,现在有两个参数:StringToEncrypt 和MySecret 。
运行这段代码会产生以下结果。

解密
在成功加密我们的字符串后,我们可以将其解密为原始状态。但为什么我们首先要这样做呢?
其中一个常见的用例是用户的密码,在保存到数据库之前应该对其进行加密。然而,在我们的应用程序中给予用户访问权之前,我们必须始终将其解密。
要做到这一点,我们必须把我们从上一个代码块中得到的加密字符串,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)
}
运行这个程序会对数据进行加密和解密,并会打印出以下内容。

结论
你已经成功地看完了这个。我们涵盖了诸如生成字符串和数字等随机数据的内容,研究了如何使用高级加密标准与Go模块(如crypto/aes,crypto/cipher,encoding/base64 )进行加密。
而且,我们不仅加密了数据,还解密了加密后的数据。
The postLearn Golang encryption and decryptionappeared first onLogRocket Blog.