package manager import ( "context" "encoding/base64" "encoding/json" "fmt" "net/http" "strings" "chainweaver.org.cn/chainweaver/did/core" "chainweaver.org.cn/chainweaver/did/core/crypto" "chainweaver.org.cn/chainweaver/did/did-kms/kms" "chainweaver.org.cn/chainweaver/did/did-mgr-common-service/errorcode" "chainweaver.org.cn/chainweaver/did/did-mgr-common-service/internal/config" "chainweaver.org.cn/chainweaver/did/did-mgr-common-service/internal/db" "chainweaver.org.cn/chainweaver/did/did-mgr-common-service/internal/middleware" "chainweaver.org.cn/chainweaver/did/did-mgr-common-service/internal/svc" "chainweaver.org.cn/chainweaver/did/did-mgr-common-service/internal/types" "chainweaver.org.cn/chainweaver/did/did-mgr-common-service/internal/utils" "chainweaver.org.cn/chainweaver/servicecommon/res/code" "google.golang.org/grpc/metadata" "github.com/zeromicro/go-zero/core/logx" ) type KeyAddLogic struct { logx.Logger ctx context.Context svcCtx *svc.ServiceContext } func NewKeyAddLogic(ctx context.Context, svcCtx *svc.ServiceContext) *KeyAddLogic { return &KeyAddLogic{ Logger: logx.WithContext(ctx), ctx: ctx, svcCtx: svcCtx, } } func (l *KeyAddLogic) KeyAdd(req *types.KeyPairReq) (resp *types.CommonResp, err error) { resp = &types.CommonResp{ Code: types.SucceedCode, Msg: types.SucceedMsg, } token := l.ctx.Value("claims").(*middleware.TokenUser) if req.Mnemonic != "" && len(strings.Split(req.Mnemonic, " ")) != 12 { err = fmt.Errorf("expect 12 words,mnemonic[%s]", req.Mnemonic) errorcode.RequestErrorParametersFormat.BuildResult(resp, err) l.Logger.Info("add key failed,err[%s]", err.Error()) return resp, nil } priKey, err := base64.StdEncoding.DecodeString(req.PrivateKey) if err != nil { errorcode.ParseErrorParsePrivateKeyFromPEM.BuildResult(resp, err) logx.Errorf("private key [%s] Parse err[%s]", req.PrivateKey, err.Error()) return resp, nil } // 检查秘钥对是否有效,并计算address privateKey, err := crypto.ParsePrivateKeyFromPEM(priKey) if err != nil { errorcode.ParseErrorParsePrivateKeyFromPEM.BuildResult(resp, err) logx.Errorf("private key [%s] Parse err[%s]", req.PrivateKey, err.Error()) return resp, nil } pkPem, _ := privateKey.PublicKey().String() address, _ := utils.ComputeEVMAddressFromPKPEM([]string{pkPem}) // 检查是否已经存在did中了,如果已经存在,则只需添加到本地数据库,并将keypair 添加到kms中 req.PrivateKey = string(priKey) isExist, err := l.checkKeyIsExist(req, address[0], token.UserName) if err != nil { errorcode.MysqlErrorUpdate.BuildResult(resp, err) l.Logger.Info("check key isExist failed,err[%s]", err.Error()) return resp, nil } if isExist { return resp, nil } //去运营端根据did address查询did 是否已经存在,不存在则添加到当前did中 proxy := config.ConfigIns.Service.DidProxy targetUrl := config.ConfigIns.Service.Did + utils.DIdGetDocumentByAddressUrl docReq := &utils.DocumentGetByAddressRequest{Address: address[0]} qdoc, err := utils.GetDocument(targetUrl, proxy, docReq) if err != nil && !strings.Contains(err.Error(), "record not found") { errorcode.DocumentRequestError.BuildResult(resp, err) logx.Errorf("get [%s] document by address of remote did service, err[%s]", address[0], err.Error()) return resp, nil } if qdoc != nil { errorcode.DidPrivateKeyUsageError.BuildResult(resp) logx.Errorf("address[%s] document[%s] already exist", address[0], qdoc.Id) return resp, nil } //1.kms 添加 res, err := l.AddKeyPair(req) if err != nil { errorcode.KmsAddDataError.BuildResult(resp, err) l.Logger.Info("kms add key failed,err[%s]", err.Error()) return resp, nil } plaintext, err := l.genAddKeyDocument(token.EnterpriseDid, []string{res.Data}, []string{pkPem}) if err != nil { errorcode.DocumentGenerateError.BuildResult(resp, err) l.Logger.Info("document generate failed,err[%s]", err.Error()) return resp, nil } doc := &core.Document{} err = json.Unmarshal(plaintext, doc) if err != nil { errorcode.MarshalOrUnMarshalError.BuildResult(resp, err) l.Logger.Info(" Unmarshal failed,err[%s]", err.Error()) return resp, nil } plaintextStr := base64.StdEncoding.EncodeToString(plaintext) signResp, err := Sign(l.svcCtx.KmsClient, plaintextStr, res.Data, token.EnterpriseDid) if err != nil { errorcode.KmsSignRequestError.BuildResult(resp, err) l.Logger.Info("kms sign failed,err[%s]", err.Error()) return resp, nil } verificationMetho := signResp.Data.VerificationMethod if signResp.Data.VerificationMethod == "" { for _, v := range doc.VerificationMethod { if v.Address == res.Data { verificationMetho = v.Id break } } } if verificationMetho == "" { err = fmt.Errorf("did does not have the expected address") errorcode.DocumentGenerateError.BuildResult(resp, err) l.Logger.Info(err.Error()) return resp, nil } proof := &Proof{ Created: signResp.Data.Created, Type: signResp.Data.Type, ProofValue: signResp.Data.Signature, VerificationMethod: verificationMetho, } document, err := l.registerAddKeyDocument(token.EnterpriseDid, proof) if err != nil { errorcode.DocumentRegisterError.BuildResult(resp, err) l.Logger.Info(" register document failed,err[%s]", err.Error()) return resp, nil } // 将did document信息保存至kms _, err = AddDidInfo(l.svcCtx.KmsClient, plaintext, document.Id) if err != nil { errorcode.KmsAddDataError.BuildResult(resp, err) l.Logger.Info("kms add did failed,err[%s]", err.Error()) return resp, nil } //2.local save err = db.CreateTable(&db.KeyInfo{ Address: res.Data, AdminName: token.UserName, State: 1, Enable: 2, Remark: req.Mnemonic, }) if err != nil { errorcode.MysqlErrorInsertInfo.BuildResult(resp, err) l.Logger.Info("create key info failed,err[%s]", err.Error()) return resp, nil } return resp, nil } func (l *KeyAddLogic) AddKeyPair(req *types.KeyPairReq) (*kms.AddKeyPairResponse, error) { in := &kms.AddKeyPairRequest{ PrivateKey: req.PrivateKey, Mnemonic: strings.Split(req.Mnemonic, " "), } res, err := l.svcCtx.KmsClient.AddKeyPair(l.ctx, in) if err != nil { return nil, err } else if code.CodeOk.NotEqualsInt32(res.Code) { return nil, fmt.Errorf(res.Msg) } return res, err } // DidUpdateGenerateReq 结构体,用于发送更新 DID 文档的请求 type DidUpdateGenerateReq struct { Did string `json:"did"` // DID 文档 PublicKey []string `json:"publicKey"` // 新的公钥列表 Addresses []string `json:"addresses"` // 地址列表 } // DidUpdateResp 结构体,用于返回更新 DID 文档的结果 type DidUpdateResp struct { Code int32 `json:"code"` // 状态码 Msg string `json:"msg"` // 消息 Data *DidUpdateData `json:"data"` // 数据部分 } // DidUpdateData 结构体,包含更新的 DID 文档信息和明文数据 type DidUpdateData struct { Plaintext Plaintext `json:"plaintext"` // 明文数据 Did string `json:"did"` // DID 文档 } // Plaintext 结构体,包含类型和明文数据 type Plaintext struct { Type int `json:"type"` // 类型 Plaintext []byte `json:"plaintext"` // 明文数据 } func (l *KeyAddLogic) genAddKeyDocument(enterpriseDid string, addresses, pubKeys []string) (plaintext []byte, err error) { // 企业DID已创建,添加地址 updateResp := &DidUpdateGenerateReq{ Did: enterpriseDid, PublicKey: pubKeys, Addresses: addresses, } // DID绑定链地址 // 公钥和DID发给DID服务,DID服务返回document,让用户对document签名 param, _ := json.Marshal(updateResp) proxy := config.ConfigIns.Service.DidProxy targetUrl := config.ConfigIns.Service.Did + utils.DIdUpdateGenerateUrl + utils.DIdUpdateGenerateActionAdd respBz, internalErr := utils.DidServerReq(targetUrl, proxy, http.MethodPost, param) if internalErr != nil { l.Logger.Errorf("GetDocument HttpServerReq failed, internalErr=%s", internalErr.Error()) return nil, internalErr } didResp := &DidUpdateResp{} internalErr = json.Unmarshal(respBz, didResp) if internalErr != nil { l.Logger.Infof("GetDocument didResp json.Unmarshal failed.err = %s", internalErr.Error()) return nil, internalErr } if didResp.Code != types.SucceedCode { l.Logger.Infof("GetDocument didResp.Code != 200000.didResp = %v", *didResp) return nil, fmt.Errorf(didResp.Msg) } return didResp.Data.Plaintext.Plaintext, nil } // DidUpdateProof 结构体包含 DID 和证明列表,用于更新 DID 的证明 type DidUpdateProof struct { Did string `json:"did"` Proof []*Proof `json:"proof"` } func (l *KeyAddLogic) registerAddKeyDocument(enterpriseDid string, proof *Proof) (*core.Document, error) { didUpdateProof := &DidUpdateProof{ Did: enterpriseDid, } didUpdateProof.Proof = append(didUpdateProof.Proof, proof) param, _ := json.Marshal(didUpdateProof) targetUrl := config.ConfigIns.Service.Did + utils.DIdUpdateUrl proxy := config.ConfigIns.Service.DidProxy // 发送更新企业did请求到did服务 respBz, internalErr := utils.DidServerReq(targetUrl, proxy, http.MethodPost, param) if internalErr != nil { l.Logger.Errorf("RegisterDocument HttpServerReq failed, internalErr=%s", internalErr.Error()) return nil, internalErr } didResp := &DIDRegisterResp{} internalErr = json.Unmarshal(respBz, didResp) if internalErr != nil { l.Logger.Infof("RegisterDocument didResp json.Unmarshal failed.err = %s", internalErr.Error()) return nil, internalErr } if didResp.Code != types.SucceedCode { err := fmt.Errorf("RegisterDocument didResp.Code != 200000.didResp = %v", *didResp) l.Logger.Infof(err.Error()) return nil, err } return didResp.Data.Document, nil } func Sign(client kms.Kms, plaintext, address, did string) (*kms.SignResponse, error) { in := &kms.SignRequest{ Plaintext: plaintext, Address: address, } context := context.Background() // 在请求头中设置自定义参数 md := metadata.Pairs("did", did) ctx := metadata.NewOutgoingContext(context, md) res, err := client.Sign(ctx, in) if err != nil { return nil, err } else if code.CodeOk.NotEqualsInt32(res.Code) { return nil, fmt.Errorf(res.Msg) } return res, err } func (l *KeyAddLogic) checkKeyIsExist(req *types.KeyPairReq, address, userName string) (bool, error) { // 获得本地document did, err := db.FindDidInfo() if err != nil { return false, fmt.Errorf("query did failed,err[%s]", err.Error()) } doc := &core.Document{} err = json.Unmarshal([]byte(did.DocumentPlaintext), doc) if err != nil { return false, fmt.Errorf("unmarshal document failed,err[%s]", err.Error()) } isExist := false for _, vm := range doc.VerificationMethod { if vm.Address == address { isExist = true break } } if !isExist { return false, nil } //2.本地数据库检查是否已经存在 keyInfo, err := db.FindKeyByAddress(address) if keyInfo != nil { return false, nil } else if err != nil { l.Logger.Errorf("FindKeyByAddress %s failed,err: %s", address, err.Error()) } //1.kms 添加 res, err := l.AddKeyPair(req) if err != nil { err = fmt.Errorf("add key to kms failed,err[%s]", err.Error()) l.Logger.Info(err.Error()) return false, err } //2.local save err = db.CreateTable(&db.KeyInfo{ Address: res.Data, AdminName: userName, State: 1, Enable: 1, Remark: req.Mnemonic, }) if err != nil { err = fmt.Errorf("insert key info failed,err[%s]", err.Error()) l.Logger.Info("insert key info failed,err[%s]", err.Error()) return false, err } return true, nil }