Go语言加密算法

Hash算法:

Hash算法(又称散列算法 散列函数 哈希算法)是把任意长度的输入通过散列算法变换成固定长度的输出.且不可逆的单向密码机制.Hash算法是密码学的重要分支.在数字签名和消息完整性检测等方面有广泛的应用.

1.Hash算法特点:

正向快速:

给定明文和Hash算法.在有限的时间和有限资源内计算出哈希值.

逆向困难:

给定(若干)Hahs值(散列值).在有限时间内很难(基本不可能)逆推出明文.

输入敏感:

原始输入信息修改一点信息.产生的Hash值会有很大不同.

冲突避免:

很难找到两段内容不同的铭文.使得它们的哈希值一致.即使对于任意两个不同的数据块.其hash值相同的可能性极小.

2.应用场景:

文件校验.

数字签名.

鉴权协议.

3.场景的Hash算法:

MD5(消息摘要算法):

一种被广泛使用的密码散列函数.可以产出一个128位(16)字节的散列值.用于确保消息传输完整一致.

SHA-1(安全散列算法):

是一种密码散列函数.由美国国家安全局设计.并由美国国家标准技术研究所发布为联邦资料处理标准.SHA-1可以生成一个被称为消息摘要的160位(20字节)散列值.散列值通常的呈现形式为40十六进制数.

SHA-2(安全散列算法2):

一种密码散列函数算法标准.由美国国家安全局研发.由美国国家标准与技术研究院在2001年发布.属于SHA算法之一.是SHA-1的后继者.其下又可再分为6个不同的算法标准.包括SHA-224 SHA-256 SHA-384 SHA-512 SHA-512/224和SHA-512/256.

RIPEMD(种族完整性原语评估消息摘要):

是一种加密散列函数.RIPEMD是以MD4为基础原则设计的.而且其表现与更有名的SHA-1类似.RIPEMD-160是以原始版RIPEMD所改进的160位版本.而且是RIPEMD系列中最常见的版本之一.

Hash算法示例:

Go语言crypto包含有一些常用的散列函数.例如MD5 SHA-1 SHA-256 SHA512等.以SHA-1算法为例.

swift 复制代码
package main

import (
    "crypto/hmac"
    "crypto/md5"
    "crypto/sha256"
    "crypto/sha512"
    "fmt"
    "io"
)

func main() {
    input := "hello,hash"
    md5_n := md5.New()
    sha_256 := sha256.New()
    sha_512 := sha512.New()
    io.WriteString(md5_n, input)
    sha_256.Write([]byte(input))
    sha_512.Write([]byte(input))
    hmac_512 := hmac.New(sha512.New, []byte("secret"))
    hmac_512.Write([]byte(input))
    fmt.Printf("md5: \t\t %x\n", md5_n.Sum(nil))
    fmt.Printf("sha_256: \t\t %x\n", sha_256.Sum(nil))
    fmt.Printf("sha_512: \t\t %x\n", sha_512.Sum(nil))
    fmt.Printf("hmac_512: \t\t %x\n", hmac_512.Sum(nil))
}

对称与非对称加密算法:

对称加密(也叫私钥加密)是指加密和解密使用相同秘钥的加密算法.有时又叫传统密码算法.就是加密秘钥能够从解密秘钥中推算出来.同时解密秘钥也可以从加密秘钥中推算出来.

对称加密的算法的特点是算法公开 计算量小 加密速度快 加密效率高.不足之处是.交易双方都使用同样的钥匙.安全得不到保证.

数据加密过程:

数据发送方将明文(原始数据)和加密秘钥一起经过特殊加密处理.生成复杂的加密密文进行发送.

数据解密过程:

数据接收方收到密文后.若想读取原数据.则需要使用同样的秘钥及相同算法的逆算法对密文进行解密.才能使其恢复成可读明文.

常见对称加密算法:

DES算法:

DES是美国国家标准局1973年开始研究除国防部外的其他部门的计算机系统的数据加密标准.于1973年5月15日和1974年8月27日先后两次向公众发出了征求加密算法的公告.加密算法要达到的目的如下.

