首页 > 其他分享 >go语言签发和验证license

go语言签发和验证license

时间:2024-07-28 16:06:15浏览次数:15  
标签:license err nil 签发 crypto pem go panic

生成非对称密钥

package main

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

// MakePem 生成 PEM 文件
func MakePem() {
	// 生成 RSA 密钥对
	privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
	if err != nil {
		panic(err)
	}

	// 将私钥写入文件
	privateFile, err := os.Create("private.pem")
	if err != nil {
		panic(err)
	}
	defer privateFile.Close()
	privateBytes := x509.MarshalPKCS1PrivateKey(privateKey)
	privateBlock := &pem.Block{
		Type:  "RSA PRIVATE KEY",
		Bytes: privateBytes,
	}
	if err := pem.Encode(privateFile, privateBlock); err != nil {
		panic(err)
	}

	// 提取公钥并写入文件
	publicKey := &privateKey.PublicKey
	publicBytes, err := x509.MarshalPKIXPublicKey(publicKey)
	if err != nil {
		panic(err)
	}
	publicBlock := &pem.Block{
		Type:  "RSA PUBLIC KEY",
		Bytes: publicBytes,
	}
	publicFile, err := os.Create("public.pem")
	if err != nil {
		panic(err)
	}
	defer publicFile.Close()
	if err := pem.Encode(publicFile, publicBlock); err != nil {
		panic(err)
	}

	println("密钥生成成功")
}

func main() {
	MakePem()
}

使用私钥来加密license

这里与其他非对称加密还不同,其他地方基本都是公钥加密,然后私钥解密

但是这里是私钥加密,然后公钥解密

这主要是为了保证license无法被篡改,而被解密倒相对次要一点

package main

import (
	"bytes"
	"crypto"
	"crypto/rand"
	"crypto/rsa"
	"crypto/sha256"
	"crypto/x509"
	"encoding/gob"
	"encoding/pem"
	"fmt"
	"os"
	"time"
)

var (
	Username = "admin"
	Secret   = "secret-key-123"
)

type License struct {
	Username   string    `json:"username"`            // 用户名
	Secret     string    `json:"secret"`              // 密钥
	Expiration time.Time `json:"expiration"`          // 到期时间
	Signature  []byte    `json:"signature,omitempty"` // 签名
}

// 签名许可证
func signLicense(license *License, privateKey *rsa.PrivateKey) ([]byte, error) {
	licenseCopy := *license
	licenseCopy.Signature = nil // 签名前移除签名字段

	// 使用gob编码
	var licenseData []byte
	buffer := new(bytes.Buffer)
	encoder := gob.NewEncoder(buffer)
	if err := encoder.Encode(licenseCopy); err != nil {
		return nil, err
	}
	licenseData = buffer.Bytes()

	hash := sha256.Sum256(licenseData)
	signature, err := rsa.SignPKCS1v15(rand.Reader, privateKey, crypto.SHA256, hash[:])
	if err != nil {
		return nil, err
	}
	return signature, nil
}

var (
	license = &License{
		Username:   Username,
		Secret:     Secret,
		Expiration: time.Now().Add(30 * 24 * time.Hour), // 30 天后过期
	}
)

func MakeLicense() {
	// 读取私钥
	privateKeyData, err := os.ReadFile("private.pem")
	if err != nil {
		panic(err)
	}
	privateBlock, _ := pem.Decode(privateKeyData)
	privateKey, err := x509.ParsePKCS1PrivateKey(privateBlock.Bytes)
	if err != nil {
		panic(err)
	}

	// 签名许可证
	signature, err := signLicense(license, privateKey)
	if err != nil {
		panic(err)
	}
	license.Signature = signature

	// 将许可证编码为二进制格式
	file, err := os.Create("license.lic")
	if err != nil {
		panic(err)
	}
	defer file.Close()

	encoder := gob.NewEncoder(file)
	if err := encoder.Encode(license); err != nil {
		panic(err)
	}

	fmt.Println("可证文件生成成功")
}

func main() {
	MakeLicense()
}

这里的license信息比较简单,只有用户名,用户密钥,有效时间以及license的签名,也可以自己更具业务需要添加其他信息

使用公钥校验license

package main

import (
	"bytes"
	"crypto"
	"crypto/rsa"
	"crypto/sha256"
	"crypto/x509"
	"encoding/gob"
	"encoding/pem"
	"fmt"
	"os"
	"time"
)

var (
	Username = "admin"
	Secret   = "secret-key-123"
)

// 验证许可证签名
func verifyLicense(license *License, publicKey *rsa.PublicKey) error {
	licenseCopy := *license
	licenseCopy.Signature = nil

	var buffer bytes.Buffer
	encoder := gob.NewEncoder(&buffer)
	err := encoder.Encode(licenseCopy)
	if err != nil {
		return err
	}
	licenseData := buffer.Bytes()

	hash := sha256.Sum256(licenseData)
	return rsa.VerifyPKCS1v15(publicKey, crypto.SHA256, hash[:], license.Signature)
}

