温馨跳转链接:模块二:go语言–区块链学习(二)-CSDN博客
保护隐私和数据安全:在信息传输过程中,不加密的数据可以被未经授权的人员截获和查看,这可能导致个人隐私泄露、商业机密被窃取或者敏感数据被篡改。通过加密数据,可以确保只有授权的人员能够解密和访问数据,提高了数据的安全性。
防止数据篡改:在信息传输过程中,数据可能会被篡改或修改,这可能导致信息内容的损坏或误导。通过加密数据,可以在接收方验证数据的完整性,确保传输的数据没有被篡改。
防止重放攻击:重放攻击是指攻击者拦截并重放先前传输的数据,以达到欺骗或恶意目的。通过加密数据并使用防重放技术,可以防止攻击者复用先前的数据来进行攻击。
遵守法律和合规要求:根据一些行业标准、法律法规和合规要求,某些类型的数据在传输过程中必须进行加密,以确保数据的安全性和保密性。加密数据可以帮助组织遵守相关的法律和合规要求。
举例:
小明–>小红:
加密三要素共同构成了加密系统,其中密钥的保护和管理非常重要,不当的密钥管理会导致加密系统的破解和信息泄露。
Go语言的encoding/base64包提供了对base64编码和解码的支持。base64是一种将二进制数据转换为可打印ASCII字符的编码方式,常用于在网络传输中以文本形式表示二进制数据。
该包中的主要函数有以下几个:
使用encoding/base64包进行base64编码和解码非常简单,可以按照以下步骤进行操作:
通过encoding/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)
dbuf, err := base64.StdEncoding.DecodeString(buf)
if err != nil {
fmt.Println(err)
return
}
fmt.Println(string(dbuf))
代码:
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))
}
}
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
数据分块:首先将二进制数据划分为固定大小的块。每个块通常包含特定数量的位,通常为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字符集将字符串中的每个字符转换回其对应的十进制值。
十进制转二进制转换:从字符转换中获得的十进制值然后被转换回二进制数据。
重建二进制数据:然后将上一步中的二进制数据连接起来以重建原始的二进制数据。现在可以将这些数据用于各种目的,例如数据反序列化或加密操作。
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))
}
如:一般网站的账号密码会经过哈希加密后存储到数据库中,以保护用户的账号密码安全。
需要注意的是,虽然MD5、SHA-1、SHA-224、SHA-256、SHA-384和SHA-512在某些场景下仍可用于非安全性需求,但对于安全性要求较高的应用,建议使用更强大的哈希算法,如SHA-256或SHA-512。
需要引入第三方包(失败,请配置环境变量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
}
代码:
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
}
代码:
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
}
代码:
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 // 返回去除填充的明文
}
代码:
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
}
代码:
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
}
RSA(Rivest, Shamir, Adleman):基于大数因子分解的数学问题,广泛应用于安全通信和数字签名。
ECC(Elliptic Curve Cryptography):基于椭圆曲线离散对数问题的加密算法,提供与RSA相当的安全性但计算成本较低。
Go标准库中并没有提供直接的ECDSA加密和解密函数。ECDSA主要用于数字签名,而不是进行加密和解密操作。
代码:
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编码
。
数字签名是一种用于验证数据完整性和身份认证的技术。它基于公钥密码学的原理,通过使用私钥对数据进行加密生成签名,然后使用对应的公钥对签名进行解密验证。
数字签名的过程如下:
通过数字签名,接收者可以验证数据的完整性,并确保数据来自于拥有私钥的发送者。即使在数据传输过程中被篡改,也能通过验证步骤的不匹配来检测到数据的篡改。
数字签名在信息安全领域有广泛的应用,例如:
代码:
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
}
代码:
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
}