温馨跳转链接:模块二:go语言–区块链学习(二)-CSDN博客
模块一:密码学
1.1 密码介绍
1.1.1 为什么要加密 ?
-
保护隐私和数据安全:在信息传输过程中,不加密的数据可以被未经授权的人员截获和查看,这可能导致个人隐私泄露、商业机密被窃取或者敏感数据被篡改。通过加密数据,可以确保只有授权的人员能够解密和访问数据,提高了数据的安全性。
-
防止数据篡改:在信息传输过程中,数据可能会被篡改或修改,这可能导致信息内容的损坏或误导。通过加密数据,可以在接收方验证数据的完整性,确保传输的数据没有被篡改。
-
防止重放攻击:重放攻击是指攻击者拦截并重放先前传输的数据,以达到欺骗或恶意目的。通过加密数据并使用防重放技术,可以防止攻击者复用先前的数据来进行攻击。
-
遵守法律和合规要求:根据一些行业标准、法律法规和合规要求,某些类型的数据在传输过程中必须进行加密,以确保数据的安全性和保密性。加密数据可以帮助组织遵守相关的法律和合规要求。
举例:
小明–>小红:
- 原文:你好啊,可以加个好友吗
- 密钥:+2
- 密文:请问你儿童好雨哦啊怕是,地方可规划以接口加两种个形参好那么友请问吗
- 密钥(密码):-2
- 解密:请问你儿童好雨哦啊怕是,地方可规划以接口加两种个形参好那么友请问吗
- 你好啊,可以加个好友吗
1.1.2 常见的几种加密算法
- 编码解码:是将信息从一种形式转换为另一种形式的过程。编码通常用于将可读文本转换为二进制格式,以便在计算机之间传输或存储数据时使用。解码是将已编码的信息还原为原始形式的过程。
- 哈希加密:也称为散列函数,是一种通过将任意长度的消息压缩为固定长度的摘要或哈希值的加密方法。哈希值是唯一的,并且由输入数据完全决定。但是,由于不同的消息可以生成相同的哈希值,因此哈希加密并不是完全安全的加密方式。
- 对称加密:是指使用相同的密钥进行加密和解密的加密方式。这意味着发送方和接收方都必须知道密钥。对称加密速度快,但密钥需要在发送和接收之间传递。
- 非对称加密:是指使用一对密钥(公钥和私钥)进行加密和解密的加密方式。公钥可公开,而私钥则必须保密。非对称加密安全性高,但速度较慢。
- 数字签名:数字签名是一种加密技术,用于确保数字文档的完整性和身份验证。它使用非对称加密来创建一个数字签名,该签名包含文档的摘要和私钥。通过验证数字签名,可以确定文档是否被篡改,并且签名者是合法的。
1.1.3 加密三要素
- 明文/密文:明文是未经过加密的数据,而密文是已经进行加密转换的数据。
- 密钥:密钥是用于加密和解密数据的秘密值。密钥的安全性决定了数据的安全性。对称加密使用相同的密钥进行加密和解密,而非对称加密则使用一对密钥(公钥和私钥)进行加密和解密。
- 加密算法/解密算法:加密算法是用于将明文转换为密文的数学函数。解密算法是用于将密文转换回明文的逆函数。加密算法的安全性越高,破解难度就越大。
加密三要素共同构成了加密系统,其中密钥的保护和管理非常重要,不当的密钥管理会导致加密系统的破解和信息泄露。
1.2 编码解码
1.2.1 常见的几种编码
- base64:26个小写字母、26个大写字母、10个数字、/、+
- base58(区块链):去掉6个容易混淆的,去掉0,大写的O、大写的I、小写的L、/、+
- /、+影响双击选择
1.2.2 go实现base64编码、解码
Go语言的encoding/base64包提供了对base64编码和解码的支持。base64是一种将二进制数据转换为可打印ASCII字符的编码方式,常用于在网络传输中以文本形式表示二进制数据。
该包中的主要函数有以下几个:
- func Encode(dst, src []byte):将给定的字节切片src进行base64编码,并将结果存储在dst中。返回编码后的字节数。
- func Decode(dst, src []byte) (n int, err error):将给定的base64编码的src进行解码,并将解码结果存储在dst中。返回解码后的字节数。
- func EncodeToString(src []byte) string:将给定的字节切片src进行base64编码,并返回编码后的字符串。
- func DecodeString(s string) ([]byte, error):将给定的base64编码的字符串s进行解码,并返回解码后的字节切片。
使用encoding/base64包进行base64编码和解码非常简单,可以按照以下步骤进行操作:
- 导入包:import “encoding/base64”
- 编码:调用base64包的EncodeToString函数,将要编码的数据作为参数传入,得到base64编码后的字符串。
- 解码:调用base64包的DecodeString函数,将要解码的base64字符串作为参数传入,得到解码后的字节切片。
通过encoding/base64包,可以方便地进行base64编码和解码,常用于处理二进制数据在文本传输中的需求,如图像、音频等文件的传输与存储。
- base64编码
src := []byte("hello")
buf := base64.StdEncoding.EncodeToString(src)
// 在url中使用
src := []byte("http://127.0.0.1:8080/?name=hello")
buf := base64.URLEncoding.EncodeToString(src)
- base64解码
dbuf, err := base64.StdEncoding.DecodeString(buf)
if err != nil {
fmt.Println(err)
return
}
fmt.Println(string(dbuf))
代码:文章来源:https://www.toymoban.com/news/detail-812837.html
package main
import (
"encoding/base64"
"fmt"
)
func main() {
{
src := []byte("hello")
buf := base64.StdEncoding.EncodeToString(src)
dbuf, err := base64.StdEncoding.DecodeString(buf)
if err != nil {
fmt.Println(err)
return
}
fmt.Println(string(dbuf))
}
{
src := []byte("http://127.0.0.1:8080/?name=hello")
buf := base64.URLEncoding.EncodeToString(src)
dbuf, err := base64.URLEncoding.DecodeString(buf)
if err != nil {
fmt.Println(err)
return
}
fmt.Println(string(dbuf))
}
}
1.2.3 go实现base58编码、解码
base58编码表
Value | Char | Value | Char | Value | Char | Value | Char |
---|---|---|---|---|---|---|---|
0 | 1 | 15 | G | 30 | X | 45 | n |
1 | 2 | 16 | H | 31 | Y | 46 | o |
2 | 3 | 17 | J | 32 | Z | 47 | p |
3 | 4 | 18 | K | 33 | a | 48 | q |
4 | 5 | 19 | L | 34 | b | 49 | r |
5 | 6 | 20 | M | 35 | c | 50 | s |
6 | 7 | 21 | N | 36 | d | 51 | t |
7 | 8 | 22 | P | 37 | e | 52 | u |
8 | 9 | 23 | Q | 38 | f | 53 | v |
9 | A | 24 | R | 39 | g | 54 | w |
10 | B | 25 | S | 40 | h | 55 | x |
11 | C | 26 | T | 41 | i | 56 | y |
12 | D | 27 | U | 42 | j | 57 | z |
13 | E | 28 | V | 43 | k | ||
14 | F | 29 | W | 44 | m |
字符1代表0,字符2代表1,…,字符z代表57
- base58编码:
-
数据分块:首先将二进制数据划分为固定大小的块。每个块通常包含特定数量的位,通常为8位(1字节)大小。这些块一个接一个地进行处理。
-
二进制转十进制转换:将每个二进制数据块转换为一个十进制值。此转换通过将二进制数据解释为基于256的整数来执行(因为每个字节有256个可能的值)。
-
映射到base58字符集:然后将在上一步中获得的十进制值映射到base58字符集中对应的字符。此映射是通过反复将十进制值除以58并使用余数作为索引从base58集合中选择字符来完成的。
-
构建编码字符串:从映射过程中获得的字符被连接在一起以形成base58编码字符串。
-
处理前导零:在某些情况下,原始二进制数据中的前导零可能导致编码字符串中出现前导的 ‘1’ 字符。这些前导的 ‘1’ 字符通常被省略或替换为特殊字符(例如 ‘1’),以保持可读性。
const (
b58Alphabet = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
)
func Base58Encode(input []byte) string {
var result strings.Builder
// 将待编码的字节数组input转换成大整数,如:hello(104 101 108 108 111) --> 104*256^4 + 101*256^3 + 108*256^2 + 108*256^1 + 111*256^0 = 448378203247
x := new(big.Int).SetBytes(input)
base := big.NewInt(58)
zero := big.NewInt(0)
// 对大整数x进行遍历,每次将x除以58,得到商和余数。将余数映射到Base58字母表中,并将结果存储在字符串构建器result中。不断重复该过程,直到商为0
for x.Cmp(zero) > 0 {
mod := new(big.Int)
x.DivMod(x, base, mod)
result.WriteByte(b58Alphabet[mod.Int64()])
}
// 处理前导0:对输入的字节数组进行遍历,统计前导的0的个数。根据Base58编码规则,在编码后的字符串中会添加相应数量的前导1
for _, b := range input {
if b != 0 {
break
}
result.WriteByte(b58Alphabet[0])
}
// 将result中的字符串反转,得到最终的Base58编码结果
reversedResult := result.String()
reversedResultBytes := []byte(reversedResult)
for i, j := 0, len(reversedResultBytes)-1; i < j; i, j = i+1, j-1 {
reversedResultBytes[i], reversedResultBytes[j] = reversedResultBytes[j], reversedResultBytes[i]
}
return string(reversedResultBytes)
}
- base58解码:
-
字符转换为十进制:要解码base58编码的字符串,需要根据base58字符集将字符串中的每个字符转换回其对应的十进制值。
-
十进制转二进制转换:从字符转换中获得的十进制值然后被转换回二进制数据。
-
重建二进制数据:然后将上一步中的二进制数据连接起来以重建原始的二进制数据。现在可以将这些数据用于各种目的,例如数据反序列化或加密操作。
func Base58Decode(input string) []byte {
result := big.NewInt(0)
base := big.NewInt(58)
zeroBytes := 0
// 处理前导0:对输入的字符串进行遍历,统计前导的0的个数,直到遇到第一个非0的字符为止
for _, b := range input {
if byte(b) == b58Alphabet[0] {
zeroBytes++
} else {
break
}
}
// 对输入字符串去除前导的0后,对其余字符进行遍历。通过在Base58字母表中查找当前字符所在的位置,得到该字符的数值,并将其乘以58的n次方(n为字符所在的位置),累加到result中
payload := input[zeroBytes:]
for _, b := range []byte(payload) {
charIndex := strings.IndexByte(b58Alphabet, b)
if charIndex == -1 {
return []byte{}
}
result.Mul(result, base)
result.Add(result, big.NewInt(int64(charIndex)))
}
// 将result转换成字节数组,并在前面添加zeroBytes个0,得到最终的解码结果
resultBytes := result.Bytes()
resultBytes = append(bytes.Repeat([]byte{byte(0)}, zeroBytes), resultBytes...)
return resultBytes
}
代码:
package main
import (
"bytes"
"fmt"
"math/big"
"strings"
)
const (
b58Alphabet = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
)
func Base58Encode(input []byte) string {
var result strings.Builder
x := new(big.Int).SetBytes(input)
base := big.NewInt(58)
zero := big.NewInt(0)
for x.Cmp(zero) > 0 {
mod := new(big.Int)
x.DivMod(x, base, mod)
result.WriteByte(b58Alphabet[mod.Int64()])
}
// Add leading 1's for zeros in the input
for _, b := range input {
if b != 0 {
break
}
result.WriteByte(b58Alphabet[0])
}
// Reverse the result
reversedResult := result.String()
reversedResultBytes := []byte(reversedResult)
for i, j := 0, len(reversedResultBytes)-1; i < j; i, j = i+1, j-1 {
reversedResultBytes[i], reversedResultBytes[j] = reversedResultBytes[j], reversedResultBytes[i]
}
return string(reversedResultBytes)
}
func Base58Decode(input string) []byte {
result := big.NewInt(0)
base := big.NewInt(58)
zeroBytes := 0
for _, b := range input {
if byte(b) == b58Alphabet[0] {
zeroBytes++
} else {
break
}
}
payload := input[zeroBytes:]
for _, b := range []byte(payload) {
charIndex := strings.IndexByte(b58Alphabet, b)
if charIndex == -1 {
return []byte{}
}
result.Mul(result, base)
result.Add(result, big.NewInt(int64(charIndex)))
}
resultBytes := result.Bytes()
resultBytes = append(bytes.Repeat([]byte{byte(0)}, zeroBytes), resultBytes...)
return resultBytes
}
func main() {
input := []byte("hello")
encoded := Base58Encode(input)
fmt.Println("Encoded:", encoded)
decoded := Base58Decode(encoded)
fmt.Println("Decoded:", string(decoded))
}
1.3 哈希算法
1.3.1 特点
- 固定长度输出:哈希算法将任意长度的输入数据映射为固定长度的输出,通常以固定位数的二进制数或十六进制字符串表示。这使得哈希算法在需要固定长度标识和比较数据的场景中非常有用。
- 单向性(不可逆):哈希算法是一种单向函数,也就是说,从哈希值无法推导出原始数据。给定一个哈希值,很难通过逆运算还原出原始数据。这种特性使得哈希算法在密码学中被广泛应用于加密、数字签名和身份验证等领域。
- 高效性:哈希算法的计算速度通常很快。对于给定的输入数据,哈希算法能够迅速生成对应的哈希值。这使得哈希算法在大规模数据处理、密码校验和数据索引等方面得到广泛应用。
- 雪崩效应(防篡改):哈希算法具有雪崩效应,即输入数据的微小变化会导致输出哈希值的巨大变化。这意味着稍微改变输入数据的任何部分,都会导致生成的哈希值完全不同。这种特性使得哈希算法在校验数据完整性和防止冲突等方面非常有用。
- 碰撞概率:由于哈希算法的输出空间是有限的,不同的输入数据可能会生成相同的哈希值,这被称为碰撞。好的哈希算法应该使得碰撞的概率非常低,即使在大量数据的情况下也能保持碰撞的概率较小。
如:一般网站的账号密码会经过哈希加密后存储到数据库中,以保护用户的账号密码安全。
1.3.2 常用的几种哈希算法
- MD4(Message Digest Algorithm 4):由Ron Rivest于1990年设计的一种哈希算法,已被认为不安全,不推荐使用。
- MD5(Message Digest Algorithm 5):也是由Ron Rivest设计的一种哈希算法,常用于校验数据完整性和密码存储。然而,由于其存在碰撞漏洞,不适合用于安全性要求较高的场景。
- SHA-1(Secure Hash Algorithm 1):由美国国家安全局(NSA)设计的一种哈希算法,常用于校验数据完整性和数字签名。然而,由于其存在碰撞漏洞,安全性逐渐被破坏,不再推荐使用。
- SHA-224:SHA-224是SHA-256的一个变种,生成224位的哈希值。
- SHA-256:SHA-256是SHA-2系列中的一种哈希算法,生成256位的哈希值。目前被广泛使用,能够提供较高的安全性。
- SHA-384:SHA-384是SHA-2系列中的一种哈希算法,生成384位的哈希值。相比于SHA-256,SHA-384具有更大的输出长度,提供更高的安全性。
- SHA-512:SHA-512是SHA-2系列中的一种哈希算法,生成512位的哈希值。与SHA-256相比,SHA-512在安全性方面更强。
需要注意的是,虽然MD5、SHA-1、SHA-224、SHA-256、SHA-384和SHA-512在某些场景下仍可用于非安全性需求,但对于安全性要求较高的应用,建议使用更强大的哈希算法,如SHA-256或SHA-512。
1.3.3 go实现哈希算法举例
1.3.3.1 go实现md4加密
需要引入第三方包(失败,请配置环境变量GOPROXY=https://goproxy.cn)
go get golang.org/x/crypto/md4
代码:
package main
import (
"encoding/hex"
"fmt"
"golang.org/x/crypto/md4"
)
func Md4Encrypt(str string) string {
// 创建一个 md4.New() 对象,该对象用于计算 MD4 哈希值
hasher := md4.New()
// 将输入的字符串转换为字节数组,并将其写入哈希对象中
hasher.Write([]byte(str))
// 调用 hasher.Sum(nil) 完成哈希计算
hash := hasher.Sum(nil)
// 将哈希值转换为十六进制字符串
return hex.EncodeToString(hash)
}
func main() {
str := "hello, world!"
fmt.Println(Md4Encrypt(str)) // 输出:03cc95120b718b883b96bce3706d64b7
}
1.3.3.2 go实现md5加密
代码:
package main
import (
"crypto/md5"
"encoding/hex"
"fmt"
)
func Md5Encrypt(str string) string {
// 创建一个 md5.New() 对象,该对象用于计算 MD5 哈希值
hasher := md5.New()
// 将输入的字符串转换为字节数组,并将其写入哈希对象中
hasher.Write([]byte(str))
// 调用 hasher.Sum(nil) 完成哈希计算
hash := hasher.Sum(nil)
// 将哈希值转换为十六进制字符串
return hex.EncodeToString(hash)
}
func main() {
str := "hello, world!"
fmt.Println(Md5Encrypt(str)) // 输出:3adbbad1791fbae3ec908894c4963870
}
1.3.3.3 go实现sha256加密
代码:
package main
import (
"crypto/sha256"
"encoding/hex"
"fmt"
)
func Sha256Encrypt(str string) string {
// 创建一个 sha256.New() 对象,该对象用于计算 SHA-256 哈希值
hasher := sha256.New()
// 将输入的字符串转换为字节数组,并将其写入哈希对象中
hasher.Write([]byte(str))
// 调用 hasher.Sum(nil) 完成哈希计算
hash := hasher.Sum(nil)
// 将哈希值转换为十六进制字符串
return hex.EncodeToString(hash)
}
func main() {
str := "hello, world!"
fmt.Println(Sha256Encrypt(str)) // 输出:68e656b251e67e8358bef8483ab0d51c6619f3e7a1a9f0e75838d41ff368f728
}
1.4 对称加密
1.4.1 特点
- 加密和解密使用相同的密钥。
- 加密速度较快,适合处理大量数据。
- 对称加密算法通常较简单。
1.4.2 优点
- 加密和解密的速度快,适用于大量数据的加密和传输。
- 实现简单,计算成本低。
1.4.3 缺点
- 密钥的管理和分发可能存在安全风险。
- 需要在通信双方之间事先共享密钥。
1.4.4 场景分析
- 对称加密适用于需要快速加密和解密大量数据的场景,如文件加密、数据库加密等。
- 由于密钥的管理和分发存在风险,对称加密更适用于双方已经建立了安全信任关系的场景。
1.4.5 常见的对称加密方式
- DES(Data Encryption Standard):使用56位密钥,已经被更安全的算法所取代。
- 3DES(Triple Data Encryption Algorithm):对DES进行三次加密以增加安全性。
- AES(Advanced Encryption Standard):目前最常用的对称加密算法,支持128、192和256位密钥。
1.4.5.1 go实现des加密
代码:
package main
import (
"bytes"
"crypto/cipher"
"crypto/des"
"encoding/base64"
"fmt"
)
func main() {
key := []byte("01234567") // DES密钥,8字节
plaintext := []byte("Hello, DES!") // 明文数据
// 调用encrypt()函数对明文进行加密,返回密文ciphertext和可能的错误err
ciphertext, err := encrypt(key, plaintext)
if err != nil {
fmt.Println("加密失败:", err)
return
}
// 使用Base64编码将密文转换为字符串并打印出来
fmt.Printf("加密结果: %s\n", base64.StdEncoding.EncodeToString(ciphertext))
// 调用decrypt()函数对密文进行解密,返回解密后的明文decryptedText和可能的错误err
decryptedText, err := decrypt(key, ciphertext)
if err != nil {
fmt.Println("解密失败:", err)
return
}
// 打印解密后的明文
fmt.Printf("解密结果: %s\n", decryptedText)
}
// 定义pad()函数,用于对数据进行填充以适应加密算法的块大小
func pad(data []byte, blockSize int) []byte {
padding := blockSize - len(data)%blockSize // 计算需要填充的字节数padding
padText := bytes.Repeat([]byte{byte(padding)}, padding) // 使用bytes.Repeat()函数创建一个字节数组,其中每个元素都是padding的值
return append(data, padText...) // 将填充的数据追加到原始数据后面并返回
}
// 定义unpad()函数,用于去除填充数据
func unpad(data []byte) []byte {
padding := data[len(data)-1] // 获取最后一个字节作为填充字节数padding
return data[:len(data)-int(padding)] // 返回去除填充后的数据
}
// 定义encrypt()函数,用于进行DES加密
func encrypt(key, plaintext []byte) ([]byte, error) {
block, err := des.NewCipher(key) // 创建DES实例
if err != nil {
return nil, err
}
blockSize := block.BlockSize() // 获取加密算法的块大小
plaintext = pad(plaintext, blockSize) // 调用pad()函数对明文进行填充
ciphertext := make([]byte, blockSize+len(plaintext)) // 创建一个字节数组ciphertext,长度为块大小加上填充后的明文长度
iv := ciphertext[:blockSize] // 将ciphertext的前块大小个字节作为初始向量iv
mode := cipher.NewCBCEncrypter(block, iv) // 创建CBC模式的加密器mode
mode.CryptBlocks(ciphertext[blockSize:], plaintext) // 使用加密器对填充后的明文进行加密,并将结果保存到ciphertext中
return ciphertext, nil // 返回加密后的密文
}
// 定义decrypt()函数,用于进行DES解密
func decrypt(key, ciphertext []byte) ([]byte, error) {
block, err := des.NewCipher(key) // 创建DES实例
if err != nil {
return nil, err
}
blockSize := block.BlockSize() // 获取解密算法的块大小
if len(ciphertext) < blockSize { // 检查密文的长度是否小于块大小
return nil, fmt.Errorf("ciphertext too short")
}
iv := ciphertext[:blockSize] // 将密文的前块大小个字节作为初始向量iv
ciphertext = ciphertext[blockSize:] // 截取密文中除去初始向量部分的数据
if len(ciphertext)%blockSize != 0 { // 检查密文长度是否是块大小的整数倍
return nil, fmt.Errorf("ciphertext is not a multiple of the block size")
}
plaintext := make([]byte, len(ciphertext)) // 创建一个字节数组plaintext,用于保存解密后的明文
mode := cipher.NewCBCDecrypter(block, iv) // 创建CBC模式的解密器mode
mode.CryptBlocks(plaintext, ciphertext) // 使用解密器对密文进行解密,并将结果保存到plaintext中
return unpad(plaintext), nil // 返回去除填充的明文
}
1.4.5.2 go实现3des加密
代码:
package main
import (
"bytes"
"crypto/cipher"
"crypto/des"
"fmt"
)
func main() {
key := []byte("0123456789abcdef01234567") // 3DES密钥,24字节
plaintext := []byte("Hello, 3DES!") // 明文数据
ciphertext, err := encrypt(key, plaintext)
if err != nil {
fmt.Println("加密失败:", err)
return
}
fmt.Printf("加密结果: %x\n", ciphertext)
decryptedText, err := decrypt(key, ciphertext)
if err != nil {
fmt.Println("解密失败:", err)
return
}
fmt.Printf("解密结果: %s\n", decryptedText)
}
func pad(data []byte, blockSize int) []byte {
padding := blockSize - len(data)%blockSize
padText := bytes.Repeat([]byte{byte(padding)}, padding)
return append(data, padText...)
}
func unpad(data []byte) []byte {
padding := data[len(data)-1]
return data[:len(data)-int(padding)]
}
func encrypt(key, plaintext []byte) ([]byte, error) {
block, err := des.NewTripleDESCipher(key)
if err != nil {
return nil, err
}
blockSize := block.BlockSize()
plaintext = pad(plaintext, blockSize)
ciphertext := make([]byte, blockSize+len(plaintext))
iv := make([]byte, blockSize)
mode := cipher.NewCBCEncrypter(block, iv)
mode.CryptBlocks(ciphertext[blockSize:], plaintext)
return ciphertext, nil
}
func decrypt(key, ciphertext []byte) ([]byte, error) {
block, err := des.NewTripleDESCipher(key)
if err != nil {
return nil, err
}
blockSize := block.BlockSize()
if len(ciphertext) < blockSize {
return nil, fmt.Errorf("ciphertext too short")
}
iv := ciphertext[:blockSize]
ciphertext = ciphertext[blockSize:]
if len(ciphertext)%blockSize != 0 {
return nil, fmt.Errorf("ciphertext is not a multiple of the block size")
}
plaintext := make([]byte, len(ciphertext))
mode := cipher.NewCBCDecrypter(block, iv)
mode.CryptBlocks(plaintext, ciphertext)
return unpad(plaintext), nil
}
1.4.5.3 go实现aes加密
代码:
package main
import (
"bytes"
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"encoding/base64"
"fmt"
"io"
)
func main() {
key := []byte("0123456789abcdef") // AES-128密钥,16字节
plaintext := []byte("Hello, AES!") // 明文数据
ciphertext, err := encrypt(key, plaintext)
if err != nil {
fmt.Println("加密失败:", err)
return
}
fmt.Printf("加密结果: %s\n", base64.StdEncoding.EncodeToString(ciphertext))
decryptedText, err := decrypt(key, ciphertext)
if err != nil {
fmt.Println("解密失败:", err)
return
}
fmt.Printf("解密结果: %s\n", decryptedText)
}
func pad(data []byte, blockSize int) []byte {
padding := blockSize - len(data)%blockSize
padText := bytes.Repeat([]byte{byte(padding)}, padding)
return append(data, padText...)
}
func unpad(data []byte) []byte {
padding := data[len(data)-1]
return data[:len(data)-int(padding)]
}
func encrypt(key, plaintext []byte) ([]byte, error) {
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
blockSize := block.BlockSize()
plaintext = pad(plaintext, blockSize)
ciphertext := make([]byte, blockSize+len(plaintext))
iv := ciphertext[:blockSize]
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
return nil, err
}
mode := cipher.NewCBCEncrypter(block, iv)
mode.CryptBlocks(ciphertext[blockSize:], plaintext)
return ciphertext, nil
}
func decrypt(key, ciphertext []byte) ([]byte, error) {
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
blockSize := block.BlockSize()
if len(ciphertext) < blockSize {
return nil, fmt.Errorf("ciphertext too short")
}
iv := ciphertext[:blockSize]
ciphertext = ciphertext[blockSize:]
if len(ciphertext)%blockSize != 0 {
return nil, fmt.Errorf("ciphertext is not a multiple of the block size")
}
plaintext := make([]byte, len(ciphertext))
mode := cipher.NewCBCDecrypter(block, iv)
mode.CryptBlocks(plaintext, ciphertext)
return unpad(plaintext), nil
}
1.5 非对称加密
1.5.1 特点
- 加密和解密使用不同的密钥。
- 加密速度较慢,适合处理少量数据。
- 非对称加密算法通常较复杂。
1.5.2 优点
- 密钥的分发和管理相对较简单,无需事先共享密钥。
- 提供了更高的安全性,可以用于身份验证和数字签名。
1.5.3 缺点
- 加密和解密的速度较慢,不适合处理大量数据。
- 计算成本较高。
1.5.4 场景分析
- 非对称加密适用于需要较高安全性和身份验证的场景,如安全传输密钥、数字签名等。
- 密钥的分发和管理相对较简单,适用于未建立安全信任关系的场景。
1.5.5 常见的非对称加密方式
-
RSA(Rivest, Shamir, Adleman):基于大数因子分解的数学问题,广泛应用于安全通信和数字签名。
-
ECC(Elliptic Curve Cryptography):基于椭圆曲线离散对数问题的加密算法,提供与RSA相当的安全性但计算成本较低。
Go标准库中并没有提供直接的ECDSA加密和解密函数。ECDSA主要用于数字签名,而不是进行加密和解密操作。
1.5.5.1 go实现rsa加密
代码:
package main
import (
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"encoding/pem"
"fmt"
"os"
)
func main() {
// 生成RSA密钥对
privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
fmt.Println("生成RSA密钥对失败:", err)
return
}
// 保存私钥到文件
err = savePrivateKeyToFile(privateKey, "private.pem")
if err != nil {
fmt.Println("保存私钥到文件失败:", err)
return
}
fmt.Println("私钥已保存到 private.pem 文件")
// 获取公钥
publicKey := &privateKey.PublicKey
// 保存公钥到文件
err = savePublicKeyToFile(publicKey, "public.pem")
if err != nil {
fmt.Println("保存公钥到文件失败:", err)
return
}
fmt.Println("公钥已保存到 public.pem 文件")
// 使用公钥加密
plaintext := []byte("Hello, RSA!")
ciphertext, err := encryptWithPublicKey(plaintext, publicKey)
if err != nil {
fmt.Println("使用公钥加密失败:", err)
return
}
fmt.Printf("加密结果: %x\n", ciphertext)
// 使用私钥解密
decryptedText, err := decryptWithPrivateKey(ciphertext, privateKey)
if err != nil {
fmt.Println("使用私钥解密失败:", err)
return
}
fmt.Println("解密结果:", string(decryptedText))
}
// 保存私钥到文件
func savePrivateKeyToFile(privateKey *rsa.PrivateKey, filename string) error {
privateKeyBytes := x509.MarshalPKCS1PrivateKey(privateKey)
privateKeyPEM := pem.EncodeToMemory(
&pem.Block{
Type: "RSA PRIVATE KEY",
Bytes: privateKeyBytes,
},
)
return os.WriteFile(filename, privateKeyPEM, 0600)
}
// 保存公钥到文件
func savePublicKeyToFile(publicKey *rsa.PublicKey, filename string) error {
publicKeyBytes, err := x509.MarshalPKIXPublicKey(publicKey)
if err != nil {
return err
}
publicKeyPEM := pem.EncodeToMemory(
&pem.Block{
Type: "PUBLIC KEY",
Bytes: publicKeyBytes,
},
)
return os.WriteFile(filename, publicKeyPEM, 0644)
}
// 使用公钥加密
func encryptWithPublicKey(plaintext []byte, publicKey *rsa.PublicKey) ([]byte, error) {
ciphertext, err := rsa.EncryptPKCS1v15(rand.Reader, publicKey, plaintext)
if err != nil {
return nil, err
}
return ciphertext, nil
}
// 使用私钥解密
func decryptWithPrivateKey(ciphertext []byte, privateKey *rsa.PrivateKey) ([]byte, error) {
plaintext, err := rsa.DecryptPKCS1v15(rand.Reader, privateKey, ciphertext)
if err != nil {
return nil, err
}
return plaintext, nil
}
privateKeyBytes := x509.MarshalPKCS1PrivateKey(privateKey) 这行代码的作用是将给定的RSA私钥(*rsa.PrivateKey类型)转换为PKCS1
格式的字节数组。
PKCS1
是一个公钥密码标准,定义了一种在RSA加密算法中使用的密钥格式。它定义了如何在二进制形式和ASCII码形式之间进行RSA密钥的编码和解码,以及如何对密钥进行加密和解密。
在Go语言中,可以使用x509标准库
的MarshalPKCS1PrivateKey函数
将RSA私钥转换为PKCS1格式
的字节数组。这个函数接受一个*rsa.PrivateKey类型
的私钥作为参数,并返回一个字节数组表示该私钥的PKCS1编码
。
publicKeyBytes, err := x509.MarshalPKIXPublicKey(publicKey) 这行代码的作用是将给定的RSA公钥(*rsa.PublicKey类型)转换为PKIX
格式的字节数组。
PKIX
是一种公钥基础设施标准,定义了证书和公钥的格式和交互方式。它提供了一种通用的公钥证书结构,适用于多种密码算法和应用场景。
在Go语言中,可以使用x509标准库
的MarshalPKIXPublicKey函数
将RSA公钥转换为PKIX格式
的字节数组。这个函数接受一个*rsa.PublicKey类型
的公钥作为参数,并返回一个字节数组表示该公钥的PKIX编码
。
1.6 数字签名
1.6.1 数字签名介绍
数字签名是一种用于验证数据完整性和身份认证的技术。它基于公钥密码学的原理,通过使用私钥对数据进行加密生成签名,然后使用对应的公钥对签名进行解密验证。
数字签名的过程如下:
- 数据发送者使用私钥对要签名的数据进行加密运算,生成签名。
- 数据发送者将签名与原始数据一起发送给接收者。
- 数据接收者使用相同的公钥对签名进行解密运算,得到原始数据的哈希值。
- 数据接收者计算接收到的原始数据的哈希值。
- 数据接收者将两个哈希值进行比较,如果相等,则说明数据完整且未被篡改,并可以确认发送者的身份。
通过数字签名,接收者可以验证数据的完整性,并确保数据来自于拥有私钥的发送者。即使在数据传输过程中被篡改,也能通过验证步骤的不匹配来检测到数据的篡改。
数字签名在信息安全领域有广泛的应用,例如:
- 网络通信:用于保护数据传输的完整性和身份认证。
- 数字证书:用于认证网站和服务的可信度。
- 文件完整性验证:用于验证文件在传输或存储过程中是否被修改。
- 数字版权保护:用于验证数字内容的真实性和完整性。
1.6.2 go实现数字签名
- 私钥签名
- 公钥验证签名
1.6.2.1 go实现rsa数字签名
代码:
package main
import (
"crypto"
"crypto/rand"
"crypto/rsa"
"crypto/sha256"
"crypto/x509"
"encoding/hex"
"encoding/pem"
"errors"
"fmt"
"os"
)
func main() {
// 生成RSA密钥对
privateKey, publicKey, err := generateRSAKeyPair(2048)
if err != nil {
fmt.Println("生成RSA密钥对失败:", err)
return
}
// 保存私钥到文件
err = savePrivateKeyToFile(privateKey, "private.pem")
if err != nil {
fmt.Println("保存私钥到文件失败:", err)
return
}
fmt.Println("私钥已保存到 private.pem 文件")
// 保存公钥到文件
err = savePublicKeyToFile(publicKey, "public.pem")
if err != nil {
fmt.Println("保存公钥到文件失败:", err)
return
}
fmt.Println("公钥已保存到 public.pem 文件")
// 要签名的数据
message := "Hello, RSA!"
// 计算消息的散列值
hashed := sha256.Sum256([]byte(message))
// 使用私钥进行签名
signature, err := signWithPrivateKey(hashed[:], privateKey)
if err != nil {
fmt.Println("签名失败:", err)
return
}
// 将签名的结果转换为16进制字符串
signatureHex := hex.EncodeToString(signature)
fmt.Printf("签名结果: %s\n", signatureHex)
// 使用公钥进行验证
err = verifyWithPublicKey(hashed[:], signature, publicKey)
if err != nil {
fmt.Println("验证签名失败:", err)
return
}
fmt.Println("签名验证通过")
}
// 生成RSA密钥对
func generateRSAKeyPair(keySize int) (*rsa.PrivateKey, *rsa.PublicKey, error) {
privateKey, err := rsa.GenerateKey(rand.Reader, keySize)
if err != nil {
return nil, nil, err
}
publicKey := &privateKey.PublicKey
return privateKey, publicKey, nil
}
// 保存私钥到文件
func savePrivateKeyToFile(privateKey *rsa.PrivateKey, filename string) error {
privateKeyBytes := x509.MarshalPKCS1PrivateKey(privateKey)
privateKeyPEM := pem.EncodeToMemory(
&pem.Block{
Type: "RSA PRIVATE KEY",
Bytes: privateKeyBytes,
},
)
return os.WriteFile(filename, privateKeyPEM, 0600)
}
// 保存公钥到文件
func savePublicKeyToFile(publicKey *rsa.PublicKey, filename string) error {
publicKeyBytes, err := x509.MarshalPKIXPublicKey(publicKey)
if err != nil {
return err
}
publicKeyPEM := pem.EncodeToMemory(
&pem.Block{
Type: "PUBLIC KEY",
Bytes: publicKeyBytes,
},
)
return os.WriteFile(filename, publicKeyPEM, 0644)
}
// 使用私钥进行签名
func signWithPrivateKey(message []byte, privateKey *rsa.PrivateKey) ([]byte, error) {
signature, err := rsa.SignPKCS1v15(rand.Reader, privateKey, crypto.SHA256, message)
if err != nil {
return nil, err
}
return signature, nil
}
// 使用公钥进行验证
func verifyWithPublicKey(message, signature []byte, publicKey *rsa.PublicKey) error {
err := rsa.VerifyPKCS1v15(publicKey, crypto.SHA256, message, signature)
if err != nil {
return errors.New("签名验证失败")
}
return nil
}
1.6.2.2 go实现ecc数字签名
代码:文章来源地址https://www.toymoban.com/news/detail-812837.html
package main
import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/sha256"
"crypto/x509"
"encoding/asn1"
"encoding/hex"
"encoding/pem"
"errors"
"fmt"
"math/big"
"os"
)
func main() {
// 生成公钥和私钥
privateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
fmt.Println("生成私钥失败:", err)
return
}
// 保存私钥到文件
err = savePrivateKeyToFile(privateKey, "private.pem")
if err != nil {
fmt.Println("保存私钥到文件失败:", err)
return
}
fmt.Println("私钥已保存到 private.pem 文件")
// 保存公钥到文件
publicKey := &privateKey.PublicKey
err = savePublicKeyToFile(publicKey, "public.pem")
if err != nil {
fmt.Println("保存公钥到文件失败:", err)
return
}
fmt.Println("公钥已保存到 public.pem 文件")
// 要签名的数据
message := "Hello, ECC!"
// 计算消息的散列值
hashed := sha256.Sum256([]byte(message))
// 使用私钥进行签名
signature, err := signWithPrivateKey(hashed[:], privateKey)
if err != nil {
fmt.Println("签名失败:", err)
return
}
// 将签名的结果转换为16进制字符串
signatureHex := hex.EncodeToString(signature)
fmt.Printf("签名结果: %s\n", signatureHex)
// 使用公钥进行验证
err = verifyWithPublicKey(hashed[:], signature, publicKey)
if err != nil {
fmt.Println("验证签名失败:", err)
return
}
fmt.Println("签名验证通过")
}
// 保存私钥到文件
func savePrivateKeyToFile(privateKey *ecdsa.PrivateKey, filename string) error {
privateKeyBytes, err := x509.MarshalECPrivateKey(privateKey)
if err != nil {
return err
}
privateKeyPEM := pem.EncodeToMemory(&pem.Block{
Type: "EC PRIVATE KEY",
Bytes: privateKeyBytes,
})
return os.WriteFile(filename, privateKeyPEM, 0600)
}
// 保存公钥到文件
func savePublicKeyToFile(publicKey *ecdsa.PublicKey, filename string) error {
publicKeyBytes, err := x509.MarshalPKIXPublicKey(publicKey)
if err != nil {
return err
}
publicKeyPEM := pem.EncodeToMemory(&pem.Block{
Type: "PUBLIC KEY",
Bytes: publicKeyBytes,
})
return os.WriteFile(filename, publicKeyPEM, 0644)
}
// 使用私钥进行签名
func signWithPrivateKey(message []byte, privateKey *ecdsa.PrivateKey) ([]byte, error) {
r, s, err := ecdsa.Sign(rand.Reader, privateKey, message)
if err != nil {
return nil, err
}
signature, err := asn1.Marshal(ecdsaSignature{r, s})
if err != nil {
return nil, err
}
return signature, nil
}
// 使用公钥进行验证
func verifyWithPublicKey(message, signature []byte, publicKey *ecdsa.PublicKey) error {
var sig ecdsaSignature
_, err := asn1.Unmarshal(signature, &sig)
if err != nil {
return err
}
if !ecdsa.Verify(publicKey, message, sig.R, sig.S) {
return errors.New("签名验证失败")
}
return nil
}
type ecdsaSignature struct {
R, S *big.Int
}
到了这里,关于go语言--区块链学习(一)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!