func CheckLicense() {
	// 读取公钥
	publicKeyData, err := os.ReadFile("public.pem")
	if err != nil {
		panic(err)
	}
	publicBlock, _ := pem.Decode(publicKeyData)
	publicKey, err := x509.ParsePKIXPublicKey(publicBlock.Bytes)
	if err != nil {
		panic(err)
	}
	rsaPublicKey, ok := publicKey.(*rsa.PublicKey)
	if !ok {
		panic("不是 RSA 公钥")
	}

	// 读取二进制许可证
	licenseData, err := os.ReadFile("license.lic")
	if err != nil {
		fmt.Println("未找到许可证文件")
		return
	}

	var license License
	buffer := bytes.NewBuffer(licenseData)
	decoder := gob.NewDecoder(buffer)
	err = decoder.Decode(&license)
	if err != nil {
		fmt.Println("解析许可证文件失败")
		return
	}

	// 验证签名
	err = verifyLicense(&license, rsaPublicKey)
	if err != nil {
		fmt.Println("许可证验证失败:", err)
		return
	}

	// 检查过期时间 这里是使用本地时间校验的,也可以通过网络时间校验
	if time.Now().After(license.Expiration) {
		fmt.Println("许可证已过期")
		return
	}

	// 检查用户名
	if !(license.Username == Username && license.Secret == Secret) {
		fmt.Println("许可证无效")
		return
	}

	fmt.Printf("许可证有效,程序可以运行,有效期至%s\n", license.Expiration.Format("2006-01-02 15:04:05"))
	// 你的应用程序逻辑在这里
}

func main() {
	CheckLicense()
}

这里是通过本地时间来校验的有效性

这样做是有风险的,因为只要修改本地时间,就可以永远保证程序的有效性,这就导致license失去了意义

所以,可以通过网络时间来校验

或者循环检查本地时间,如果发现与网络时间相差太大,就不再允许使用程序

还有这里的用户名和密钥,可以通过网络进行校验,可以保证对程序的严格控制

标签:license,err,nil,签发,crypto,pem,go,panic
From: https://www.cnblogs.com/guangdelw/p/18328342

相关文章

  • 在 Google Colab 上运行 Django:错误 403 Forbidden
    我正在尝试对我的Python程序的Colab进行一些测试并使用Django。我按照此链接中的说明进行操作。我确保在settings.py中设置了此设置ALLOWED_HOSTS=['*']运行此命令以获取链接https://randomstrings.colab.googleusercontent.com/fromgo......
  • Django 测试设置错误:MySQL 后端的 django_content_type 表问题
    我在使用MySQL后端设置Django测试时遇到问题。该错误发生在测试数据库设置阶段,特别与django_content_type表相关。详细信息如下:环境:Django版本:5.0.7MySQL版本:8.0.37操作系统:Ubuntu20.04Python版本:3.11.12......
  • Django Haystack 多值字符串分面——未知字段tags_exact
    我网站上的内容用可变长度的字符串标记,我想对这些标记进行分面搜索。例如,一个故事可能有标签“内战”、“格兰特将军”和“葛底斯堡之战”。我希望能够对精确的非标记化字符串进行分面搜索。在我的search_index.py中,我定义了:tags=MultiValueField(faceted=True,in......
  • mongodb使用总结
    mongodb项目地址晓智科技晓智科技晓智文档晓智文档源码地址源码地址文档源码文档源码Linux下安装mongodb在/etc/yum.repos.d/下创建mongodb-org-4.2.repotouchmongodb-org-4.2.repovim写入下面文件[mngodb-org]name=MongoDBRepositorybaseurl=http://mi......
  • 【Python】字母 Rangoli 图案
    一、题目YouaregivenanintegerN.YourtaskistoprintanalphabetrangoliofsizeN.(RangoliisaformofIndianfolkartbasedoncreationofpatterns.)Differentsizesofalphabetrangoliareshownbelow:#size3----c------c-b-c--c-b-a-b-c--c......
  • GYM105139C Lili Likes Polygons
    记矩形的并为\(P\),定义多边形的大小为它的顶点个数\(|P|\),其\(90\)°的顶角为凸角,\(270\)°的顶角为凹角并记凹点构成的集合为\(C\),称竖直或水平在多边形内部分割开矩形的线为割线,连接了两个凹点的割线为好割线贪心可以发现对于\(P\)的任意极小矩阵划分,所有的割线至少有一......
  • Zygote 进程你不知道的东西
    一、概述1.Zygote(孵化)进程是所有Android进程的父进程,包括SystemServer和各种应用进程都是通过Zygote进程fork出来的。Zygote进程相当于Android系统的根进程,系统启动后所有的进程都是通过这个进程fork出来的。这样做的好处是:每个新应用进程都可以从Zygote进程继承已经初始化......
  • Windows下使用Apache和mod_wsgi部署django项目
    一、安装Python确定好所需要的python版本。二、安装Apacheapache下载地址:http://httpd.apache.org/docs/current/platform/windows.html#down下载完成后做如下操作将apache解压后直接复制到你想安装的路径下1、更改httpd.conf文件,找到如下代码并更改路径DefineSRVROOT"E:......
  • 在 Django 中注册时未填充国家/地区字段
    当用户注册时,我想将他们的国家/地区添加到他们的个人资料中。但是,该字段在数据库中仍为空白,并且不会填充国家/地区。我是Django中的身份验证新手,因此其他方面可能是错误的。我认为顺序可能是问题所在?我真的不确定。Views.pydefget_country(ip):ifipin['127.0.0......
  • 在 Django 中构建动态任务和徽章评估系统
    模型任务classQuest(models.Model):name=models.CharField(max_length=255)description=models.TextField(blank=True)criteria=models.JSONField()#StorecriteriaasJSONreward_points=models.IntegerField(default=0)def__str_......