加解密和签名校验可视化工具(Golang)

目录

一、RSA简介

二、工程目录

三、RSA Utils

1、加密与解密

2、签名与验证签名

四、其他

1、不可逆的密码加密工具

2、JWT

五、测试

六、GUI

七、sys

八、界面

1、测试加密

2、测试解密

3、测试签名 

4、验证签名 

九、浅应用

十、改进

十一、参考


一、RSA简介

RSA公开密钥密码体制是一种使用不同的加密密钥与解密密钥,“由已知加密密钥推导出解密密钥在计算上是不可行的”密码体制 。
在公开密钥密码体制中,加密密钥(即公开密钥)PK是公开信息,而解密密钥(即秘密密钥)SK是需要保密的。加密算法E和解密算法D也都是公开的。虽然解密密钥SK是由公开密钥PK决定的,但却不能根据PK计算出SK 。
正是基于这种理论,1978年出现了著名的RSA算法,它通常是先生成一对RSA密钥,其中之一是保密密钥,由用户保存;另一个为公开密钥,可对外公开,甚至可在网络服务器中注册。为提高保密强度,RSA密钥至少为500位长。这就使加密的计算量很大。为减少计算量,在传送信息时,常采用传统加密方法与公开密钥加密方法相结合的方式,即信息采用改进的DES或IDEA对话密钥加密,然后使用RSA密钥加密对话密钥和信息摘要。对方收到信息后,用不同的密钥解密并可核对信息摘要 。

——摘自百度百科

本文将基于RSA算法实现可视化窗体的加密、解密、签名、校验工具。

二、工程目录

├─cmd 主入口
├─files RSA 密钥、加解密后的文件
├─gui 可视化窗体
├─sys 系统配置
├─test 测试
└─utils 工具

三、RSA Utils

1、加密与解密

需要用到的库

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

 首先生成密钥对

//生成RSA私钥和公钥,保存到文件中

func GenerateRSAKey(bits int) {
	//GenerateKey函数使用随机数据生成器random生成一对具有指定字位数的RSA密钥
	//Reader是一个全局、共享的密码用强随机数生成器
	privateKey, err := rsa.GenerateKey(rand.Reader, bits)
	if err != nil {
		log.Fatalln(err)
	}
	//保存私钥
	//通过x509标准将得到的ras私钥序列化为ASN.1 的 DER编码字符串
	X509PrivateKey := x509.MarshalPKCS1PrivateKey(privateKey)
	//使用pem格式对x509输出的内容进行编码
	//创建文件保存私钥
	privateFile, err := os.Create("./files/private.pem")
	if err != nil {
		log.Fatalln(err)
	}
	defer privateFile.Close()
	//构建一个pem.Block结构体对象
	privateBlock := pem.Block{Type: "RSA Private Key", Bytes: X509PrivateKey}
	//将数据保存到文件
	pem.Encode(privateFile, &privateBlock)

	//保存公钥
	//获取公钥的数据
	publicKey := privateKey.PublicKey
	//X509对公钥编码
	X509PublicKey, err := x509.MarshalPKIXPublicKey(&publicKey)
	if err != nil {
		log.Fatalln(err)
	}
	//pem格式编码
	//创建用于保存公钥的文件
	publicFile, err := os.Create("./files/public.pem")
	if err != nil {
		log.Fatalln(err)
	}
	defer publicFile.Close()
	//创建一个pem.Block结构体对象
	publicBlock := pem.Block{Type: "RSA Public Key", Bytes: X509PublicKey}
	//保存到文件
	pem.Encode(publicFile, &publicBlock)
}

RSAEncrypt 函数使用公钥对信息进行加密,RSADecrypt 函数使用密钥对信息进行解密,加密和解密都进行一次 BASE64 处理。

//RSA加密

