276 lines
8.3 KiB
Go
276 lines
8.3 KiB
Go
package signer
|
||
|
||
import (
|
||
"crypto"
|
||
"crypto/ecdsa"
|
||
"crypto/rand"
|
||
"crypto/sha256"
|
||
"crypto/x509"
|
||
"encoding/asn1"
|
||
"encoding/pem"
|
||
"math/big"
|
||
"os"
|
||
"strings"
|
||
|
||
pb "schain/proto"
|
||
"schain/proto/util"
|
||
|
||
"github.com/pkg/errors"
|
||
)
|
||
|
||
// Config 用户签名的配置(里面包含MSPID 用户属于那个组织,IdentityPath 用户证书的地址,KeyPath 用户密钥的地址)
|
||
type Config struct {
|
||
MSPID string
|
||
IdentityPath string
|
||
KeyPath string
|
||
}
|
||
|
||
// Signer 用户的签名,包含公钥、私钥和用户的身份
|
||
type Signer struct {
|
||
privateKey *ecdsa.PrivateKey
|
||
publicKeyKey *ecdsa.PublicKey
|
||
creator []byte
|
||
}
|
||
|
||
type ECDSASignature struct {
|
||
R, S *big.Int
|
||
}
|
||
|
||
// NewSigner 根据配置创建一个用户签名
|
||
func NewSigner(conf *Config) (*Signer, error) {
|
||
sId, err := serializeIdentity(conf.IdentityPath, conf.MSPID)
|
||
if err != nil {
|
||
return nil, errors.WithStack(err)
|
||
}
|
||
|
||
//加载私钥
|
||
privateKey, err := loadPrivateKey(conf.KeyPath)
|
||
if err != nil {
|
||
return nil, errors.WithStack(err)
|
||
}
|
||
|
||
//加载公钥,需要先调取用户证书,再从证书中获取公钥
|
||
publicKey, err := loadPublicKey(conf.IdentityPath)
|
||
if err != nil {
|
||
return nil, errors.WithStack(err)
|
||
}
|
||
return &Signer{
|
||
creator: sId,
|
||
privateKey: privateKey,
|
||
publicKeyKey: publicKey,
|
||
}, nil
|
||
}
|
||
|
||
// 序列化身份,客户端证书和组织id
|
||
func serializeIdentity(clientCert string, mspID string) ([]byte, error) {
|
||
//获取证书并返回证书里的内容b
|
||
b, err := os.ReadFile(clientCert)
|
||
if err != nil {
|
||
return nil, errors.WithStack(err)
|
||
}
|
||
if err := validateEnrollmentCertificate(b); err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
//Decode将在输入中找到下一个PEM格式的块(证书、私钥等)。它返回该块和输入的其余部分。
|
||
/*pem格式中的块数据 pem.Block
|
||
type Block struct {
|
||
Type string // 类型,如 "CERTIFICATE"、"PRIVATE KEY"、"PUBLIC KEY" 等
|
||
Headers map[string]string // 标题,如 "Proc-Type"、"DEK-Info" 等
|
||
Bytes []byte // 数据部分的字节切片
|
||
}*/
|
||
bl, _ := pem.Decode(b)
|
||
if bl == nil {
|
||
return nil, err
|
||
}
|
||
|
||
//从给定的ASN.1 DER数据中解析单个证书,证书结构体如下
|
||
/*type Certificate struct {
|
||
Raw []byte // 完整的 DER 编码证书数据
|
||
RawTBSCertificate []byte // 证书内容的 DER 编码(不包含签名)
|
||
RawSubjectPublicKeyInfo []byte // 主体公钥的 DER 编码
|
||
RawSubject []byte // 主体字段的 DER 编码
|
||
RawIssuer []byte // 颁发者字段的 DER 编码
|
||
SignatureAlgorithm SignatureAlgorithm // 签名算法
|
||
PublicKeyAlgorithm PublicKeyAlgorithm // 公钥算法
|
||
PublicKey interface{} // 公钥
|
||
Version int // 证书版本号
|
||
SerialNumber *big.Int // 序列号
|
||
Issuer pkix.Name // 颁发者
|
||
Subject pkix.Name // 主体
|
||
NotBefore time.Time // 有效期开始时间
|
||
NotAfter time.Time // 有效期结束时间
|
||
KeyUsage KeyUsage // 密钥用途
|
||
ExtKeyUsage []ExtKeyUsage // 扩展密钥用途
|
||
UnknownExtKeyUsage []asn1.ObjectIdentifier // 未知的扩展密钥用途
|
||
BasicConstraintsValid bool // 是否启用基本约束验证
|
||
IsCA bool // 是否是证书颁发机构
|
||
MaxPathLen int // 最大路径长度约束
|
||
MaxPathLenZero bool // 最大路径长度约束是否为零
|
||
SubjectKeyId []byte // 主体密钥标识符
|
||
AuthorityKeyId []byte // 颁发者密钥标识符
|
||
OCSPServer []string // OCSP 服务器列表
|
||
IssuingCertificateURL []string // 颁发者证书 URL 列表
|
||
DNSNames []string // 域名列表
|
||
EmailAddresses []string // 电子邮件地址列表
|
||
IPAddresses []net.IP // IP 地址列表
|
||
URIs []string // 统一资源标识符列表
|
||
PermittedDNSDomainsCritical bool // 允许的 DNS 域名是否为关键扩展
|
||
PermittedDNSDomains []string // 允许的 DNS 域名列表
|
||
CRLDistributionPointsCritical bool // CRL 分发点是否为关键扩展
|
||
CRLDistributionPoints []string // CRL 分发点列表
|
||
PolicyIdentifiers []asn1.ObjectIdentifier // 策略标识符
|
||
// ... 其他字段
|
||
}*/
|
||
key, err := x509.ParseCertificate(bl.Bytes)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
//将公钥对象转换为 DER 编码的 PKIX
|
||
//(Public-Key Infrastructure Cross-Certificate Interoperability Specification)公钥格式的字节切片
|
||
publicKeyBytes, err := x509.MarshalPKIXPublicKey(key.PublicKey)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
sId := &pb.Creator{
|
||
Mspid: mspID,
|
||
IdBytes: publicKeyBytes,
|
||
}
|
||
return util.MarshalOrPanic(sId), nil
|
||
}
|
||
|
||
// 验证注册证书
|
||
func validateEnrollmentCertificate(b []byte) error {
|
||
bl, _ := pem.Decode(b)
|
||
if bl == nil {
|
||
return errors.Errorf("enrollment certificate isn't a valid PEM block")
|
||
}
|
||
|
||
if bl.Type != "CERTIFICATE" {
|
||
return errors.Errorf("enrollment certificate should be a certificate, got a %s instead", strings.ToLower(bl.Type))
|
||
}
|
||
|
||
//从给定的ASN.1 DER数据中解析单个证书
|
||
if _, err := x509.ParseCertificate(bl.Bytes); err != nil {
|
||
return errors.Errorf("enrollment certificate is not a valid x509 certificate: %v", err)
|
||
}
|
||
return nil
|
||
}
|
||
|
||
func (si *Signer) Serialize() ([]byte, error) {
|
||
return si.creator, nil
|
||
}
|
||
|
||
// 返回一个长度为 32 的字节数组,表示计算得到的 SHA256 校验和
|
||
func (si *Signer) Sign(msg []byte) ([]byte, error) {
|
||
digest := sha256.Sum256(msg)
|
||
return signECDSA(si.privateKey, digest[:])
|
||
}
|
||
|
||
// 验证 ECDSA 签名
|
||
func (si *Signer) Verify(signature, msg []byte) bool {
|
||
//计算消息的哈希值
|
||
digest := sha256.Sum256(msg)
|
||
return verifyECDSA(si.publicKeyKey, signature, digest[:])
|
||
}
|
||
|
||
// Verify 验证签名是否正确
|
||
func Verify(creatorBytes []byte, signature, msg []byte) error {
|
||
creator, err := util.UnmarshalCreator(creatorBytes)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
|
||
publicKey, err := x509.ParsePKIXPublicKey(creator.IdBytes)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
publicKeyKey := publicKey.(*ecdsa.PublicKey)
|
||
digest := sha256.Sum256(msg)
|
||
if !verifyECDSA(publicKeyKey, signature, digest[:]) {
|
||
return errors.New("verifySignature error")
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
// 根据文件加载用户的私钥
|
||
func loadPrivateKey(file string) (*ecdsa.PrivateKey, error) {
|
||
b, err := os.ReadFile(file)
|
||
if err != nil {
|
||
return nil, errors.WithStack(err)
|
||
}
|
||
bl, _ := pem.Decode(b)
|
||
if bl == nil {
|
||
return nil, errors.Errorf("failed to decode PEM block from %s", file)
|
||
}
|
||
key, err := parsePrivateKey(bl.Bytes)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
return key.(*ecdsa.PrivateKey), nil
|
||
}
|
||
|
||
// 根据文件加载用户的公钥
|
||
func loadPublicKey(file string) (*ecdsa.PublicKey, error) {
|
||
b, err := os.ReadFile(file)
|
||
if err != nil {
|
||
return nil, errors.WithStack(err)
|
||
}
|
||
bl, _ := pem.Decode(b)
|
||
if bl == nil {
|
||
return nil, errors.Errorf("failed to decode PEM block from %s", file)
|
||
}
|
||
key, err := x509.ParseCertificate(bl.Bytes)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
publicKey := key.PublicKey
|
||
return publicKey.(*ecdsa.PublicKey), nil
|
||
}
|
||
|
||
func parsePrivateKey(der []byte) (crypto.PrivateKey, error) {
|
||
// OpenSSL 1.0.0 generates PKCS#8 keys.
|
||
if key, err := x509.ParsePKCS8PrivateKey(der); err == nil {
|
||
switch key := key.(type) {
|
||
// Fabric only supports ECDSA at the moment.
|
||
case *ecdsa.PrivateKey:
|
||
return key, nil
|
||
default:
|
||
return nil, errors.Errorf("found unknown private key type (%T) in PKCS#8 wrapping", key)
|
||
}
|
||
}
|
||
|
||
// OpenSSL ecparam generates SEC1 EC private keys for ECDSA.
|
||
key, err := x509.ParseECPrivateKey(der)
|
||
if err != nil {
|
||
return nil, errors.Errorf("failed to parse private key: %v", err)
|
||
}
|
||
|
||
return key, nil
|
||
}
|
||
|
||
func signECDSA(k *ecdsa.PrivateKey, digest []byte) (signature []byte, err error) {
|
||
r, s, err := ecdsa.Sign(rand.Reader, k, digest)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
return asn1.Marshal(ECDSASignature{R: r, S: s})
|
||
}
|
||
|
||
func verifyECDSA(pubKey *ecdsa.PublicKey, signature, digest []byte) bool {
|
||
|
||
// 解析 ASN.1 编码的签名数据
|
||
var sig ECDSASignature
|
||
_, err := asn1.Unmarshal(signature, &sig)
|
||
if err != nil {
|
||
return false
|
||
}
|
||
|
||
// 使用 ECDSA 签名算法对消息的哈希值进行验证
|
||
return ecdsa.Verify(pubKey, digest, sig.R, sig.S)
|
||
}
|