目录
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 工具
需要用到的库
- 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)
- }
需要用到的库
- 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)
- }
通常用于密码的加密保存,使用 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
- }
- }
- 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
- 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
-
- }
- 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()
- }
- 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 过期
构建一个签证Center、令牌Center、加解密Center,采用MySQL、Redis等中间件,实现定期动态新增密钥对,Center随机调用密钥对,记录密钥对 ID ,以便后续用相同密钥对进行操作,因此每个使用者调用的加密密钥对都是不同的。
使用QT、WinForm、Web等前端工具构建更加友好的操作页面;采用多种加密方式供用户选择,而不只是局限于一种加密方式。
Go实现RSA数字签名算法(附代码)
Go语言实现RSA加密解密
golang gui库fyne的简单尝试