func RSAEncrypt(plainText []byte, saveFilePath, path string) []byte {
	//打开文件
	file, err := os.Open(path)
	if err != nil {
		log.Fatalln(err)
	}
	defer file.Close()
	//读取文件的内容
	info, _ := file.Stat()
	buf := make([]byte, info.Size())
	file.Read(buf)
	//pem解码
	block, _ := pem.Decode(buf)
	//x509解码

	publicKeyInterface, err := x509.ParsePKIXPublicKey(block.Bytes)
	if err != nil {
		log.Fatalln(err)
	}
	//类型断言
	publicKey := publicKeyInterface.(*rsa.PublicKey)
	//对明文进行加密
	cipherText, err := rsa.EncryptPKCS1v15(rand.Reader, publicKey, plainText)
	if err != nil {
		log.Fatalln(err)
	}
	//保存密文
	encode := Base64Encode(cipherText)
	log.Println("base64 ", encode)
	saveCipherText(encode, saveFilePath)
	//返回密文
	return encode
}

保存密文


// 保存密文
func saveCipherText(cipherText []byte, path string) {
	cipherFile, err := os.Create(path)
	if err != nil {
		log.Fatalln(err)
		return
	}
	defer cipherFile.Close()
	_, err = cipherFile.Write(cipherText)
	if err != nil {
		log.Fatalln(err)
		return
	}
	log.Println("CipherText has been save as ", path)
}

解密

//RSA解密

func RSADecrypt(cipherText []byte, path string) []byte {
	//打开文件
	file, err := os.Open(path)
	if err != nil {
		log.Fatalln(err)
	}
	defer file.Close()
	//获取文件内容
	info, _ := file.Stat()
	buf := make([]byte, info.Size())
	file.Read(buf)
	//pem解码
	block, _ := pem.Decode(buf)
	//X509解码
	privateKey, err := x509.ParsePKCS1PrivateKey(block.Bytes)
	if err != nil {
		log.Fatalln(err)
	}
	//对密文进行解密
	decode, err := Base64Decode(cipherText)
	if err != nil {
		log.Fatalln(err)
	}
	plainText, _ := rsa.DecryptPKCS1v15(rand.Reader, privateKey, decode)
	//返回明文
	return plainText
}

从文件中读取密文并解密


// 从文件中读取密文并解密

func RSADecryptFromFile(cipherFilePath string, path string) []byte {
	f, err := os.Open(cipherFilePath)
	if err != nil {
		log.Fatalln(err)
		return nil
	}
	defer f.Close()
	all, err := ioutil.ReadAll(f)
	if err != nil {
		log.Fatalln(err)
		return nil
	}
	return RSADecrypt(all, path)
}

2、签名与验证签名

需要用到的库

import (
	"crypto"
	"crypto/rand"
	"crypto/rsa"
	"crypto/x509"
	"encoding/base64"
	"encoding/pem"
	"errors"
	"io/ioutil"
)

解析密钥对

//读取公钥文件,解析出公钥对象
func ReadParsePublicKey(filename string) (*rsa.PublicKey, error) {
	//--1.读取公钥文件,获取公钥字节
	publicKeyBytes, err := ioutil.ReadFile(filename)
	if err != nil {
		return nil, err
	}
	//--2.解码公钥字节,生成加密对象
	block, _ := pem.Decode(publicKeyBytes)
	if block == nil {
		return nil, errors.New("公钥信息错误")
	}
	//--3.解析DER编码的公钥,生成公钥接口
	publicKeyInterface, err := x509.ParsePKIXPublicKey(block.Bytes)
	if err != nil {
		return nil, err
	}
	//--4.公钥接口转型成公钥对象
	publicKey := publicKeyInterface.(*rsa.PublicKey)
	return publicKey, nil
}

//读取私钥文件,解析出私钥对象
func ReadParsePrivateKey(filename string) (*rsa.PrivateKey, error) {
	//--1.读取私钥文件,获取私钥字节
	privateKeyBytes, _ := ioutil.ReadFile(filename)
	//--2.对私钥文件进行编码,生成加密对象
	block, _ := pem.Decode(privateKeyBytes)
	if block == nil {
		return nil, errors.New("私钥信息错误")
	}
	//3.解析DER编码的私钥,生成私钥对象
	privateKey, err := x509.ParsePKCS1PrivateKey(block.Bytes)
	if err != nil {
		return nil, err
	}
	return privateKey, err
}

使用 RSASign 函数进行私钥签名