提供高质量的数据保护.防止数据未经授权的泄漏和未被察觉的修改.

具有相当高的复杂性.使得破译的开销超过可能获得利益.同时又要便于理解掌握.

DES的密码体制的安全性不应该依赖于算法的保密.其安全性仅以加密秘钥的保密为基础.

实现经济 运行有效.并且适用于多种完全不同的应用.

DES算法的入口参数有三个.Key Data和Mode.

Key为8个字节共64位.是DES算法的工作秘钥.

Data也为8个字节64位.是要被加密或解密的数据.

Mode为DES的工作方式.有加密或解密两种.

go 复制代码
package main

import (
    "crypto/des"
    "fmt"
)

func main() {
    key := []byte{0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}
    cipher, err := des.NewCipher(key)
    if err != nil {
       fmt.Println(err)
    }
    src := []byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}
    encrptDst := make([]byte, len(src))
    cipher.Encrypt(encrptDst, src)
    fmt.Println(encrptDst)
    plainDst := make([]byte, len(encrptDst))
    cipher.Decrypt(plainDst, encrptDst)
    fmt.Println(plainDst)
}

3DES算法:

3DES是三重数据加密算法块密码的通称.相当于是每个数据块应用3次DES加密算法.

由于计算机运算能力增强.原版DES密码的秘钥长度容易被暴力破解.3DES是设计一种相对简单的方法.即通过增加DES的秘钥长度来避免类似的攻击.而不是设计一种全新的块密码算法.3DES算法的加解密过程分别是对明文/密文进行了3次DES加密或解密.得到相对应的密文的明文.

go 复制代码
package main

import (
    "crypto/des"
    "fmt"
)

func main() {
    key := []byte{0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}
    cipher, err := des.NewTripleDESCipher(key)
    if err != nil {
       fmt.Println(err)
    }
    src := []byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}
    encrptDst := make([]byte, len(src))
    cipher.Encrypt(encrptDst, src)
    fmt.Println(encrptDst)
    plainDst := make([]byte, len(encrptDst))
    cipher.Decrypt(plainDst, encrptDst)
    fmt.Println(plainDst)
}

AES算法:

AES加密算法是美国联邦政府采用的一种区块加密标准.这个标准用来替代原先的DES.

go 复制代码
package main

import (
    "crypto/aes"
    "fmt"
)

func main() {
    key := []byte{0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}
    cipher, err := aes.NewCipher(key)
    if err != nil {
       fmt.Println(err)
    }
    src := []byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}
    encrptDst := make([]byte, len(src))
    cipher.Encrypt(encrptDst, src)
    fmt.Println(encrptDst)
    plainDst := make([]byte, len(encrptDst))
    cipher.Decrypt(plainDst, encrptDst)
    fmt.Println(plainDst)
}

迭代模式:

上面的三种加密算法都是分组密码.每次只能处理特定长度的一块数据.例如DES和3DES能处理的数据长度为8字节.AES为16字节.而日常明文都是大于这个长度的.这就需要将明文的内容进行分组迭代加密.

电子密码本模式:

最简单的模式之一.将明文分组直接作为加密算法的输入.加密算法的输出直接作为密文分组.

密文分组链接模式:

密文之间是链状的.明文分组跟上个密文分组或之后作为加密算法的输入.加密算法的输出作为密文分组.第一个明文分组加密时.需要一个初始化向量.

密文反馈模式:

是上一个密文分组作为下一个加密算法的输入.加密算法的输出与明文分组或结果作为密文分组.第一个明文分组加密时同样需要一个初始化向量.

输出反馈模式:

上一个加密算法的输出作为下一个加密算法的输入.明文与加密算法的输出或作为密文分组.第一个明文分组加密时同样需要一个初始化向量.

计数器模式:

将计数器作为加密算法的输入.加密算法的输出与明文分组或作为密文分组.计数器是累加的.第一个明文分组加密时需要一个初始的计数器值.

go 复制代码
package main

import (
	"crypto/aes"
	"crypto/cipher"
	"fmt"
)

