golang实战-数字签名与认证

一. 数字签名 

        设想一个场景:Alice 给 Bob 发送了一段消息(明天我请你吃饭),该消息使用 Bob 的公钥加密(公钥加密才能确保消息被截获后也只有 Bob 本人能用自己的私钥解密),但是由于 Bob 的公钥可能其他人也有,Tom 也可以使用 Bob 的公钥加密信息后发送给 Bob,所以 Bob 无法确保这条信息是 Alice 本人发送的,但是如果每条信息都加上 Alice 本人的签名,Bob 接收到信息之后就知道真的是 Alice 发送的,也能通过第三方机构防止 Alice 否认该消息。

        数字签名就是由发送者对消息进行签名,具体方式是先对消息计算散列值,然后使用发送者的私钥对该散列值进行非对称加密得到数字签名,要发送的消息跟随数字签名一同发送。接收者拿到消息后,使用发送者的公钥对数字签名进行非对称解密得到散列值,然后对消息进行哈希计算得到散列值,将解密得到的散列值与哈希得到的散列值进行比较,如果二者相等,说明该消息确实是发送者本人发送的。

        常用的进行数字签名的方式有 RSA 和椭圆曲线。

二. golang实战

1. 使用rsa进行数字签名与认证

        crypto/rsa 包提供了 SignPKCS1v15 方法进行数字签名,VerifyPKCS1v15 方法进行数字签名认证。

使用 rsa 私钥文件进行数字签名步骤如下。

(1)读取私钥文件内容,使用 pem.Decode 将内容解析成 pem 格式块

(2)通过 x509.ParsePKCS1PrivateKey 将 pem 块中的 PKCS # 1, ASN.1 DER 格式的字符串解析成 rsa.PrivateKey

(3)计算发送内容散列值

(4)通过 rsa.SignPKCS1v15 使用私钥对散列值进行签名,该函数返回值就是数字签名内容

使用 rsa 公钥文件进行认证数字签名步骤如下。

(1)读取公钥文件内容,使用 pem.Decode 将内容解析成 pem 格式块

(2)通过 x509.ParsePKIXPublicKey 将 pem 块中的 DER 格式字符串解析成 rsa.PublicKey

(3)计算接收内容的散列值

(4)通过 rsa.VerifyPKCS1v15 使用公钥对散列值进行认证,如果该返回返回 err == nil,则表示认证成功

package main

import (
	"crypto"
	"crypto/ecdsa"
	"crypto/rand"
	"crypto/rsa"
	"crypto/sha512"
	"crypto/x509"
	"encoding/pem"
	"fmt"
	"math/big"
	"os"
)

func rsaSignature(plainText []byte, privateKeyFile string) ([]byte, error) {
	// 读取私钥文件并解析成rsa.PrivateKey
	file, err := os.Open(privateKeyFile)
	if err != nil {
		return nil, err
	}
	stat, err := file.Stat()
	if err != nil {
		return nil, err
	}
	buf := make([]byte, stat.Size())
	file.Read(buf)
	defer file.Close()

	block, _ := pem.Decode(buf)
	privateKey, err := x509.ParsePKCS1PrivateKey(block.Bytes)

	// 计算原始内容的散列值
	h := sha512.New()
	h.Write(plainText)
	hValue := h.Sum(nil)

	// 通过rsa.SignPKCS1v15使用私钥对原始内容散列值进行签名
	digestSign, err := rsa.SignPKCS1v15(rand.Reader, privateKey, crypto.SHA512, hValue)
	return digestSign, err
}

func rsaVerifySign(plainText []byte, publicKeyFile string, signed []byte) bool {
	// 读取公钥文件并解析成rsa.PublicKey
	file, err := os.Open(publicKeyFile)
	if err != nil {
		return false
	}
	stat, err := file.Stat()
	if err != nil {
		return false
	}
	buf := make([]byte, stat.Size())
	file.Read(buf)
	defer file.Close()

	block, _ := pem.Decode(buf)
	publicKeyInt, err := x509.ParsePKIXPublicKey(block.Bytes)
	publicKey := publicKeyInt.(*rsa.PublicKey)

	// 计算原始内容的散列值
	h := sha512.New()
	h.Write(plainText)
	hValue := h.Sum(nil)

	// 确认签名
	err = rsa.VerifyPKCS1v15(publicKey, crypto.SHA512,hValue, signed)

	return err == nil
}

func main() {
	content := []byte("Hello digest sign")
	sign, err := rsaSignature(content, "private.pem")
	if err != nil {
		return
	}
	fmt.Println("signature:",sign)
	fmt.Println("verify result:", rsaVerifySign(content, "public.pem", sign))
}

运行结果如下所示。 

