数字签名、证书,RSA加密、解密

文章目录

  • 一、相关知识扫盲篇
    • 数字签名、数字证书
    • 防止证书伪造 之 证书链-Digital Certificates
  • 二、公私钥格式、协议规范
  • 三、golang RSA加密、解密具体实现
    • `RSA`加密、解密
      • `RSA`公钥加密
      • `RSA`私钥解密
      • `hmac_sha256`加密
      • 私钥加密,生成签名`SHA256withRSA`
      • `RSA`公钥验证签名
    • golang解析数字证书操作

一、相关知识扫盲篇

数字签名、数字证书

A与B的交互,通过一方的公私密钥,实现数据的加密,解密,验证数据签名,以保证其数据是对的。

但是存在比如B处存放的A的公钥被别人替换为如C的公钥可能性,这样C去发送信息给B,B会误以为是A发的,达到欺骗的效果
由此出现了,中心证书的CA(Certificate authority)机构,做中间人,把公钥持有人的信息,再用机构的私钥对A的信息进行加密。

B拿到数据,先用证书持有机构的公钥进行解密,证明是A的数据,然后再用A的公钥进行验签

引申:既然道格可以替换鲍勃的公钥,为什么不能故技重施,伪造CA的公钥,然后用自己的私钥伪造成CA的数字证书,从而达到欺骗苏珊的目的呢?

解释:CA的公钥是公开的,是可查的,他们的公钥在自己网站上提供下载,因此无法伪造

总结:公私钥用于加密、验证之间的数据,证书(中心媒介)用于证明其拿到的是正确的交互方的公钥。

防止证书伪造 之 证书链-Digital Certificates

文档地址,点击跳转

SHA256withRSA 先用RAS加密,在执行SHA256的哈希运算,主要用在签名验证操作