func main() {
	key := []byte{0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}
	cipherBlock, err := aes.NewCipher(key)
	if err != nil {
		fmt.Println(err)
	}
	src := []byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
		0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
		0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
		0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}
	inv := []byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
		0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}
	cbcEncrypter := cipher.NewCBCEncrypter(cipherBlock, inv)
	encrptDst := make([]byte, len(src))
	cbcEncrypter.CryptBlocks(encrptDst, src)
	fmt.Println(encrptDst)

	decrypter := cipher.NewCBCDecrypter(cipherBlock, inv)
	plainDst := make([]byte, len(encrptDst))
	decrypter.CryptBlocks(plainDst, encrptDst)
	fmt.Println(plainDst)
}

非对称加密算法:

非对称加密算法是一种秘钥的保密方法.它需要两个秘钥.一个是公开秘钥(公钥).另一个是私有秘钥(私钥).公钥用于加密.私钥用作解密.使用公钥把明文加密后所得到的密文.只能用相对应的私钥才能解密并得到原本的明文.最初用来加密的公钥不能解密.

非对称加密算法:

主要有RSA算法 Elgamal 背包算法 D-H ECC(椭圆加密算法)等.步骤如下.

1).先获取密钥对对象.

2).获取字符串的公钥和私钥.

3).将字符串的公钥或私钥转换成公钥私钥类对象.

4).使用类对象的公钥进行数据加密.

5).使用类对象的私钥进行解密.

RSA加密示例:

go 复制代码
package main

import (
    "crypto/rand"
    "crypto/rsa"
    "crypto/x509"
    "encoding/base64"
    "encoding/pem"
    "fmt"
    "os"
)

func main() {
    var src = "1234567890"
    cipherText := RsaEncrypt([]byte(src), "./public_key.pem")
    //base64编码输出.
    fmt.Printf("%s\n", base64.StdEncoding.EncodeToString(cipherText))

    //解密.
    plainText := RsaDecrypt(cipherText, "./private_key.pem")
    fmt.Printf("%s\n", plainText)

}

func RsaEncrypt(plainText []byte, keyPath string) []byte {
    data, err := os.ReadFile(keyPath)
    if err != nil {
       panic(fmt.Sprintf("读取公钥文件失败: %v", err))
    }
    block, _ := pem.Decode(data)
    if block == nil {
       panic("公钥文件格式错误,未找到 PEM 数据")
    }
    pub, err := x509.ParsePKIXPublicKey(block.Bytes)
    if err != nil {
       panic(fmt.Sprintf("解析公钥失败: %v", err))
    }
    ciphertext, err := rsa.EncryptPKCS1v15(rand.Reader, pub.(*rsa.PublicKey), plainText)
    if err != nil {
       panic(fmt.Sprintf("RSA 加密失败: %v", err))
    }
    return ciphertext
}

func RsaDecrypt(cipherText []byte, keyPath string) []byte {
    data, err := os.ReadFile(keyPath)
    if err != nil {
       panic(fmt.Sprintf("读取私钥文件失败: %v", err))
    }
    block, _ := pem.Decode(data)
    if block == nil {
       panic("私钥文件格式错误,未找到 PEM 数据")
    }
    // 尝试 PKCS#1 格式
    priv, err := x509.ParsePKCS1PrivateKey(block.Bytes)
    if err != nil {
       // 尝试 PKCS#8 格式
       key, err2 := x509.ParsePKCS8PrivateKey(block.Bytes)
       if err2 != nil {
          panic(fmt.Sprintf("解析私钥失败: %v (PKCS#1: %v)", err2, err))
       }
       priv = key.(*rsa.PrivateKey)
    }
    plaintext, err := rsa.DecryptPKCS1v15(rand.Reader, priv, cipherText)
    if err != nil {
       panic(fmt.Sprintf("RSA 解密失败: %v", err))
    }
    return plaintext
}

公钥私钥生成:

go 复制代码
func main() {
    // 1. 生成 2048 位 RSA 密钥对
    privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
    if err != nil {
       panic(err)
    }
    publicKey := &privateKey.PublicKey

    // 2. 保存私钥(PKCS#1 格式,供 x509.ParsePKCS1PrivateKey 使用)
    privFile, err := os.Create("private_key.pem")
    if err != nil {
       panic(err)
    }
    defer privFile.Close()
    privBytes := x509.MarshalPKCS1PrivateKey(privateKey)
    err = pem.Encode(privFile, &pem.Block{Type: "RSA PRIVATE KEY", Bytes: privBytes})
    if err != nil {
       panic(err)
    }
    fmt.Println("私钥已保存到 private_key.pem")

    // 3. 保存公钥(PKIX 格式,供 x509.ParsePKIXPublicKey 使用)
    pubFile, err := os.Create("public_key.pem")
    if err != nil {
       panic(err)
    }
    defer pubFile.Close()
    pubBytes, err := x509.MarshalPKIXPublicKey(publicKey)
    if err != nil {
       panic(err)
    }
    err = pem.Encode(pubFile, &pem.Block{Type: "PUBLIC KEY", Bytes: pubBytes})
    if err != nil {
       panic(err)
    }
    fmt.Println("公钥已保存到 public_key.pem")

    // 4. 测试加解密(验证密钥可用)
    plaintext := []byte("Hello RSA 测试 1234567890")
    fmt.Printf("原始数据: %s\n", plaintext)

    // 加密
    ciphertext, err := rsa.EncryptPKCS1v15(rand.Reader, publicKey, plaintext)
    if err != nil {
       panic(err)
    }
    fmt.Printf("加密结果(base64): %x\n", ciphertext)

    // 解密
    decrypted, err := rsa.DecryptPKCS1v15(rand.Reader, privateKey, ciphertext)
    if err != nil {
       panic(err)
    }
    fmt.Printf("解密结果: %s\n", decrypted)

    // 5. 提示如何与你的加解密函数配合
    fmt.Println("\n=== 密钥文件已生成,可用于你的加解密程序 ===")
}

椭圆曲线加密算法:

椭圆曲线密码学(ECC)是一种基于数学椭圆曲线的公开秘钥加密算法.ECC的主要优势是在某些情况下它比它算法(如RSA算法)使用更小的秘钥并提供相当或更高的等级安全.ECC的另一个优势是可以定义群之间的双线映射.基于Weil对或是Tate对.

go 复制代码
package main

import (
    "crypto/aes"
    "crypto/cipher"
    "crypto/ecdh"
    "crypto/rand"
    "crypto/rsa"
    "crypto/sha256"
    "crypto/x509"
    "encoding/base64"
    "encoding/pem"
    "errors"
    "fmt"
    "io"
    "os"
)

var curve = ecdh.P256()

// 椭圆曲线密钥对,
func GenerateKeyPair() (privateKey *ecdh.PrivateKey, publicKey *ecdh.PublicKey, err error) {
    privateKey, err = curve.GenerateKey(rand.Reader)
    if err != nil {
       return nil, nil, err
    }
    publicKey = privateKey.PublicKey()
    return privateKey, publicKey, nil
}

func Encrypt(publicKey *ecdh.PublicKey, message []byte) ([]byte, error) {
    //生成临时秘钥对.
    ephemeralPrivate, err := curve.GenerateKey(rand.Reader)
    if err != nil {
       return nil, err
    }
    ephemeralPublic := ephemeralPrivate.PublicKey()
    //edch共享秘钥.
    sharedSecret, err := ephemeralPrivate.ECDH(publicKey)
    if err != nil {
       return nil, err
    }
    //使用 KDF (SHA-256) 派生对称加密密钥 (32字节用于 AES-256)
    kdf := sha256.Sum256(sharedSecret)
    symmetricKey := kdf[:]
    //生成随机 nonce
    nonce := make([]byte, 12)
    if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
       return nil, err
    }
    //AES-GCM 加密
    block, err := aes.NewCipher(symmetricKey)
    if err != nil {
       return nil, err
    }
    aesGCM, err := cipher.NewGCM(block)
    if err != nil {
       return nil, err
    }
    ciphertext := aesGCM.Seal(nil, nonce, message, nil)
    // 序列化: 临时公钥 + nonce + 密文
    ephemeralPubBytes := ephemeralPublic.Bytes()
    result := make([]byte, 0, len(ephemeralPubBytes)+len(nonce)+len(ciphertext))
    result = append(result, ephemeralPubBytes...)
    result = append(result, nonce...)
    result = append(result, ciphertext...)
    return result, nil
}