func RSASign(data []byte, filename string) (string, error) {
	//1.选择hash算法,对需要签名的数据进行hash运算
	myhash := crypto.SHA256
	hashInstance := myhash.New()
	hashInstance.Write(data)
	hashed := hashInstance.Sum(nil)
	//2.读取私钥文件,解析出私钥对象
	privateKey, err := ReadParsePrivateKey(filename)
	if err != nil {
		return "", err
	}
	//3.RSA数字签名
	bytes, err := rsa.SignPKCS1v15(rand.Reader, privateKey, myhash, hashed)
	if err != nil {
		return "", err
	}
	return base64.StdEncoding.EncodeToString(bytes), nil
}

RSAVerify 函数进行公钥验证签名


//公钥验证数据签名是否正确

func RSAVerify(data []byte, base64Sig, filename string) error {
	//- 对base64编码的签名内容进行解码,返回签名字节
	bytes, err := base64.StdEncoding.DecodeString((base64Sig))
	if err != nil {
		return err
	}
	//- 选择hash算法,对需要签名的数据进行hash运算
	myhash := crypto.SHA256
	hashInstance := myhash.New()
	hashInstance.Write(data)
	hashed := hashInstance.Sum(nil)
	//- 读取公钥文件,解析出公钥对象
	publicKey, err := ReadParsePublicKey(filename)
	if err != nil {
		return err
	}
	//- RSA验证数字签名
	return rsa.VerifyPKCS1v15(publicKey, myhash, hashed, bytes)
}

四、其他

1、不可逆的密码加密工具

通常用于密码的加密保存,使用 CompareHashAndPassword 函数校验密码,而不能解密密码。

package utils

import (
	"fmt"
	"golang.org/x/crypto/bcrypt"
)

// 不可逆的密码加密工具封装
// 加密使用 bcrypt.GenerateFromPassword
// 比对密码时使用 bcrypt.CompareHashAndPassword

//加密处理

func EncodePassword(password string) (string, error) {
	hash, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
	if err != nil {
		fmt.Println(err)
		return "", err
	}
	// 保存在数据库的密码,虽然每次生成都不同,只需保存一份即可
	return string(hash), nil
}

func ComparePassword(databasePwd, loginPwd string) bool {
	// 密码验证
	err := bcrypt.CompareHashAndPassword([]byte(databasePwd), []byte(loginPwd))
	if err != nil {
		fmt.Println("pwd wrong")
		return false
	} else {
		fmt.Println("pwd ok")
		return true
	}
}

2、JWT

package utils

import (
	"encryptTool/sys"
	"fmt"
	"github.com/dgrijalva/jwt-go"
)

// 定义一个jwt对象

type JWT struct {
	// 声明签名信息
	SigningKey []byte
}

// 初始化jwt对象

func NewJWT() *JWT {
	return &JWT{
		[]byte(sys.SigningKey),
	}
}

// 自定义有效载荷(这里采用自定义的 UserCode 和 UserRole 作为有效载荷的一部分)

type CustomClaims struct {
	UserCode string `json:"userCode"`
	UserRole string `json:"userRole"`
	// StandardClaims结构体实现了Claims接口(Valid()函数)
	jwt.StandardClaims
}

// 调用jwt-go库生成token
// 指定编码的算法为jwt.SigningMethodHS512

func (j *JWT) CreateToken(claims CustomClaims) (string, error) {
	// https://gowalker.org/github.com/dgrijalva/jwt-go#Token
	// 返回一个token的结构体指针
	token := jwt.NewWithClaims(jwt.SigningMethodHS512, claims)
	return token.SignedString(j.SigningKey)
}

// token解码

