A与B的交互,通过一方的公私密钥,实现数据的加密,解密,验证数据签名,以保证其数据是对的。
但是存在比如B处存放的A的公钥被别人替换为如C的公钥可能性,这样C去发送信息给B,B会误以为是A发的,达到欺骗的效果
由此出现了,中心证书的CA(Certificate authority)机构,做中间人,把公钥持有人的信息,再用机构的私钥对A的信息进行加密。
B拿到数据,先用证书持有机构的公钥进行解密,证明是A的数据,然后再用A的公钥进行验签
引申:既然道格可以替换鲍勃的公钥,为什么不能故技重施,伪造CA的公钥,然后用自己的私钥伪造成CA的数字证书,从而达到欺骗苏珊的目的呢?
解释:CA的公钥是公开的,是可查的,他们的公钥在自己网站上提供下载,因此无法伪造
总结:公私钥用于加密、验证之间的数据,证书(中心媒介)用于证明其拿到的是正确的交互方的公钥。
文档地址,点击跳转
SHA256withRSA
先用RAS加密,在执行SHA256的哈希运算,主要用在签名验证操作
主要涉及的go包:
x509
x509包解析X.509(密码学里公钥证书的格式标准)编码的证书和密钥pem
包,pem包实现了PEM数据编码(源自保密增强邮件协议 Privacy Enbanced Mail)。目前PEM编码主要用于TLS密钥和证书crypto/rsa
rsa包实现了PKCS#1规定的RSA加密算法RSA
加密、解密RSA
加密数据SHA256
哈希 获得不可修改的签名signatureRSA
公钥加密// 公钥加密-分段
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
}
PKCS#1
&PKCS#8
格式的私钥