func Decrypt(privateKey *ecdh.PrivateKey, ciphertext []byte) ([]byte, error) {
    //解析密文: 临时公钥(65字节) + nonce(12字节) + 实际密文
    if len(ciphertext) < 65+12 {
       return nil, errors.New("ciphertext too short")
    }
    ephemeralPubBytes := ciphertext[:65]
    nonce := ciphertext[65:77]
    encryptedData := ciphertext[77:]

    //还原临时公钥
    ephemeralPublic, err := curve.NewPublicKey(ephemeralPubBytes)
    if err != nil {
       return nil, err
    }

    // ECDH 共享密钥
    sharedSecret, err := privateKey.ECDH(ephemeralPublic)
    if err != nil {
       return nil, err
    }

    // 使用相同 KDF 派生对称密钥
    kdf := sha256.Sum256(sharedSecret)
    symmetricKey := kdf[:]

    //AES-GCM 解密
    block, err := aes.NewCipher(symmetricKey)
    if err != nil {
       return nil, err
    }
    aesGCM, err := cipher.NewGCM(block)
    if err != nil {
       return nil, err
    }
    plaintext, err := aesGCM.Open(nil, nonce, encryptedData, nil)
    if err != nil {
       return nil, err
    }
    return plaintext, nil
}

func main() {
    receiverPrivate, receiverPublic, err := GenerateKeyPair()
    if err != nil {
       panic(err)
    }
    fmt.Println("=== 椭圆曲线加密算法演示 (ECIES) ===")
    fmt.Printf("接收方私钥: %x\n", receiverPrivate.Bytes())
    fmt.Printf("接收方公钥: %x\n", receiverPublic.Bytes())

    // 原始消息
    originalMessage := []byte("Hello, 椭圆曲线加密!")
    fmt.Printf("\n原始消息: %s\n", originalMessage)

    // 加密
    ciphertext, err := Encrypt(receiverPublic, originalMessage)
    if err != nil {
       panic(err)
    }
    fmt.Printf("密文 (长度 %d 字节): %x\n", len(ciphertext), ciphertext)

    // 解密
    decryptedMessage, err := Decrypt(receiverPrivate, ciphertext)
    if err != nil {
       panic(err)
    }
    fmt.Printf("解密后消息: %s\n", decryptedMessage)

    // 验证
    if string(decryptedMessage) == string(originalMessage) {
       fmt.Println("\n✓ 加解密成功,消息完整一致。")
    } else {
       fmt.Println("\n✗ 加解密失败。")
    }
}

语雀地址www.yuque.com/itbosunmian...?

《Go.》 密码:xbkk 欢迎大家访问.提意见.

相关推荐
青云计划2 小时前
数据库的守护者-单飞锁
后端
神奇小汤圆2 小时前
每次重启能救下几十万个请求:Cloudflare 如何用 Rust 实现零停机升级
后端
用户298698530142 小时前
Java 统计 Word 文档中的单词数量
java·后端
fliter3 小时前
为什么我要杀掉 syn:Rust 编译速度之战与 unsynn 的诞生
后端
huzhongqiang3 小时前
扩展 Python 事件机制:支持等待事件消失
后端·python
_Evan_Yao3 小时前
大学自学能力怎么练?慕课、B站、书籍资源清单
后端·学习
SimonKing3 小时前
从惊艳到踩坑:AI结对编程的真实复盘
java·后端·程序员
咖啡八杯3 小时前
GoF设计模式——原型模式
java·后端·设计模式·原型模式
IT_陈寒3 小时前
Python多线程居然不加速?这个坑我踩得明明白白
前端·人工智能·后端