二、公私钥格式、协议规范

  • 公私钥格式、标准、规范,golang生成公私钥,见(https://blog.csdn.net/BuquTianya/article/details/82958194)
  • openssl数字证书常见格式与协议介绍

主要涉及的go包:

  • x509 x509包解析X.509(密码学里公钥证书的格式标准)编码的证书和密钥
  • pem包,pem包实现了PEM数据编码(源自保密增强邮件协议 Privacy Enbanced Mail)。目前PEM编码主要用于TLS密钥和证书
  • crypto/rsa rsa包实现了PKCS#1规定的RSA加密算法

三、golang RSA加密、解密具体实现

RSA加密、解密

  • RSA加密数据
  • 再配合 SHA256哈希 获得不可修改的签名signature

RSA公钥加密

// 公钥加密-分段

func RsaEncryptBlock(src, publicKeyByte []byte) (bytesEncrypt []byte, err error) {
     
    // 解码公钥文件,返回block结构
    // The encoded form is:
    //    -----BEGIN Type-----
    //    Headers
    //    base64-encoded Bytes
    //    -----END Type-----
	block, _ := pem.Decode(publicKeyByte)
	if block == nil {
     
		return nil, errors.New("Decode PublicKey Fail")
	}
	
	// 解析 PKIX 格式的公钥,返回 *rsa.PublicKey, *dsa.PublicKey, *ecdsa.PublicKey 等格式的struct
	publicKey, err := x509.ParsePKIXPublicKey(block.Bytes)
	if err != nil {
     
		return nil, err
	}
	
	//根据自己具体使用的格式做推测
	pub, keySize :=  publicKey.(*rsa.PublicKey), publicKey.(*rsa.PublicKey).Size()
	srcSize := len(src)
	
    fmt.Println("密钥长度:", keySize, "\t明文长度:\t", srcSize)
	//单次加密的长度需要减掉padding的长度,PKCS1为11
	

    // 按需加密
	offSet, once := 0, keySize-11
	buffer := bytes.Buffer{
     }
	for offSet < srcSize {
     
		endIndex := offSet + once
		if endIndex > srcSize {
     
			endIndex = srcSize
		}
		// 加密一部分
		bytesOnce, err := rsa.EncryptPKCS1v15(myrand.Reader, pub, src[offSet:endIndex])
		if err != nil {
     
			return nil, err
		}
		buffer.Write(bytesOnce)
		offSet = endIndex
	}
	bytesEncrypt = buffer.Bytes()
	return
}

RSA私钥解密

**
私钥解密-分段
 */
func RsaDecryptBlock(src, privateKeyByte []byte) (bytesDecrypt  []byte, err error) {
     
	block, _ := pem.Decode(privateKeyByte)
	if block == nil {
     
		return nil, errors.New("Decode PrivateKey Fail")
	}
	
	// 注意格式 PKCS8 / PKCS1
	privateKey, err := x509.ParsePKCS8PrivateKey(block.Bytes)
	if err != nil {
     
		return nil, err
	}
	
	private := privateKey.(*rsa.PrivateKey)
	keySize, srcSize := private.Size(), len(src)
	fmt.Println("密钥长度:", keySize, "\t密文长度:\t", srcSize)
	
	var offSet = 0
	var buffer = bytes.Buffer{
     }
	for offSet < srcSize {
     
		endIndex := offSet + keySize
		if endIndex > srcSize {
     
			endIndex = srcSize
		}
		// 解密
		bytesOnce, err := rsa.DecryptPKCS1v15(myrand.Reader, private, src[offSet:endIndex])
		if err != nil {
     
			return nil, err
		}
		buffer.Write(bytesOnce)
		offSet = endIndex
	}
	bytesDecrypt = buffer.Bytes()
	return
}

hmac_sha256加密

func HmacSha256(message string, secret string) string {
     
	key := []byte(secret)
	h := hmac.New(sha256.New, key)
	h.Write([]byte(message))
	return base64.StdEncoding.EncodeToString(h.Sum(nil))  // 转base64str
}

私钥加密,生成签名SHA256withRSA

func RSASign(origData string, privateKeyBytes []byte) (sign string,err error) {
     
    /*
    // 可以通过读取私钥文件,获取私钥字节数据
    privateKeyBytes, err := ioutil.ReadFile(filename)
    if err != nil {
        return nil, err
    }
    */
    
    // 解码私钥
	blocks,_  := pem.Decode(privateKeyBytes)
    if block == nil {
     
        return nil, errors.New("decode privatekey fail")
    }
	// 解析私钥对象
	privateKey, _ := x509.ParsePKCS8PrivateKey(blocks.Bytes)
	
	// 选择hash算法,对数据进行hash
	h := sha256.New()
	h.Write([]byte(origData))
	digest := h.Sum(nil)
	
	// 生成签名
	s, _ := rsa.SignPKCS1v15(nil, privateKey.(*rsa.PrivateKey), crypto.SHA256, digest)
	// 获取base64str
	sign = base64.StdEncoding.EncodeToString(s)
	return sign, nil
}

RSA公钥验证签名

func RSAVerify(data []byte, base64Sig, filename string) error {
     
    // 对base64编码的签名内容进行解码
    sign_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)
    
    // 读取公钥文件,解析出公钥对象
    // 1、读取公钥文件,获取公钥字节
    publicKeyBytes, err := ioutil.ReadFile(filename)
    if err != nil {
     
        return err
    }
    // 2、解码公钥字节,生成加密对象
    block, _ := pem.Decode(publicKeyBytes)
    if block == nil {
     
        return errors.New("decode publicKey fail")
    }
    // 3、解析DER编码的公钥,生成公钥接口
    publicKeyInterface, err := x509.ParsePKIXPublicKey(block.Bytes)
    if err != nil {
     
        return err
    }
    // 4、公钥接口转型成公钥对象(按需转换判断类型)
    publicKey := publicKeyInterface.(*rsa.PublicKey)

    
    // RSA验证数字签名(参数是公钥对象、哈希类型、签名文件的哈希串、已知的签名数据)
    return rsa.VerifyPKCS1v15(publicKey, myhash, hashed, sign_bytes)
}

这里以PayPal webhook数据验签(SHA256withRSA)举例:

import (
	"crypto"
	"crypto/rsa"
	"crypto/x509"
	"encoding/base64"
	"encoding/pem"
	"errors"
	"github.com/astaxie/beego/logs"
	"hash/crc32"
	"io/ioutil"
	"net/http"
	"strconv"
)


type PayPalWebHook struct {
     
	TransmissionID   string // Paypal-Transmission-Id
	TransmissionTime string // Paypal-Transmission-Time (请求头header里边)
	TransmissionSig  string // Paypal-Transmission-Sig 传递来的签名
	WebHookID        string // webhook_id 注意与  webhook_event_id做区分
	CertUrl          string // Paypal-Cert-Url
	EventData        []byte // payload data
}

func (this *PayPalWebHook) VerifySignature() (bool, error) {
     

	//获取证书 
	// 这里使用下载证书的方式,如果是本地使用io操作读取即可
	resp, err := http.Get(this.CertUrl)
	if err != nil {
     
		logs.Error("[Get Cert Error]: ", err)
		return false, err
	}
	defer resp.Body.Close()
	cert_bytes, err := ioutil.ReadAll(resp.Body)
	if err != nil {
     
		return false, err
	}


	//解析pub_key,从证书里边解析出公钥
	// 若已有公钥,则忽略此从证书解出公钥的操作
	pub_key, err := this.getPublicKey(cert_bytes)
	if err != nil {
     
		return false, err
	}

	//拼接签名所需字符串。
	expect_str := this.getExpectedStr()

	//执行校验
	sig_bytes, err := base64.StdEncoding.DecodeString(this.TransmissionSig)
	if err != nil {
     
		logs.Error("[base64 decode error]: ", err)
		return false, err
	}
	hash_handler := crypto.SHA256
	hash_instance := hash_handler.New()
	hash_instance.Write([]byte(expect_str))
	hashed := hash_instance.Sum(nil)

	err = rsa.VerifyPKCS1v15(pub_key, hash_handler, hashed, sig_bytes)
	if err != nil {
     
		logs.Error("[Verify Signature Error]: ", err)
		return false, err
	}
	return true, nil
}



//获取签名拼接字符串(按需设置)
func (this *PayPalWebHook) getExpectedStr() string {
     
	// 这里取数据的crc32冗余校验的值,和相关参数做拼接的hash前的数据字符串。按需改为需要进行签名操作的数据
	//crc冗余校验
	event_crc32 := crc32.ChecksumIEEE(this.EventData)
	//PayPal签名要求格式
	expect_string := this.TransmissionID + "|" + this.TransmissionTime + "|" + this.WebHookID + "|" + strconv.FormatUint(uint64(event_crc32), 10)
	logs.Debug("[Expect string]: ", expect_string)
	return expect_string
}

// 从证书里提取公钥
func (this *PayPalWebHook) getPublicKey(cert_bytes []byte) (*rsa.PublicKey, error) {
     
	//解码证书pem,验证格式
	cert_der_block, _ := pem.Decode(cert_bytes)
	if cert_der_block == nil {
     
		logs.Error(cert_der_block)
		return nil, errors.New("decode_cert_error")
	}
	//解析证书
	cert, err := x509.ParseCertificate(cert_der_block.Bytes)
	if err != nil {
     
		logs.Error("[ParseCertificate Error]: , err")
		return nil, err
	}
	//序列化公钥
	pub_key_der, err := x509.MarshalPKIXPublicKey(cert.PublicKey)
	if err != nil {
     
		logs.Error("[MarshalPKIXPublicKey Error]: ", err)
		return nil, err
	}
	//解析公钥
	pub_key, err := x509.ParsePKIXPublicKey(pub_key_der)
	if err != nil {
     
		logs.Error("[ParsePKIXPublicKey Error]: ", err)
		return nil, err
	}
	//这里因为知道其加密方式,直接推断的rsa,这里根据具体情况修改实现
	return pub_key.(*rsa.PublicKey), nil
}

golang解析数字证书操作

  • golang解析数字证书和PKCS#1&PKCS#8格式的私钥

你可能感兴趣的:(goland,网络通讯,SHA256withRSA,RSA,SHA256,公钥,私钥)