func (j *JWT) ParserToken(tokenString string) (*CustomClaims, error) {
	// https://gowalker.org/github.com/dgrijalva/jwt-go#ParseWithClaims
	// 输入用户自定义的Claims结构体对象,token,以及自定义函数来解析token字符串为jwt的Token结构体指针
	// Keyfunc是匿名函数类型: type Keyfunc func(*Token) (interface{}, error)
	// func ParseWithClaims(tokenString string, claims Claims, keyFunc Keyfunc) (*Token, error) {}
	token, err := jwt.ParseWithClaims(tokenString, &CustomClaims{}, func(token *jwt.Token) (interface{}, error) {
		return j.SigningKey, nil
	})

	if err != nil {
		// https://gowalker.org/github.com/dgrijalva/jwt-go#ValidationError
		// jwt.ValidationError 是一个无效token的错误结构
		if ve, ok := err.(*jwt.ValidationError); ok {
			// ValidationErrorMalformed是一个uint常量,表示token不可用
			if ve.Errors&jwt.ValidationErrorMalformed != 0 {
				return nil, fmt.Errorf(sys.TokenDisabled)
				// ValidationErrorExpired表示Token过期
			} else if ve.Errors&jwt.ValidationErrorExpired != 0 {
				return nil, fmt.Errorf(sys.TokenExpired)
				// ValidationErrorNotValidYet表示无效token
			} else if ve.Errors&jwt.ValidationErrorNotValidYet != 0 {
				return nil, fmt.Errorf(sys.TokenInvalid)
			} else {
				return nil, fmt.Errorf(sys.TokenDisabled)
			}

		}
	}

	// 将token中的claims信息解析出来并断言成用户自定义的有效载荷结构
	if claims, ok := token.Claims.(*CustomClaims); ok && token.Valid {
		return claims, nil
	}

	return nil, fmt.Errorf(sys.TokenInvalid)

}

五、测试

// SIGN => VERIFY

func TestV1() {

	str := "will be best"
	base64Sig, _ := utils.RSASign([]byte(str), sys.PRIVATE_KEY_PATH)
	fmt.Println("签名后信息", base64Sig)

	err := utils.RSAVerify([]byte(str), base64Sig, sys.PUBLIC_KEY_PATH)
	if err == nil {
		fmt.Println("验证签名ok!")
	} else {
		fmt.Println("验证失败!")
	}

}

// ENCODE => DECODE

func TestV2() {

	//生成密钥对,保存到文件;若已有密钥对则无需再生成
	// utils.GenerateRSAKey(2048)

	// 需要加密的明文
	message := []byte("hello,world")
	fmt.Println("加密原文:", string(message))
	//加密
	cipherText := utils.RSAEncrypt(message, sys.CIPHER_FILE_PATH, sys.PUBLIC_KEY_PATH)
	fmt.Println("加密后为:")
	for _, v := range cipherText {
		fmt.Printf("%s", string(v))
	}
	//解密
	plainText := utils.RSADecrypt(cipherText, sys.PRIVATE_KEY_PATH)
	fmt.Println("解密后为:", string(plainText))

	// 读取加密文件进行解密
	msg := utils.RSADecryptFromFile(sys.CIPHER_FILE_PATH, sys.PRIVATE_KEY_PATH)
	fmt.Println("文件解密后为:", string(msg))
}

func TestV3() {
	loginPassword := "123456"
	password, err := utils.EncodePassword(loginPassword)
	if err != nil {
		fmt.Println(err)
	}
	fmt.Println("EncodePassword ", password)
	if utils.ComparePassword(password, loginPassword) {
		fmt.Println("password match")
	} else {
		fmt.Println("password error")
	}
}

func TestV4() {
	jwt := utils.NewJWT()
	token, err := jwt.CreateToken(utils.CustomClaims{UserCode: "user", UserRole: "role"})
	if err != nil {
		fmt.Println(err)
	}
	fmt.Println("CreateToken ", token)
	parserToken, err := jwt.ParserToken(token)
	if err != nil {
		fmt.Println(err)
	}
	fmt.Println("ParserToken ", parserToken)
}

//aGVsbG8gd29ybGQ=
//hello world <nil>
func TestBase64() {

	// 标准Base64编码
	src := "hello world"
	res := base64.StdEncoding.EncodeToString([]byte(src))
	fmt.Println(res) // aGVsbG8gd29ybGQ=

	// 标准Base64解码
	s, err := base64.StdEncoding.DecodeString(res)
	fmt.Println(string(s), err) // hello world <nil>

}

六、GUI

package gui