2. 使用椭圆曲线进行数字签名与认证

package main

import (
	"crypto"
	"crypto/ecdsa"
	"crypto/rand"
	"crypto/rsa"
	"crypto/sha512"
	"crypto/x509"
	"encoding/pem"
	"fmt"
	"math/big"
	"os"
)

func eccSignature(plainText []byte, privateKeyFile string) ([]byte, []byte, error){
	// 读取私钥文件并解析成ecdsa.PrivateKey
	file, err := os.Open(privateKeyFile)
	if err != nil {
		return nil, nil, err
	}
	stat, err := file.Stat()
	if err != nil {
		return nil, nil, err
	}
	buf := make([]byte, stat.Size())
	file.Read(buf)
	defer file.Close()

	block, _ := pem.Decode(buf)
	privateKey, _ := x509.ParseECPrivateKey(block.Bytes)

	// 计算原始内容的散列值
	h := sha512.New()
	h.Write(plainText)
	hValue := h.Sum(nil)

	r, s, err := ecdsa.Sign(rand.Reader, privateKey, hValue)
	rText, _ := r.MarshalText()
	sText, _ := s.MarshalText()

	return rText, sText, nil
}

func eccVerifySign(plainText []byte, publicKeyFile string, rText []byte, sText []byte) bool {
	// 读取公钥文件并解析成ecdsa.PublicKey
	file, err := os.Open(publicKeyFile)
	if err != nil {
		return false
	}
	stat, err := file.Stat()
	if err != nil {
		return false
	}
	buf := make([]byte, stat.Size())
	file.Read(buf)
	defer file.Close()

	block, _ := pem.Decode(buf)
	publicKeyInt, err := x509.ParsePKIXPublicKey(block.Bytes)
	publicKey := publicKeyInt.(*ecdsa.PublicKey)

	// 计算原始内容的散列值
	h := sha512.New()
	h.Write(plainText)
	hValue := h.Sum(nil)

	var r, s big.Int
	r.UnmarshalText(rText)
	s.UnmarshalText(sText)

	return ecdsa.Verify(publicKey, hValue, &r, &s)
}

func main() {
	r, s, err := eccSignature(content, "ecc_private.pem")
	if err != nil {
		return
	}
	fmt.Println("r:", string(r))
	fmt.Println("s:", string(s))
	fmt.Println("verify result:", eccVerifySign(content, "ecc_public.pem", r, s))
}

运行结果如下所示。 

三. 数字签名存在的问题

         验证签名的一端无法确认公钥是否真的属于发送者。如上,中间人 Tom 拦截了 Alice 的公钥,并使用自己的公钥发送给 Bob,之后 Tom 将 Alice 的数据拦截,再自己生成数据并进行签名,而 Bob 却无法得知公钥已经被偷换了。

        这个问题使用数字证书可以解决。

  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
在Go语言中,没有内置的SCP(Secure Copy)功能,但你可以使用第方库来实现SCP功能。一个常用的第三方库是`github.com/pkg/sftp`,它提供了SSH文件传输协议的实现。 要使用该库进行SCP操作,你需要先建立一个SSH连接,然后使用SFTP客户端进行文件传输。下面是一个简单的示例代码: ```go package main import ( "fmt" "golang.org/x/crypto/ssh" "github.com/pkg/sftp" "io" "os" ) func main() { // SSH连接配置 config := &ssh.ClientConfig{ User: "username", Auth: []ssh.AuthMethod{ ssh.Password("password"), }, HostKeyCallback: ssh.InsecureIgnoreHostKey(), } // 建立SSH连接 conn, err := ssh.Dial("tcp", "example.com:22", config) if err != nil { fmt.Println("Failed to connect to the server:", err) return } defer conn.Close() // 创建SFTP客户端 client, err := sftp.NewClient(conn) if err != nil { fmt.Println("Failed to create SFTP client:", err) return } defer client.Close() // 打开本地文件 localFile, err := os.Open("/path/to/local/file") if err != nil { fmt.Println("Failed to open local file:", err) return } defer localFile.Close() // 创建远程文件 remoteFile, err := client.Create("/path/to/remote/file") if err != nil { fmt.Println("Failed to create remote file:", err) return } defer remoteFile.Close() // 将本地文件内容复制到远程文件 _, err = io.Copy(remoteFile, localFile) if err != nil { fmt.Println("Failed to copy file:", err) return } fmt.Println("File copied successfully!") } ``` 请注意,上述示例代码中的`username`、`password`、`example.com:22`、`/path/to/local/file`和`/path/to/remote/file`需要根据实际情况进行替换。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

椛茶

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

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

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

打赏作者

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

抵扣说明:

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

余额充值