import (
	"encryptTool/sys"
	"encryptTool/utils"
	"fmt"
	"fyne.io/fyne/app"
	"fyne.io/fyne/container"
	"fyne.io/fyne/layout"
	"fyne.io/fyne/widget"
	"log"
	"strings"
)

func InitGui() {
	application := app.New()

	// 输入框
	input := widget.NewMultiLineEntry()
	// 输出框
	output := widget.NewMultiLineEntry()
	// 加密
	encode := widget.NewButton("Encode", func() {
		log.Println("encode", input.Text)
		message := []byte(input.Text)
		//加密
		cipherText := utils.RSAEncrypt(message, sys.CIPHER_FILE_PATH, sys.PUBLIC_KEY_PATH)
		fmt.Println("加密后为:")
		n := 1
		outStr := ""
		for _, v := range cipherText {
			if n > 50 {
				outStr = outStr + "\n"
				fmt.Print("\n")
				n = 0
			}
			outStr = outStr +string(v)
			fmt.Printf("%s", string(v))
			n++
		}
		output.SetText(outStr)
	})
	// 解密
	decode := widget.NewButton("Decode", func() {
		log.Println("decode", input.Text)
		//解密
		plainText := utils.RSADecrypt([]byte(strings.TrimSpace(input.Text)), sys.PRIVATE_KEY_PATH)
		log.Println("解密后为:", string(plainText))
		output.SetText(string(plainText))
	})
	// 签名
	sign := widget.NewButton("Sign", func() {
		log.Println("sign", input.Text)
		base64Sig, _ := utils.RSASign([]byte(input.Text), sys.PRIVATE_KEY_PATH)
		log.Println("签名后信息", base64Sig)
		n := 1
		outStr := ""
		for _, v := range base64Sig {
			if n > 50 {
				outStr = outStr + "\n"
				fmt.Print("\n")
				n = 0
			}
			outStr = outStr +string(v)
			fmt.Printf("%s", string(v))
			n++
		}
		output.SetText(outStr)
	})
	// 认证
	verify := widget.NewButton("Verify", func() {
		log.Println("verify", input.Text)
		err := utils.RSAVerify([]byte(strings.TrimSpace(input.Text)), output.Text, sys.PUBLIC_KEY_PATH)
		if err == nil {
			fmt.Println("验证签名ok!")
			output.SetText("验证签名ok!")
		} else {
			fmt.Println("验证失败!")
			output.SetText("验证签名失败!")
		}
	})
	// 新窗口
	w := application.NewWindow("EncryptTool")
	w.SetContent(container.NewVBox(
		widget.NewLabel("Input"),
		input,
		widget.NewLabel("Output"),
		output,
		container.NewCenter(
			container.NewHBox(
				encode,
				decode,
				sign,
				verify,
			),
		),
		layout.NewSpacer(),
		widget.NewButton("Quit", func() {
			application.Quit()
		}),
	))
	// 居中显示
	w.CenterOnScreen()
	// 重置窗口大小
	w.SetFixedSize(true)
	//w.Resize(fyne.NewSize(400, 400))
	w.ShowAndRun()
}

七、sys

package sys

const CIPHER_FILE_PATH string = "./files/TestCipherFile"
const PUBLIC_KEY_PATH string = "./files/public.pem"
const PRIVATE_KEY_PATH string = "./files/private.pem"

const SigningKey string = ""
const TokenDisabled string = "token disabled" // token 不可用
const TokenInvalid string = "token invalid"   // token 无效
const TokenExpired string = "token expired"   // token 过期

八、界面

1、测试加密

2、测试解密

3、测试签名 

4、验证签名 

九、浅应用

构建一个签证Center、令牌Center、加解密Center,采用MySQL、Redis等中间件,实现定期动态新增密钥对,Center随机调用密钥对,记录密钥对 ID ,以便后续用相同密钥对进行操作,因此每个使用者调用的加密密钥对都是不同的。

十、改进

使用QT、WinForm、Web等前端工具构建更加友好的操作页面;采用多种加密方式供用户选择,而不只是局限于一种加密方式。

十一、参考

Go实现RSA数字签名算法(附代码)

Go语言实现RSA加密解密

golang gui库fyne的简单尝试

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

金汐脉动 | PulseTide

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值