修改加密
This commit is contained in:
parent
1a07ff016d
commit
5b2b5d0ba0
|
|
@ -46,7 +46,7 @@ const (
|
||||||
func init() {
|
func init() {
|
||||||
flag.StringVar(&nacosIp, "nacosIp", "120.55.12.245", "nacos ip address")
|
flag.StringVar(&nacosIp, "nacosIp", "120.55.12.245", "nacos ip address")
|
||||||
flag.IntVar(&nacosPort, "nacosPort", 8848, "nacos port")
|
flag.IntVar(&nacosPort, "nacosPort", 8848, "nacos port")
|
||||||
flag.StringVar(&nacosSpace, "nacosSpace", "voucher", "nacos space")
|
flag.StringVar(&nacosSpace, "nacosSpace", "voucher-test", "nacos space")
|
||||||
flag.StringVar(&nacosUsername, "nacosUsername", "nacos", "nacos passowrd")
|
flag.StringVar(&nacosUsername, "nacosUsername", "nacos", "nacos passowrd")
|
||||||
flag.StringVar(&nacosPassword, "nacosPassword", "LsxdNacos@2025", "nacos passowrd")
|
flag.StringVar(&nacosPassword, "nacosPassword", "LsxdNacos@2025", "nacos passowrd")
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
|
||||||
2
go.mod
2
go.mod
|
|
@ -3,12 +3,14 @@ module voucher
|
||||||
go 1.22.2
|
go 1.22.2
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
gitea.cdlsxd.cn/self-tools/l_crypt v1.0.0
|
||||||
github.com/ZZMarquis/gm v1.3.2
|
github.com/ZZMarquis/gm v1.3.2
|
||||||
github.com/aliyunmq/mq-http-go-sdk v1.0.3
|
github.com/aliyunmq/mq-http-go-sdk v1.0.3
|
||||||
github.com/apache/rocketmq-client-go/v2 v2.1.2
|
github.com/apache/rocketmq-client-go/v2 v2.1.2
|
||||||
github.com/brianvoe/gofakeit/v6 v6.28.0
|
github.com/brianvoe/gofakeit/v6 v6.28.0
|
||||||
github.com/bwmarrin/snowflake v0.3.0
|
github.com/bwmarrin/snowflake v0.3.0
|
||||||
github.com/duke-git/lancet/v2 v2.2.8
|
github.com/duke-git/lancet/v2 v2.2.8
|
||||||
|
github.com/emmansun/gmsm v0.29.8
|
||||||
github.com/envoyproxy/protoc-gen-validate v1.0.4
|
github.com/envoyproxy/protoc-gen-validate v1.0.4
|
||||||
github.com/go-kratos/kratos/contrib/config/nacos/v2 v2.0.0-20241105072421-f8b97f675b32
|
github.com/go-kratos/kratos/contrib/config/nacos/v2 v2.0.0-20241105072421-f8b97f675b32
|
||||||
github.com/go-kratos/kratos/v2 v2.8.2
|
github.com/go-kratos/kratos/v2 v2.8.2
|
||||||
|
|
|
||||||
5
go.sum
5
go.sum
|
|
@ -1,6 +1,8 @@
|
||||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||||
dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk=
|
dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk=
|
||||||
dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
|
dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
|
||||||
|
gitea.cdlsxd.cn/self-tools/l_crypt v1.0.0 h1:U0SBdxgrWNmbzPjEMuAtA51GeQ8PpRoZgN+SNoyqrQM=
|
||||||
|
gitea.cdlsxd.cn/self-tools/l_crypt v1.0.0/go.mod h1:Q7sEfyYLXGZ7i97HBJ7OaYLYyc4f9scUJK9Ev3ZGyDM=
|
||||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
github.com/BurntSushi/toml v1.1.0 h1:ksErzDEI1khOiGPgpwuI7x2ebx/uXQNw7xJpn9Eq1+I=
|
github.com/BurntSushi/toml v1.1.0 h1:ksErzDEI1khOiGPgpwuI7x2ebx/uXQNw7xJpn9Eq1+I=
|
||||||
github.com/BurntSushi/toml v1.1.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
|
github.com/BurntSushi/toml v1.1.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
|
||||||
|
|
@ -46,6 +48,8 @@ github.com/duke-git/lancet/v2 v2.2.8 h1:wlruXhliDe4zls1e2cYmz4qLc+WtcvrpcCnk1VJd
|
||||||
github.com/duke-git/lancet/v2 v2.2.8/go.mod h1:zGa2R4xswg6EG9I6WnyubDbFO/+A/RROxIbXcwryTsc=
|
github.com/duke-git/lancet/v2 v2.2.8/go.mod h1:zGa2R4xswg6EG9I6WnyubDbFO/+A/RROxIbXcwryTsc=
|
||||||
github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg=
|
github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg=
|
||||||
github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
|
github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
|
||||||
|
github.com/emmansun/gmsm v0.29.8 h1:py9RwKHe4sIxjgD9mtWSIF5bmspP7IXvQ70Rx8x22Ow=
|
||||||
|
github.com/emmansun/gmsm v0.29.8/go.mod h1:7UTFG3GmF8yxyZVB4HgpdeU0JoergL/i2OMGm5w02RA=
|
||||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||||
|
|
@ -129,7 +133,6 @@ github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeN
|
||||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||||
github.com/google/subcommands v1.2.0 h1:vWQspBTo2nEqTUFita5/KeEWlUL8kQObDFbub/EN9oE=
|
|
||||||
github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk=
|
github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk=
|
||||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// protoc-gen-go v1.34.2
|
// protoc-gen-go v1.31.0
|
||||||
// protoc v4.24.3
|
// protoc v3.12.4
|
||||||
// source: conf/conf.proto
|
// source: conf/conf.proto
|
||||||
|
|
||||||
package conf
|
package conf
|
||||||
|
|
@ -1429,7 +1429,7 @@ func file_conf_conf_proto_rawDescGZIP() []byte {
|
||||||
}
|
}
|
||||||
|
|
||||||
var file_conf_conf_proto_msgTypes = make([]protoimpl.MessageInfo, 17)
|
var file_conf_conf_proto_msgTypes = make([]protoimpl.MessageInfo, 17)
|
||||||
var file_conf_conf_proto_goTypes = []any{
|
var file_conf_conf_proto_goTypes = []interface{}{
|
||||||
(*Bootstrap)(nil), // 0: voucher.config.Bootstrap
|
(*Bootstrap)(nil), // 0: voucher.config.Bootstrap
|
||||||
(*Server)(nil), // 1: voucher.config.Server
|
(*Server)(nil), // 1: voucher.config.Server
|
||||||
(*Data)(nil), // 2: voucher.config.Data
|
(*Data)(nil), // 2: voucher.config.Data
|
||||||
|
|
@ -1484,7 +1484,7 @@ func file_conf_conf_proto_init() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if !protoimpl.UnsafeEnabled {
|
if !protoimpl.UnsafeEnabled {
|
||||||
file_conf_conf_proto_msgTypes[0].Exporter = func(v any, i int) any {
|
file_conf_conf_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
|
||||||
switch v := v.(*Bootstrap); i {
|
switch v := v.(*Bootstrap); i {
|
||||||
case 0:
|
case 0:
|
||||||
return &v.state
|
return &v.state
|
||||||
|
|
@ -1496,7 +1496,7 @@ func file_conf_conf_proto_init() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
file_conf_conf_proto_msgTypes[1].Exporter = func(v any, i int) any {
|
file_conf_conf_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
|
||||||
switch v := v.(*Server); i {
|
switch v := v.(*Server); i {
|
||||||
case 0:
|
case 0:
|
||||||
return &v.state
|
return &v.state
|
||||||
|
|
@ -1508,7 +1508,7 @@ func file_conf_conf_proto_init() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
file_conf_conf_proto_msgTypes[2].Exporter = func(v any, i int) any {
|
file_conf_conf_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
|
||||||
switch v := v.(*Data); i {
|
switch v := v.(*Data); i {
|
||||||
case 0:
|
case 0:
|
||||||
return &v.state
|
return &v.state
|
||||||
|
|
@ -1520,7 +1520,7 @@ func file_conf_conf_proto_init() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
file_conf_conf_proto_msgTypes[3].Exporter = func(v any, i int) any {
|
file_conf_conf_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
|
||||||
switch v := v.(*RocketMQ); i {
|
switch v := v.(*RocketMQ); i {
|
||||||
case 0:
|
case 0:
|
||||||
return &v.state
|
return &v.state
|
||||||
|
|
@ -1532,7 +1532,7 @@ func file_conf_conf_proto_init() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
file_conf_conf_proto_msgTypes[4].Exporter = func(v any, i int) any {
|
file_conf_conf_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
|
||||||
switch v := v.(*EventMap); i {
|
switch v := v.(*EventMap); i {
|
||||||
case 0:
|
case 0:
|
||||||
return &v.state
|
return &v.state
|
||||||
|
|
@ -1544,7 +1544,7 @@ func file_conf_conf_proto_init() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
file_conf_conf_proto_msgTypes[5].Exporter = func(v any, i int) any {
|
file_conf_conf_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {
|
||||||
switch v := v.(*Wechat); i {
|
switch v := v.(*Wechat); i {
|
||||||
case 0:
|
case 0:
|
||||||
return &v.state
|
return &v.state
|
||||||
|
|
@ -1556,7 +1556,7 @@ func file_conf_conf_proto_init() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
file_conf_conf_proto_msgTypes[6].Exporter = func(v any, i int) any {
|
file_conf_conf_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} {
|
||||||
switch v := v.(*Cmb); i {
|
switch v := v.(*Cmb); i {
|
||||||
case 0:
|
case 0:
|
||||||
return &v.state
|
return &v.state
|
||||||
|
|
@ -1568,7 +1568,7 @@ func file_conf_conf_proto_init() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
file_conf_conf_proto_msgTypes[7].Exporter = func(v any, i int) any {
|
file_conf_conf_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} {
|
||||||
switch v := v.(*WechatNotifyMQ); i {
|
switch v := v.(*WechatNotifyMQ); i {
|
||||||
case 0:
|
case 0:
|
||||||
return &v.state
|
return &v.state
|
||||||
|
|
@ -1580,7 +1580,7 @@ func file_conf_conf_proto_init() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
file_conf_conf_proto_msgTypes[8].Exporter = func(v any, i int) any {
|
file_conf_conf_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} {
|
||||||
switch v := v.(*Alarm); i {
|
switch v := v.(*Alarm); i {
|
||||||
case 0:
|
case 0:
|
||||||
return &v.state
|
return &v.state
|
||||||
|
|
|
||||||
|
|
@ -49,7 +49,7 @@ func (c *CmbMixRepoImpl) recordBody(ctx context.Context) {
|
||||||
|
|
||||||
func (s *CmbMixRepoImpl) OrderVerify(ctx context.Context, req *v1.CmbRequest) (*v1.CmbOrderRequest, error) {
|
func (s *CmbMixRepoImpl) OrderVerify(ctx context.Context, req *v1.CmbRequest) (*v1.CmbOrderRequest, error) {
|
||||||
|
|
||||||
bizStr, err := s.Verify(ctx, req)
|
bizStr, err := s.VerifyNew(ctx, req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err2.ErrorCmbVerifyFail(err.Error())
|
return nil, err2.ErrorCmbVerifyFail(err.Error())
|
||||||
}
|
}
|
||||||
|
|
@ -73,7 +73,7 @@ func (s *CmbMixRepoImpl) OrderVerify(ctx context.Context, req *v1.CmbRequest) (*
|
||||||
|
|
||||||
func (s *CmbMixRepoImpl) QueryVerify(ctx context.Context, req *v1.CmbRequest) (*v1.CmbQueryRequest, error) {
|
func (s *CmbMixRepoImpl) QueryVerify(ctx context.Context, req *v1.CmbRequest) (*v1.CmbQueryRequest, error) {
|
||||||
|
|
||||||
bizStr, err := s.Verify(ctx, req)
|
bizStr, err := s.VerifyNew(ctx, req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err2.ErrorCmbVerifyFail(err.Error())
|
return nil, err2.ErrorCmbVerifyFail(err.Error())
|
||||||
}
|
}
|
||||||
|
|
@ -97,7 +97,7 @@ func (s *CmbMixRepoImpl) QueryVerify(ctx context.Context, req *v1.CmbRequest) (*
|
||||||
|
|
||||||
func (s *CmbMixRepoImpl) ProductQueryVerify(ctx context.Context, req *v1.CmbRequest) (*v1.CmbQueryProductRequest, error) {
|
func (s *CmbMixRepoImpl) ProductQueryVerify(ctx context.Context, req *v1.CmbRequest) (*v1.CmbQueryProductRequest, error) {
|
||||||
|
|
||||||
bizStr, err := s.Verify(ctx, req)
|
bizStr, err := s.VerifyNew(ctx, req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -140,9 +140,32 @@ func (s *CmbMixRepoImpl) Verify(_ context.Context, req *v1.CmbRequest) (string,
|
||||||
return bizStr, nil
|
return bizStr, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *CmbMixRepoImpl) VerifyNew(_ context.Context, req *v1.CmbRequest) (string, error) {
|
||||||
|
|
||||||
|
str := cmb.SortStructStr(req)
|
||||||
|
|
||||||
|
//b, err := cmb.Verify(s.bc.Cmb.CmbSm2Puk, str, req.Sign)
|
||||||
|
b, err := cmb.VerifyBody(str, req.Sign, s.bc.Cmb.CmbSm2Puk)
|
||||||
|
if err != nil {
|
||||||
|
return "", err2.ErrorCmbVerifyFail(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
if !b {
|
||||||
|
return "", err2.ErrorCmbVerifyFail("签名验证失败")
|
||||||
|
}
|
||||||
|
|
||||||
|
bizStr, err := cmb.DecryptBody(&cmb.Decrypts{req.EncryptBody, s.bc.Cmb.CmbSm2Pik})
|
||||||
|
if err != nil {
|
||||||
|
return "", err2.ErrorCmbBizContentDecryptFail(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(bizStr), nil
|
||||||
|
}
|
||||||
|
|
||||||
func (s *CmbMixRepoImpl) GetRequest(_ context.Context, reqBo *bo.CmbRequestBo) (*v1.CmbRequest, error) {
|
func (s *CmbMixRepoImpl) GetRequest(_ context.Context, reqBo *bo.CmbRequestBo) (*v1.CmbRequest, error) {
|
||||||
|
|
||||||
encryptBody, err := cmb.Encrypt(s.bc.Cmb.CmbSm2Puk, reqBo.BizContent)
|
//encryptBody, err := cmb.Encrypt(s.bc.Cmb.CmbSm2Puk, reqBo.BizContent)
|
||||||
|
encryptBody, err := cmb.EncryptBody(&cmb.Encrypts{SoaPubKey: s.bc.Cmb.CmbSm2Puk, JsonParam: reqBo.BizContent})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -160,7 +183,7 @@ func (s *CmbMixRepoImpl) GetRequest(_ context.Context, reqBo *bo.CmbRequestBo) (
|
||||||
|
|
||||||
str := fmt.Sprintf("%s?%s", reqBo.FuncName, cmb.SortStructStr(req))
|
str := fmt.Sprintf("%s?%s", reqBo.FuncName, cmb.SortStructStr(req))
|
||||||
|
|
||||||
sing, err := cmb.Sign(s.bc.Cmb.Sm2Prk, str)
|
sing, err := cmb.Sign(s.bc.Cmb.CmbSm2Pik, str)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -180,7 +203,8 @@ func (s *CmbMixRepoImpl) GetMockRequest(_ context.Context, bizContent string) (*
|
||||||
return nil, errors.New("mock cmb sm2 pik is empty")
|
return nil, errors.New("mock cmb sm2 pik is empty")
|
||||||
}
|
}
|
||||||
|
|
||||||
encryptBody, err := cmb.Encrypt(s.bc.Cmb.Sm2Puk, bizContent)
|
//encryptBody, err := cmb.Encrypt(s.bc.Cmb.Sm2Puk, bizContent)
|
||||||
|
encryptBody, err := cmb.EncryptBody(&cmb.Encrypts{SoaPubKey: s.bc.Cmb.CmbSm2Puk, JsonParam: bizContent})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -196,7 +220,7 @@ func (s *CmbMixRepoImpl) GetMockRequest(_ context.Context, bizContent string) (*
|
||||||
Sign: "",
|
Sign: "",
|
||||||
}
|
}
|
||||||
|
|
||||||
sign, err := cmb.Sign(s.bc.Cmb.CmbSm2Pik, cmb.SortStructStr(req))
|
sign, err := cmb.SignBody(cmb.SortStructStr(req), s.bc.Cmb.CmbSm2Pik)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -210,7 +234,8 @@ func (s *CmbMixRepoImpl) VerifyResponse(_ context.Context, req *v1.CmbReply) err
|
||||||
|
|
||||||
str := cmb.SortStructStr(req)
|
str := cmb.SortStructStr(req)
|
||||||
|
|
||||||
b, err := cmb.Verify(s.bc.Cmb.CmbSm2Puk, str, req.Sign)
|
//b, err := cmb.Verify(s.bc.Cmb.CmbSm2Puk, str, req.Sign)
|
||||||
|
b, err := cmb.VerifyBody(str, req.Sign, s.bc.Cmb.CmbSm2Puk)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -235,14 +260,14 @@ func (s *CmbMixRepoImpl) GetResponse(_ context.Context, reqBo *bo.CmbResponseBo)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(reqBo.BizContent) > 0 {
|
if len(reqBo.BizContent) > 0 {
|
||||||
encryptBody, err := cmb.Encrypt(s.bc.Cmb.CmbSm2Puk, reqBo.BizContent)
|
encryptBody, err := cmb.EncryptBody(&cmb.Encrypts{SoaPubKey: s.bc.Cmb.CmbSm2Puk, JsonParam: reqBo.BizContent})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
reply.EncryptBody = encryptBody
|
reply.EncryptBody = encryptBody
|
||||||
}
|
}
|
||||||
|
|
||||||
sign, err := cmb.Sign(s.bc.Cmb.Sm2Prk, cmb.SortStructStr(reply))
|
sign, err := cmb.SignBody(cmb.SortStructStr(reply), s.bc.Cmb.Sm2Prk)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -295,6 +320,7 @@ func (s *CmbMixRepoImpl) Decrypt(_ context.Context, encryptBody string) (string,
|
||||||
if len(s.bc.Cmb.CmbSm2Pik) == 0 {
|
if len(s.bc.Cmb.CmbSm2Pik) == 0 {
|
||||||
return "", errors.New("mock CmbSm2Pik is empty")
|
return "", errors.New("mock CmbSm2Pik is empty")
|
||||||
}
|
}
|
||||||
|
rs, err := cmb.DecryptBody(&cmb.Decrypts{EncryptBody: encryptBody, PrivateKey: s.bc.Cmb.CmbSm2Pik})
|
||||||
return cmb.Decrypt(s.bc.Cmb.CmbSm2Pik, encryptBody)
|
return string(rs), err
|
||||||
|
//return cmb.Decrypt(s.bc.Cmb.CmbSm2Pik, encryptBody)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,101 @@
|
||||||
|
package aes
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto/aes"
|
||||||
|
"crypto/cipher"
|
||||||
|
"crypto/rand"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"math/big"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
TestKey = "ROK0nAaxcsI0KRCy8VJgLlnlfjWYAOHk"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 加密函数
|
||||||
|
func Encrypt(plaintext, key []byte) ([]byte, error) {
|
||||||
|
block, err := aes.NewCipher(key)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 加密后的数据会比原文长,因为需要添加一些填充字节
|
||||||
|
// PKCS#7填充
|
||||||
|
plaintext = pkcs7Padding(plaintext, block.BlockSize())
|
||||||
|
|
||||||
|
// 创建一个cipher.BlockMode,这里使用CBC模式
|
||||||
|
// 需要一个iv(初始化向量),它的长度和Block的块大小相同
|
||||||
|
ciphertext := make([]byte, aes.BlockSize+len(plaintext))
|
||||||
|
iv := ciphertext[:aes.BlockSize]
|
||||||
|
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
mode := cipher.NewCBCEncrypter(block, iv)
|
||||||
|
mode.CryptBlocks(ciphertext[aes.BlockSize:], plaintext)
|
||||||
|
|
||||||
|
// 返回的密文包括iv和加密后的数据
|
||||||
|
return ciphertext, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解密函数
|
||||||
|
func Decrypt(ciphertext, key []byte) ([]byte, error) {
|
||||||
|
block, err := aes.NewCipher(key)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(ciphertext) < aes.BlockSize {
|
||||||
|
return nil, fmt.Errorf("ciphertext too short")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 提取iv
|
||||||
|
iv := ciphertext[:aes.BlockSize]
|
||||||
|
ciphertext = ciphertext[aes.BlockSize:]
|
||||||
|
|
||||||
|
// 创建一个cipher.BlockMode
|
||||||
|
mode := cipher.NewCBCDecrypter(block, iv)
|
||||||
|
|
||||||
|
// 解密
|
||||||
|
mode.CryptBlocks(ciphertext, ciphertext)
|
||||||
|
|
||||||
|
// 去除PKCS#7填充
|
||||||
|
plaintext, err := pkcs7Unpadding(ciphertext, block.BlockSize())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return plaintext, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// PKCS#7填充
|
||||||
|
func pkcs7Padding(ciphertext []byte, blockSize int) []byte {
|
||||||
|
padding := blockSize - len(ciphertext)%blockSize
|
||||||
|
padtext := bytes.Repeat([]byte{byte(padding)}, padding)
|
||||||
|
return append(ciphertext, padtext...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 去除PKCS#7填充
|
||||||
|
func pkcs7Unpadding(ciphertext []byte, blockSize int) ([]byte, error) {
|
||||||
|
length := len(ciphertext)
|
||||||
|
unpadding := int(ciphertext[length-1])
|
||||||
|
if unpadding > blockSize || unpadding == 0 {
|
||||||
|
return nil, fmt.Errorf("invalid padding")
|
||||||
|
}
|
||||||
|
return ciphertext[:(length - unpadding)], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GenerateRandomStringCrypto(length int) (string, error) {
|
||||||
|
const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
|
||||||
|
var bytes = make([]byte, length)
|
||||||
|
for i := range bytes {
|
||||||
|
num, err := rand.Int(rand.Reader, big.NewInt(int64(len(charset))))
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
bytes[i] = charset[num.Int64()]
|
||||||
|
}
|
||||||
|
return string(bytes), nil
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,34 @@
|
||||||
|
package aes
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestCreateKey(t *testing.T) {
|
||||||
|
key, _ := GenerateRandomStringCrypto(32)
|
||||||
|
fmt.Printf(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAesEncrypt(t *testing.T) {
|
||||||
|
|
||||||
|
fmt.Printf("%s\n", encrypt())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAesDecrypt(t *testing.T) {
|
||||||
|
data := "dk9trGQn2kZyE0rmfYglxwNmr7gc0DGFWvnPDVpLHfzwPiaWKkr+ZffKu0wj9/DQurIgjrQZIW8uV5/e+jxzBE6+5YZuKcT9+5xZSv6ieiXB7WUrKKnjxX/bHEaWgfoa0eDqEBhGth+mgql7qWFZLmuXpoKuAc0FPhdHTiynAttqjNMU4rMeXq52yJLuXrKHGlavhd9DETNDrhPFzyIAw7XjoMfGtBIFkXyDK5X+AKGkRvMJ8KfXR3lk9sSzTyGKRK/S"
|
||||||
|
|
||||||
|
res, err := Decrypt([]byte(data), []byte(TestKey))
|
||||||
|
fmt.Println(string(res), err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func encrypt() string {
|
||||||
|
data := "{\"pay_channel_id\":8935141660703064070,\"out_trade_no\":\"asdadasdas\",\"order_type\":1,\"amount\":1,\"desc\":\"abc\",\"ext_json\":\"\",\"app_id\":5476377146882523138,\"timestamp\":53612533412643}{\"pay_channel_id\":8935141660703064070,\"out_trade_no\":\"asdadasdas\",\"order_type\":1,\"amount\":1,\"desc\":\"abc\",\"ext_json\":\"\",\"app_id\":5476377146882523138,\"timestamp\":53612533412643}{\"pay_channel_id\":8935141660703064070,\"out_trade_no\":\"asdadasdas\",\"order_type\":1,\"amount\":1,\"desc\":\"abc\",\"ext_json\":\"\",\"app_id\":5476377146882523138,\"timestamp\":53612533412643}{\"pay_channel_id\":8935141660703064070,\"out_trade_no\":\"asdadasdas\",\"order_type\":1,\"amount\":1,\"desc\":\"abc\",\"ext_json\":\"\",\"app_id\":5476377146882523138,\"timestamp\":53612533412643}{\"pay_channel_id\":8935141660703064070,\"out_trade_no\":\"asdadasdas\",\"order_type\":1,\"amount\":1,\"desc\":\"abc\",\"ext_json\":\"\",\"app_id\":5476377146882523138,\"timestamp\":53612533412643}{\"pay_channel_id\":8935141660703064070,\"out_trade_no\":\"asdadasdas\",\"order_type\":1,\"amount\":1,\"desc\":\"abc\",\"ext_json\":\"\",\"app_id\":5476377146882523138,\"timestamp\":53612533412643}{\"pay_channel_id\":8935141660703064070,\"out_trade_no\":\"asdadasdas\",\"order_type\":1,\"amount\":1,\"desc\":\"abc\",\"ext_json\":\"\",\"app_id\":5476377146882523138,\"timestamp\":53612533412643}{\"pay_channel_id\":8935141660703064070,\"out_trade_no\":\"asdadasdas\",\"order_type\":1,\"amount\":1,\"desc\":\"abc\",\"ext_json\":\"\",\"app_id\":5476377146882523138,\"timestamp\":53612533412643}"
|
||||||
|
|
||||||
|
en, err := Encrypt([]byte(data), []byte(TestKey))
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(en)
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,162 @@
|
||||||
|
package rsa
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/rand"
|
||||||
|
"crypto/rsa"
|
||||||
|
"crypto/sha256"
|
||||||
|
"crypto/x509"
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/pem"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// parseRSAPublicKeyFromPEM 解析PEM编码的RSA公钥
|
||||||
|
func parseRSAPublicKeyFromPEM(pemData []byte) (*rsa.PublicKey, error) {
|
||||||
|
block, _ := pem.Decode(pemData)
|
||||||
|
if block == nil || block.Type != "PUBLIC KEY" {
|
||||||
|
return nil, fmt.Errorf("failed to parse PEM block containing the RSA public key")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub, err := x509.ParsePKIXPublicKey(block.Bytes)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
switch pub := pub.(type) {
|
||||||
|
case *rsa.PublicKey:
|
||||||
|
return pub, nil
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("unknown public key type in PKIX wrapping")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// encrypt 使用RSA公钥加密数据
|
||||||
|
func Encrypt(publicKeyPEM string, plaintext string) ([]byte, error) {
|
||||||
|
var encryptedData []byte
|
||||||
|
// 将PEM编码的公钥转换为[]byte
|
||||||
|
pemData := []byte(publicKeyPEM)
|
||||||
|
|
||||||
|
// 解析PEM数据以获取公钥
|
||||||
|
pubKey, err := parseRSAPublicKeyFromPEM(pemData)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
hash := sha256.New()
|
||||||
|
maxBlockSize := pubKey.Size() - 2*hash.Size() - 2
|
||||||
|
// 创建用于加密的随机填充
|
||||||
|
label := []byte("") // OAEP标签,对于某些情况可能是非空的
|
||||||
|
for len(plaintext) > 0 {
|
||||||
|
blockSize := maxBlockSize
|
||||||
|
if len(plaintext) < maxBlockSize {
|
||||||
|
blockSize = len(plaintext)
|
||||||
|
}
|
||||||
|
block := plaintext[:blockSize]
|
||||||
|
encryptedBlock, err := rsa.EncryptOAEP(hash, rand.Reader, pubKey, []byte(block), label)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
encryptedData = append(encryptedData, encryptedBlock...)
|
||||||
|
plaintext = plaintext[blockSize:]
|
||||||
|
}
|
||||||
|
return encryptedData, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseRSAPrivateKeyFromPEM 解析PEM编码的RSA私钥
|
||||||
|
func parseRSAPrivateKeyFromPEM(pemData []byte) (*rsa.PrivateKey, error) {
|
||||||
|
block, _ := pem.Decode(pemData)
|
||||||
|
if block == nil || block.Type != "RSA PRIVATE KEY" {
|
||||||
|
return nil, fmt.Errorf("failed to parse PEM block containing the RSA private key")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 尝试使用PKCS#1 v1.5
|
||||||
|
priv, err := x509.ParsePKCS1PrivateKey(block.Bytes)
|
||||||
|
if err != nil {
|
||||||
|
// 如果失败,尝试使用PKCS#8
|
||||||
|
privInterface, err := x509.ParsePKCS8PrivateKey(block.Bytes)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
switch k := privInterface.(type) {
|
||||||
|
case *rsa.PrivateKey:
|
||||||
|
priv = k
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("unknown private key type in PKCS#8 wrapping")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return priv, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// decrypt 使用RSA私钥解密数据
|
||||||
|
func Decrypt(privateKeyPEM string, encryptedDataBase64 string) ([]byte, error) {
|
||||||
|
var decryptedData []byte
|
||||||
|
// 将PEM编码的私钥转换为[]byte
|
||||||
|
pemData := []byte(privateKeyPEM)
|
||||||
|
// 解析PEM数据以获取私钥
|
||||||
|
privKey, err := parseRSAPrivateKeyFromPEM(pemData)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
keySize := privKey.PublicKey.Size()
|
||||||
|
label := []byte("") // OAEP标签,对于某些情况可能是非空的
|
||||||
|
hash := sha256.New()
|
||||||
|
// 将Base64编码的加密数据解码为字节切片
|
||||||
|
encryptedData, err := base64.StdEncoding.DecodeString(encryptedDataBase64)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
for len(encryptedData) > 0 {
|
||||||
|
block := encryptedData[:keySize]
|
||||||
|
// 这里假设使用的是OAEP填充和SHA-256哈希函数
|
||||||
|
decryptedBlock, err := rsa.DecryptOAEP(hash, rand.Reader, privKey, block, label)
|
||||||
|
if err != nil {
|
||||||
|
//// 如果失败,可以尝试使用PKCS#1 v1.5填充
|
||||||
|
decryptedBlock, err = rsa.DecryptPKCS1v15(rand.Reader, privKey, encryptedData)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
decryptedData = append(decryptedData, decryptedBlock...)
|
||||||
|
encryptedData = encryptedData[keySize:]
|
||||||
|
}
|
||||||
|
return decryptedData, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 生成密钥对
|
||||||
|
func GenerateKey() (string, string, error) {
|
||||||
|
// 生成私钥
|
||||||
|
privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
|
||||||
|
if err != nil {
|
||||||
|
return "", "", err
|
||||||
|
}
|
||||||
|
// 导出私钥PKCS#1格式
|
||||||
|
privKey := x509.MarshalPKCS1PrivateKey(privateKey)
|
||||||
|
// 将私钥转换为PEM编码
|
||||||
|
var privBlock = &pem.Block{
|
||||||
|
Type: "RSA PRIVATE KEY",
|
||||||
|
Bytes: privKey,
|
||||||
|
}
|
||||||
|
privPem := pem.EncodeToMemory(privBlock)
|
||||||
|
// 导出公钥
|
||||||
|
pubKey := &privateKey.PublicKey
|
||||||
|
derPkix, err := x509.MarshalPKIXPublicKey(pubKey)
|
||||||
|
if err != nil {
|
||||||
|
return "", "", err
|
||||||
|
}
|
||||||
|
// 将公钥转换为PEM编码
|
||||||
|
var pubBlock = &pem.Block{
|
||||||
|
Type: "PUBLIC KEY",
|
||||||
|
Bytes: derPkix,
|
||||||
|
}
|
||||||
|
pubPem := pem.EncodeToMemory(pubBlock)
|
||||||
|
pri := strings.Replace(string(privPem), "-----BEGIN RSA PRIVATE KEY-----\n", "", -1)
|
||||||
|
pri = strings.Replace(pri, "\n-----END RSA PRIVATE KEY-----\n", "", -1)
|
||||||
|
pri = strings.Replace(pri, "\n", "", -1)
|
||||||
|
pub := strings.Replace(string(pubPem), "-----BEGIN PUBLIC KEY-----\n", "", -1)
|
||||||
|
pub = strings.Replace(pub, "\n-----END PUBLIC KEY-----\n", "", -1)
|
||||||
|
pub = strings.Replace(pub, "\n", "", -1)
|
||||||
|
return pub, pri, nil
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,63 @@
|
||||||
|
package rsa
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/base64"
|
||||||
|
"fmt"
|
||||||
|
"physicalGoods/pkg/encrypt/encrypt_way/aes"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
PRI = "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCmjK6SPkrP03ssex5Dxn4KZeVm9rQoJMslru13GNgoGKJAq13xwUGl6lpGlhzM9IMzg0ldOO2a0SQPPjc0fLrDjIgHPfIT6H1hDGkZdPaF4D0OfiHGzlLwXq7FyyyGc2JXILsniMBXcCxNFhbW0jAaJyYrBJb3q84a5y5AtK3IC+eDV6Bj2J4HlQVGKgW9u2ic6Jxl23sacXY6iifi+KEuoXNCJrmYlgRWTaQovLmTCLkErkzxzG9DFRDWGoz25LthDPqcCSUmWEbJ+obwIGB4r2WCbFXvaeVBQORlyVRyNUvYMItjHBQIKinDWZ6y8KzA0YKOoxEfr0KfE8Uk4PQBAgMBAAECggEABTAX0PzelN4uyvTT0sMi/R0YRKPgepP40vtBsNvF10E7Lp4ClAupHpYFSrJq178xu1/2dVBXEGM9hw8GUQd7RCjuD3cFwcp/EKU+Zy6uQ38iZRTskEDa3bC+q3EXzuFXDxqOfIhai262UTlkATw0sjUwJRdkbMxoeWHkSNuH7UBVddxFL8Bq1DKaPzRCqQ8zlkMZHy8Xbf2b8rFoi1oPBoPjHyxCo33zcnSg5xntIoA5pkD6x4z5cAnU55aBoYUiRv7jmG+MVp1jpDvAmJLfUayVZNakgX1r74bMPsl9kpA7dVdgOlWrIkbJE1plVXXswBb8QKN0/Yx2vv/YASSi0QKBgQDaO9BkRjvht/lrsQEur1wXf5ITnsVWsqlUhYQKGHihzOj7e0rGO9QICM4eQZH9ssHfxEXhmEoFdkaqo3Fo47NI+yinpWm+KruwrRFkCGejlKZ4bhn9zWPb8L1qJbN4UD1c5jUNk1B0EEdnLRFg0/O7xm602bGilvY5x2nf0v95+QKBgQDDXyiiGNV7GO4h8OQYJwq0IqenPyIanRgYI3rw//tg147mhWcxT6ms6dMUh9nEXEFali2J1En+aVvx1Xn47CuGrRmZOLaGkw9ESFA/4ngYdea+xgttbKMXm0QwIwvATq2jxxrYEKmnr/+EUUWzIWioM1zQffAhVlkLFVnImquMSQKBgDe7qNfC/A4ELwWqubOTg0BZCxRJqvoePJJiWrs9Tql7rFB1Rz5jDx5SKVmew0r4OP0Nog8gFl9YumlfvlncNPBBfDt8SgoP3ckcGeHjJ5ymHPGKpMaliogj7ivKnw/t5g3wmMHzyksp0SJvZw3Ec22UGrfDFNOCHDXbUJWhzC75AoGBALbM6rAAnH65LNcFFeajYRh69HNAVyCfrFOpnvawDPzntAVs/MjeyNvJTH8BPXjE+UFREvrLbxBkdGsqWx3VnEQ+4pzCu8XfA4HYR33+4G/CoUwO8dJIu7DyzjJcGDqvYzjCqxNPQ+5qdqHPiW+56rq2lDlgHLaUnGwKZh+U2L5BAoGAeogtSQbnZaLQofSGcUOol2fyG24NU1JK3My+N1ypkSpD+y1KiFQeM5kg7UcJB6LBKZ/lRCKcjEMaxMyj57ETMkbRVKBLvsIL5QYolJLIHYqBul0AeFJ4K51SKK2Xk5rFvyuJKkJBux26WodtCXTnEzP1KRZGlSxJeN/V1yXjSEU="
|
||||||
|
|
||||||
|
PUB = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEApoyukj5Kz9N7LHseQ8Z+CmXlZva0KCTLJa7tdxjYKBiiQKtd8cFBpepaRpYczPSDM4NJXTjtmtEkDz43NHy6w4yIBz3yE+h9YQxpGXT2heA9Dn4hxs5S8F6uxcsshnNiVyC7J4jAV3AsTRYW1tIwGicmKwSW96vOGucuQLStyAvng1egY9ieB5UFRioFvbtonOicZdt7GnF2Ooon4vihLqFzQia5mJYEVk2kKLy5kwi5BK5M8cxvQxUQ1hqM9uS7YQz6nAklJlhGyfqG8CBgeK9lgmxV72nlQUDkZclUcjVL2DCLYxwUCCopw1mesvCswNGCjqMRH69CnxPFJOD0AQIDAQAB"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestRsaEncrypt(t *testing.T) {
|
||||||
|
fmt.Printf("%s\n", encrypt())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRsaDecrypt(t *testing.T) {
|
||||||
|
data := "pPnAPy7v2SY9Fu0LFcQH8UBA6VQ2FCfSg3nRZdMXS7mWjBwlacKHuFnh9UhobL7mxnmMyZPP100bpjCg2kvcfOpOp3ci85p+OYWINt4Fh3qgEOTG5FUyziaagGLm882t/I36KsDTVvbMZvC5sg4gZ9JQ5yAR+nuJfr0IxI0se/iD5luV1rms1kZHggd30iXdZtbkbX7xJ4xtnIiJmZU7kL+Xmvv1rDdPLxbol65QfnM1me1IHkXJapqSBnhEEmFQyBx31vp1ccNjkza8ZWbvTPCngc1k4kvlm6lKfwsG4hMuSdXUzveDm+Oo8StAKnyVoerJ202n7Vfx1XhehineQT0TPD7bO0HCEsDXXYEWwvcax8VdzYvHk7qSbH6e154qCr4LgDRSHKwAAExinTrzxx2rtSimieBLaEpDL2v5ch45HnhjRhWTRmM61W1g6sdHaVX1mQxaXvrT4v+h+f4TbIV4r4qeGJ6rXG+yKRoYseLzyGgystoOny9P0UH15W8rWPytV2eioWT7i3Cglg04BWP9mst67LQXeFH4CA6CkwVV2w9nCHrzxX2ouYSQELUEkTlIMry2AlkZubUnupGJLmLLUyZj7pM/6cLjyAgm02/gRc4wwf7JBBq/ipmKXpkhHXWLtQDWJEZTT+ug2v9EXy5dgPNPe8ZI0MILAeipjIc="
|
||||||
|
privateKeyPEM := `-----BEGIN RSA PRIVATE KEY-----
|
||||||
|
` + PRI + `
|
||||||
|
-----END RSA PRIVATE KEY-----`
|
||||||
|
res, err := Decrypt(privateKeyPEM, data)
|
||||||
|
fmt.Println(string(res), err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func encrypt() string {
|
||||||
|
data := "{\"pay_channel_id\":8935141660703064070,\"out_trade_no\":\"refundOutTreadNo001\",\"amount\":1,\"desc\":\"退款\",\"ext_json\":\"\",\"app_id\":5476377146882523138,\"timestamp\":53612533412643,\"refund_out_trade_no\":\"asdadasdas\"}"
|
||||||
|
pub := `-----BEGIN PUBLIC KEY-----
|
||||||
|
` + PUB + `
|
||||||
|
-----END PUBLIC KEY-----`
|
||||||
|
en, err := Encrypt(pub, data)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return base64.StdEncoding.EncodeToString(en)
|
||||||
|
}
|
||||||
|
|
||||||
|
func encryptWithAes() string {
|
||||||
|
data := "{\"pay_channel_id\":8935141660703064070,\"out_trade_no\":\"refundOutTreadNo001\",\"amount\":1,\"desc\":\"退款\",\"ext_json\":\"\",\"app_id\":5476377146882523138,\"timestamp\":53612533412643,\"refund_out_trade_no\":\"asdadasdas\"}"
|
||||||
|
aes.Encrypt([]byte(data), []byte(aes.TestKey))
|
||||||
|
|
||||||
|
dataJson := base64.StdEncoding.EncodeToString([]byte(data))
|
||||||
|
pub := `-----BEGIN PUBLIC KEY-----
|
||||||
|
` + PUB + `
|
||||||
|
-----END PUBLIC KEY-----`
|
||||||
|
en, err := Encrypt(pub, dataJson)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return base64.StdEncoding.EncodeToString(en)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 测试生成密钥对
|
||||||
|
func TestGenerateRSAKey(t *testing.T) {
|
||||||
|
pub, pri, _ := GenerateKey()
|
||||||
|
|
||||||
|
fmt.Println("pub:", pub, "\n", "pri:", pri)
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,167 @@
|
||||||
|
package sm2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/rand"
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/hex"
|
||||||
|
"fmt"
|
||||||
|
"github.com/tjfoc/gmsm/sm2"
|
||||||
|
"math/big"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 生成公钥、私钥
|
||||||
|
func GenerateSM2Key() (PublicKey string, PrivateKey string, err error) {
|
||||||
|
// 生成私钥、公钥
|
||||||
|
privKey, err := sm2.GenerateKey(rand.Reader)
|
||||||
|
if err != nil {
|
||||||
|
|
||||||
|
return "", "", err
|
||||||
|
}
|
||||||
|
return PublicKeyToString(&privKey.PublicKey), PrivateKeyToString(privKey), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GenerateKey 生成密钥对
|
||||||
|
func GenerateKey() (string, string) {
|
||||||
|
pri, _ := sm2.GenerateKey(rand.Reader)
|
||||||
|
hexPri := pri.D.Text(16)
|
||||||
|
// 获取公钥
|
||||||
|
publicKeyHex := PublicKeyToString(&pri.PublicKey)
|
||||||
|
return strings.ToUpper(hexPri), publicKeyHex
|
||||||
|
}
|
||||||
|
|
||||||
|
// PublicKeyToString 公钥sm2.PublicKey转字符串(与java中org.bouncycastle.crypto生成的公私钥完全互通使用)
|
||||||
|
func PublicKeyToString(publicKey *sm2.PublicKey) string {
|
||||||
|
xBytes := publicKey.X.Bytes()
|
||||||
|
yBytes := publicKey.Y.Bytes()
|
||||||
|
|
||||||
|
// 确保坐标字节切片长度相同
|
||||||
|
byteLen := len(xBytes)
|
||||||
|
if len(yBytes) > byteLen {
|
||||||
|
byteLen = len(yBytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 为坐标补齐前导零
|
||||||
|
xBytes = append(make([]byte, byteLen-len(xBytes)), xBytes...)
|
||||||
|
yBytes = append(make([]byte, byteLen-len(yBytes)), yBytes...)
|
||||||
|
|
||||||
|
// 添加 "04" 前缀
|
||||||
|
publicKeyBytes := append([]byte{0x04}, append(xBytes, yBytes...)...)
|
||||||
|
|
||||||
|
return strings.ToUpper(hex.EncodeToString(publicKeyBytes))
|
||||||
|
}
|
||||||
|
|
||||||
|
// PrivateKeyToString 私钥sm2.PrivateKey 转字符串(与java中org.bouncycastle.crypto生成的公私钥完全互通使用)
|
||||||
|
func PrivateKeyToString(privateKey *sm2.PrivateKey) string {
|
||||||
|
return strings.ToUpper(hex.EncodeToString(privateKey.D.Bytes()))
|
||||||
|
}
|
||||||
|
|
||||||
|
func SM2Decrypt(cipherText, publicKey string, privateKey string) (string, error) {
|
||||||
|
if cipherText == "" {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
decodedBytes, err := base64.StdEncoding.DecodeString(cipherText)
|
||||||
|
if err != nil {
|
||||||
|
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
decrypt, err := decryptLoc(publicKey, privateKey, string(decodedBytes))
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return decrypt, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func SM2Encrypt(cipherText string, privateKey string) (string, error) {
|
||||||
|
if cipherText == "" {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
decrypt, err := encryptLoc(privateKey, cipherText)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return decrypt, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func encryptLoc(publicKeyStr, data string) (string, error) {
|
||||||
|
publicKeyObj, err := StringToPublicKey(publicKeyStr)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
decrypt, err := sm2.Encrypt(publicKeyObj, []byte(data), rand.Reader, sm2.C1C2C3)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
resultStr := hex.EncodeToString(decrypt)
|
||||||
|
return base64.StdEncoding.EncodeToString([]byte(resultStr)), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func decryptLoc(publicKeyStr, privateKeyStr, cipherText string) (string, error) {
|
||||||
|
publicKeyObj, err := StringToPublicKey(publicKeyStr)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
}
|
||||||
|
privateKeyObj, err := StringToPrivateKey(privateKeyStr, publicKeyObj)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
}
|
||||||
|
decodeString, err := hex.DecodeString(cipherText)
|
||||||
|
decrypt, err := sm2.Decrypt(privateKeyObj, decodeString, sm2.C1C2C3)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
}
|
||||||
|
resultStr := string(decrypt)
|
||||||
|
return resultStr, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// StringToPrivateKey 私钥还原为 sm2.PrivateKey对象(与java中org.bouncycastle.crypto生成的公私钥完全互通使用)
|
||||||
|
func StringToPrivateKey(privateKeyStr string, publicKey *sm2.PublicKey) (*sm2.PrivateKey, error) {
|
||||||
|
privateKeyBytes, err := hex.DecodeString(privateKeyStr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 将字节切片转换为大整数
|
||||||
|
d := new(big.Int).SetBytes(privateKeyBytes)
|
||||||
|
|
||||||
|
// 创建 sm2.PrivateKey 对象
|
||||||
|
privateKey := &sm2.PrivateKey{
|
||||||
|
PublicKey: *publicKey,
|
||||||
|
D: d,
|
||||||
|
}
|
||||||
|
|
||||||
|
return privateKey, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// StringToPublicKey 公钥字符串还原为 sm2.PublicKey 对象(与java中org.bouncycastle.crypto生成的公私钥完全互通使用)
|
||||||
|
func StringToPublicKey(publicKeyStr string) (*sm2.PublicKey, error) {
|
||||||
|
publicKeyBytes, err := hex.DecodeString(publicKeyStr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 提取 x 和 y 坐标字节切片
|
||||||
|
curve := sm2.P256Sm2().Params()
|
||||||
|
byteLen := (curve.BitSize + 7) / 8
|
||||||
|
xBytes := publicKeyBytes[1 : byteLen+1]
|
||||||
|
yBytes := publicKeyBytes[byteLen+1 : 2*byteLen+1]
|
||||||
|
|
||||||
|
// 将字节切片转换为大整数
|
||||||
|
x := new(big.Int).SetBytes(xBytes)
|
||||||
|
y := new(big.Int).SetBytes(yBytes)
|
||||||
|
|
||||||
|
// 创建 sm2.PublicKey 对象
|
||||||
|
publicKey := &sm2.PublicKey{
|
||||||
|
Curve: curve,
|
||||||
|
X: x,
|
||||||
|
Y: y,
|
||||||
|
}
|
||||||
|
|
||||||
|
return publicKey, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证签名
|
||||||
|
func VerSm2Sig(pub *sm2.PublicKey, msg []byte, sign []byte) bool {
|
||||||
|
isok := pub.Verify(msg, sign)
|
||||||
|
return isok
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,68 @@
|
||||||
|
package sm2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
SELF_PRI = "cde43bc462e2ffad2d6fda08e975bee59d673c33591fcbf1aa181d4cb9d89314"
|
||||||
|
|
||||||
|
SELF_PUB = "04B5A21130C6BCE5B161C33B182D4E9E84F366C28660F515AC1DB3278E2B55C5CB62765A56144049997D6AE5F75D70750EA1C621D3D9599558E8847B55A90856B2"
|
||||||
|
|
||||||
|
SOA_PUB = "0416445bc16cbf42e47002ad9fe7c7af67d902b48be1eb69b98f6a006b0918630e1127f5f2fff83b2ecb30fc7fd72c34c33f37c7c355dffde3589f66800f0036ca"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestGenerateSM2KeyPair(t *testing.T) {
|
||||||
|
// Generate a new SM2 key pair
|
||||||
|
privateKey, publicKey := GenerateKey()
|
||||||
|
|
||||||
|
// Print the private and public keys
|
||||||
|
fmt.Printf("Private Key: %s\n", privateKey)
|
||||||
|
fmt.Printf("Public Key: %s\n", publicKey)
|
||||||
|
|
||||||
|
data := "{\"name\":\"张三\",\"sex\":1,\"is_human\":true}"
|
||||||
|
en, err := SM2Encrypt(data, publicKey)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
decrypt, err := SM2Decrypt(en, publicKey, privateKey)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
t.Log(decrypt)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSM2Encrypt(t *testing.T) {
|
||||||
|
t.Log(encrypt())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSM2Decrypt(t *testing.T) {
|
||||||
|
en := encrypt()
|
||||||
|
decrypt, err := SM2Decrypt(en, SELF_PUB, SELF_PRI)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
t.Log(decrypt)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSM2Decrypt2(t *testing.T) {
|
||||||
|
en := "BOZcpvnFxHrqyzqpq6w3aiTv0KZ5VvLMQnT9sZDxBpMY2hjOrDmwiRau+5QB6iV8ghNZZQKivzUU3I6sfIBSqvRtmtK8nGepQsqi+OCq3uNyMnzWOXkW2Ft9kbt4n4AusCeLLJJGTwS0cmoURiWpdrpr+sNk5wW3+crw9Yo4Xv5UIhyFYtYS8vyEb57Z|YNtjSKBan8/cAxfTgirq7EqLxSdFkEpht/BDp2wW56BCg+4eTmUZo7/YDtUe2MIoAJpSRoI8RwqzrZ3B1V5rOxGopycEwZ/nrrcUkGN6pNE="
|
||||||
|
decrypt, err := SM2Decrypt(en, SELF_PUB, SELF_PRI)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
t.Log(decrypt)
|
||||||
|
}
|
||||||
|
|
||||||
|
func encrypt() string {
|
||||||
|
data := "funmallGetOrderList.json?aid=AppFunmallTestSM&cmbKeyAlias=CO_PUB_KEY_SM2&date=20241106105348&encryptBody=TURRNU5HWXpPV015WXpNMU1HRTVNMlV4TkRnNE1XTTNPVGhsWlRjMk1qUXhNamhoWkRVeU5UQmtPVFEyWVRjMk56VXpNemt3TkdVeE5XVTVObUUxTmpFNU1XWXdPVFJpT1RoaFlURmhNemMzTWpNMVlqTTVaRGd3WmpZNFl6ZzVZVE15TUdKaFpESXdaVEJsWlRnNE1qTTBNelZrWlRBNE5tVTVNR0poTWpZeE5qUmhOakprWm1ZeFpHRTBaV0UyTmpJNU1ESmlObUUwTWpGa056UTRZVGhtTkRkbE4yUTJNek15TVRVellUSXlOREV5WWpKaE9HRTBNVEUwWXpoaFltVTROalpoTVdZM00yRmlNall4WW1abVptRXpNakExTmpVMU0yWmpZamN4WVRBellqVXdZakl3TXpabU4ySTVNR1V3WlRCbFl6VmpNVE0wTURFelkyTXpaRGd4TVdZeU9HTXhOemszTVRrM1pqVmpZell6TjJVeFltUTFOakEzTnpsbE5tVTROemxoWlRsaE0yUTRNREUxWVdGbU5qWTJORFJrT0ROalptVTJaamsy|lGeghQQEGiVwd0oINhKJDvEIhI6Qbvh41G5x9CseXApghhHGJ98TOd2B8UUCo6uJ4bvmDhl6lMhq1Uy2FvZfkF1WQ8cG2H2EwDLUtsF7CEY=&keyAlias=SM2_CMBLIFE&mid=MerchFunmallTestSM&random=ziy1gctmame68wz4hdr3xc0vmvmhnf35"
|
||||||
|
|
||||||
|
en, err := SM2Encrypt(data, SELF_PUB)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return en
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,219 @@
|
||||||
|
package sm2
|
||||||
|
|
||||||
|
// reference to ecdsa
|
||||||
|
import (
|
||||||
|
"crypto"
|
||||||
|
"crypto/elliptic"
|
||||||
|
"crypto/rand"
|
||||||
|
"encoding/asn1"
|
||||||
|
"errors"
|
||||||
|
"github.com/tjfoc/gmsm/sm3"
|
||||||
|
"io"
|
||||||
|
"math/big"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
default_uid = []byte{0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38}
|
||||||
|
)
|
||||||
|
|
||||||
|
type PublicKey struct {
|
||||||
|
elliptic.Curve
|
||||||
|
X, Y *big.Int
|
||||||
|
}
|
||||||
|
|
||||||
|
type PrivateKey struct {
|
||||||
|
PublicKey
|
||||||
|
D *big.Int
|
||||||
|
}
|
||||||
|
|
||||||
|
type sm2Signature struct {
|
||||||
|
R, S *big.Int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (priv *PrivateKey) Public() crypto.PublicKey {
|
||||||
|
return &priv.PublicKey
|
||||||
|
}
|
||||||
|
|
||||||
|
var errZeroParam = errors.New("zero parameter")
|
||||||
|
var one = new(big.Int).SetInt64(1)
|
||||||
|
var two = new(big.Int).SetInt64(2)
|
||||||
|
|
||||||
|
func (priv *PrivateKey) Sign(random io.Reader, msg []byte, signer crypto.SignerOpts) ([]byte, error) {
|
||||||
|
r, s, err := Sm2Sign(priv, msg, nil, random)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return asn1.Marshal(sm2Signature{r, s})
|
||||||
|
}
|
||||||
|
|
||||||
|
func Sm2Sign(priv *PrivateKey, msg, uid []byte, random io.Reader) (r, s *big.Int, err error) {
|
||||||
|
digest, err := priv.PublicKey.Sm3Digest(msg, uid)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
e := new(big.Int).SetBytes(digest)
|
||||||
|
c := priv.PublicKey.Curve
|
||||||
|
N := c.Params().N
|
||||||
|
if N.Sign() == 0 {
|
||||||
|
return nil, nil, errZeroParam
|
||||||
|
}
|
||||||
|
var k *big.Int
|
||||||
|
for { // 调整算法细节以实现SM2
|
||||||
|
for {
|
||||||
|
k, err = randFieldElement(c, random)
|
||||||
|
if err != nil {
|
||||||
|
r = nil
|
||||||
|
return
|
||||||
|
}
|
||||||
|
r, _ = priv.Curve.ScalarBaseMult(k.Bytes())
|
||||||
|
r.Add(r, e)
|
||||||
|
r.Mod(r, N)
|
||||||
|
if r.Sign() != 0 {
|
||||||
|
if t := new(big.Int).Add(r, k); t.Cmp(N) != 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
rD := new(big.Int).Mul(priv.D, r)
|
||||||
|
s = new(big.Int).Sub(k, rD)
|
||||||
|
d1 := new(big.Int).Add(priv.D, one)
|
||||||
|
d1Inv := new(big.Int).ModInverse(d1, N)
|
||||||
|
s.Mul(s, d1Inv)
|
||||||
|
s.Mod(s, N)
|
||||||
|
if s.Sign() != 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pub *PublicKey) Sm3Digest(msg, uid []byte) ([]byte, error) {
|
||||||
|
if len(uid) == 0 {
|
||||||
|
uid = default_uid
|
||||||
|
}
|
||||||
|
|
||||||
|
za, err := getZ(pub, uid)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
e, err := msgHash(za, msg)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return e.Bytes(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func Sm2Verify(pub *PublicKey, msg, uid []byte, r, s *big.Int) bool {
|
||||||
|
c := pub.Curve
|
||||||
|
N := c.Params().N
|
||||||
|
one := new(big.Int).SetInt64(1)
|
||||||
|
if r.Cmp(one) < 0 || s.Cmp(one) < 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if r.Cmp(N) >= 0 || s.Cmp(N) >= 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if len(uid) == 0 {
|
||||||
|
uid = default_uid
|
||||||
|
}
|
||||||
|
za, err := getZ(pub, uid)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
e, err := msgHash(za, msg)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
t := new(big.Int).Add(r, s)
|
||||||
|
t.Mod(t, N)
|
||||||
|
if t.Sign() == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
var x *big.Int
|
||||||
|
x1, y1 := c.ScalarBaseMult(s.Bytes())
|
||||||
|
x2, y2 := c.ScalarMult(pub.X, pub.Y, t.Bytes())
|
||||||
|
x, _ = c.Add(x1, y1, x2, y2)
|
||||||
|
|
||||||
|
x.Add(x, e)
|
||||||
|
x.Mod(x, N)
|
||||||
|
return x.Cmp(r) == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func msgHash(za, msg []byte) (*big.Int, error) {
|
||||||
|
e := sm3.New()
|
||||||
|
e.Write(za)
|
||||||
|
e.Write(msg)
|
||||||
|
return new(big.Int).SetBytes(e.Sum(nil)[:32]), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func bigIntToByte(n *big.Int) []byte {
|
||||||
|
byteArray := n.Bytes()
|
||||||
|
// If the most significant byte's most significant bit is set,
|
||||||
|
// prepend a 0 byte to the slice to avoid being interpreted as a negative number.
|
||||||
|
if (byteArray[0] & 0x80) != 0 {
|
||||||
|
byteArray = append([]byte{0}, byteArray...)
|
||||||
|
}
|
||||||
|
return byteArray
|
||||||
|
}
|
||||||
|
|
||||||
|
func getZ(pub *PublicKey, uid []byte) ([]byte, error) {
|
||||||
|
z := sm3.New()
|
||||||
|
uidLen := len(uid) * 8
|
||||||
|
entla := []byte{byte(uidLen >> 8), byte(uidLen & 255)}
|
||||||
|
z.Write(entla)
|
||||||
|
z.Write(uid)
|
||||||
|
|
||||||
|
// a 先写死,原来的没有暴露
|
||||||
|
z.Write(bigIntToByte(sm2P256ToBig(&sm2P256.a)))
|
||||||
|
z.Write(bigIntToByte(sm2P256.B))
|
||||||
|
z.Write(bigIntToByte(sm2P256.Gx))
|
||||||
|
z.Write(bigIntToByte(sm2P256.Gy))
|
||||||
|
|
||||||
|
z.Write(bigIntToByte(pub.X))
|
||||||
|
z.Write(bigIntToByte(pub.Y))
|
||||||
|
return z.Sum(nil), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func randFieldElement(c elliptic.Curve, random io.Reader) (k *big.Int, err error) {
|
||||||
|
if random == nil {
|
||||||
|
random = rand.Reader //If there is no external trusted random source,please use rand.Reader to instead of it.
|
||||||
|
}
|
||||||
|
params := c.Params()
|
||||||
|
b := make([]byte, params.BitSize/8+8)
|
||||||
|
_, err = io.ReadFull(random, b)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
k = new(big.Int).SetBytes(b)
|
||||||
|
n := new(big.Int).Sub(params.N, one)
|
||||||
|
k.Mod(k, n)
|
||||||
|
k.Add(k, one)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func GenerateKey(random io.Reader) (*PrivateKey, error) {
|
||||||
|
c := P256Sm2()
|
||||||
|
if random == nil {
|
||||||
|
random = rand.Reader //If there is no external trusted random source,please use rand.Reader to instead of it.
|
||||||
|
}
|
||||||
|
params := c.Params()
|
||||||
|
b := make([]byte, params.BitSize/8+8)
|
||||||
|
_, err := io.ReadFull(random, b)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
k := new(big.Int).SetBytes(b)
|
||||||
|
n := new(big.Int).Sub(params.N, two)
|
||||||
|
k.Mod(k, n)
|
||||||
|
k.Add(k, one)
|
||||||
|
priv := new(PrivateKey)
|
||||||
|
priv.PublicKey.Curve = c
|
||||||
|
priv.D = k
|
||||||
|
priv.PublicKey.X, priv.PublicKey.Y = c.ScalarBaseMult(k.Bytes())
|
||||||
|
|
||||||
|
return priv, nil
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,45 @@
|
||||||
|
package sm2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/hex"
|
||||||
|
"errors"
|
||||||
|
"math/big"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ReadPrivateKeyFromHex(Dhex string) (*PrivateKey, error) {
|
||||||
|
c := P256Sm2()
|
||||||
|
d, err := hex.DecodeString(Dhex)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
k := new(big.Int).SetBytes(d)
|
||||||
|
params := c.Params()
|
||||||
|
one := new(big.Int).SetInt64(1)
|
||||||
|
n := new(big.Int).Sub(params.N, one)
|
||||||
|
if k.Cmp(n) >= 0 {
|
||||||
|
return nil, errors.New("privateKey's D is overflow.")
|
||||||
|
}
|
||||||
|
priv := new(PrivateKey)
|
||||||
|
priv.PublicKey.Curve = c
|
||||||
|
priv.D = k
|
||||||
|
priv.PublicKey.X, priv.PublicKey.Y = c.ScalarBaseMult(k.Bytes())
|
||||||
|
return priv, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ReadPublicKeyFromHex(Qhex string) (*PublicKey, error) {
|
||||||
|
q, err := hex.DecodeString(Qhex)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if len(q) == 65 && q[0] == byte(0x04) {
|
||||||
|
q = q[1:]
|
||||||
|
}
|
||||||
|
if len(q) != 64 {
|
||||||
|
return nil, errors.New("publicKey is not uncompressed.")
|
||||||
|
}
|
||||||
|
pub := new(PublicKey)
|
||||||
|
pub.Curve = P256Sm2()
|
||||||
|
pub.X = new(big.Int).SetBytes(q[:32])
|
||||||
|
pub.Y = new(big.Int).SetBytes(q[32:])
|
||||||
|
return pub, nil
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,163 @@
|
||||||
|
package util
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto/elliptic"
|
||||||
|
"crypto/rand"
|
||||||
|
"errors"
|
||||||
|
zzsm2 "github.com/ZZMarquis/gm/sm2"
|
||||||
|
"github.com/tjfoc/gmsm/sm3"
|
||||||
|
"github.com/tjfoc/gmsm/x509"
|
||||||
|
"math/big"
|
||||||
|
"voucher/internal/pkg/cmb/encrypt/encrypt_way/sm4/internal/sm2"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Sm2Decrypt(privateKey *sm2.PrivateKey, encryptData []byte) ([]byte, error) {
|
||||||
|
C1Byte := make([]byte, 65)
|
||||||
|
copy(C1Byte, encryptData[:65])
|
||||||
|
x, y := elliptic.Unmarshal(privateKey.Curve, C1Byte)
|
||||||
|
dBC1X, dBC1Y := privateKey.Curve.ScalarMult(x, y, bigIntToByte(privateKey.D))
|
||||||
|
dBC1Bytes := elliptic.Marshal(privateKey.Curve, dBC1X, dBC1Y)
|
||||||
|
|
||||||
|
kLen := len(encryptData) - 65 - 32
|
||||||
|
t, err := kdf(dBC1Bytes, kLen)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
M := make([]byte, kLen)
|
||||||
|
for i := 0; i < kLen; i++ {
|
||||||
|
M[i] = encryptData[65+i] ^ t[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
C3 := make([]byte, 32)
|
||||||
|
copy(C3, encryptData[len(encryptData)-32:])
|
||||||
|
u := calculateHash(dBC1X, M, dBC1Y)
|
||||||
|
|
||||||
|
if bytes.Compare(u, C3) == 0 {
|
||||||
|
return M, nil
|
||||||
|
} else {
|
||||||
|
return nil, errors.New("解密失败")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Sm2Encrypt(publicKey *sm2.PublicKey, m []byte) ([]byte, error) {
|
||||||
|
kLen := len(m)
|
||||||
|
var C1, t []byte
|
||||||
|
var err error
|
||||||
|
var kx, ky *big.Int
|
||||||
|
for {
|
||||||
|
k, _ := rand.Int(rand.Reader, publicKey.Params().N)
|
||||||
|
C1x, C1y := zzsm2.GetSm2P256V1().ScalarBaseMult(bigIntToByte(k))
|
||||||
|
// C1x, C1y := sm2.P256Sm2().ScalarBaseMult(bigIntToByte(k))
|
||||||
|
C1 = elliptic.Marshal(publicKey.Curve, C1x, C1y)
|
||||||
|
|
||||||
|
kx, ky = publicKey.ScalarMult(publicKey.X, publicKey.Y, bigIntToByte(k))
|
||||||
|
kpbBytes := elliptic.Marshal(publicKey, kx, ky)
|
||||||
|
t, err = kdf(kpbBytes, kLen)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if !isAllZero(t) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
C2 := make([]byte, kLen)
|
||||||
|
for i := 0; i < kLen; i++ {
|
||||||
|
C2[i] = m[i] ^ t[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
C3 := calculateHash(kx, m, ky)
|
||||||
|
|
||||||
|
r := make([]byte, 0, len(C1)+len(C2)+len(C3))
|
||||||
|
r = append(r, C1...)
|
||||||
|
r = append(r, C2...)
|
||||||
|
r = append(r, C3...)
|
||||||
|
return r, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func isAllZero(m []byte) bool {
|
||||||
|
for i := 0; i < len(m); i++ {
|
||||||
|
if m[i] != 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func calculateHash(x *big.Int, M []byte, y *big.Int) []byte {
|
||||||
|
digest := sm3.New()
|
||||||
|
digest.Write(bigIntToByte(x))
|
||||||
|
digest.Write(M)
|
||||||
|
digest.Write(bigIntToByte(y))
|
||||||
|
result := digest.Sum(nil)[:32]
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func bigIntToByte(n *big.Int) []byte {
|
||||||
|
byteArray := n.Bytes()
|
||||||
|
// If the most significant byte's most significant bit is set,
|
||||||
|
// prepend a 0 byte to the slice to avoid being interpreted as a negative number.
|
||||||
|
if (byteArray[0] & 0x80) != 0 {
|
||||||
|
byteArray = append([]byte{0}, byteArray...)
|
||||||
|
}
|
||||||
|
return byteArray
|
||||||
|
}
|
||||||
|
|
||||||
|
func kdf(Z []byte, klen int) ([]byte, error) {
|
||||||
|
ct := 1
|
||||||
|
end := (klen + 31) / 32
|
||||||
|
result := make([]byte, 0)
|
||||||
|
for i := 1; i <= end; i++ {
|
||||||
|
b, err := sm3hash(Z, toByteArray(ct))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
result = append(result, b...)
|
||||||
|
ct++
|
||||||
|
}
|
||||||
|
last, err := sm3hash(Z, toByteArray(ct))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if klen%32 == 0 {
|
||||||
|
result = append(result, last...)
|
||||||
|
} else {
|
||||||
|
result = append(result, last[:klen%32]...)
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func sm3hash(sources ...[]byte) ([]byte, error) {
|
||||||
|
b, err := joinBytes(sources...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
md := make([]byte, 32)
|
||||||
|
h := x509.SM3.New()
|
||||||
|
h.Write(b)
|
||||||
|
h.Sum(md[:0])
|
||||||
|
return md, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func joinBytes(params ...[]byte) ([]byte, error) {
|
||||||
|
var buffer bytes.Buffer
|
||||||
|
for i := 0; i < len(params); i++ {
|
||||||
|
_, err := buffer.Write(params[i])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return buffer.Bytes(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func toByteArray(i int) []byte {
|
||||||
|
byteArray := []byte{
|
||||||
|
byte(i >> 24),
|
||||||
|
byte((i & 16777215) >> 16),
|
||||||
|
byte((i & 65535) >> 8),
|
||||||
|
byte(i & 255),
|
||||||
|
}
|
||||||
|
return byteArray
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,62 @@
|
||||||
|
package util
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/md5"
|
||||||
|
"crypto/rand"
|
||||||
|
"encoding/hex"
|
||||||
|
"math/big"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GenerateSM4Key() []byte {
|
||||||
|
str := "qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM1234567890"
|
||||||
|
buffer := make([]byte, 16)
|
||||||
|
for i := 0; i < 16; i++ {
|
||||||
|
nextInt, _ := rand.Int(rand.Reader, big.NewInt(int64(len(str))))
|
||||||
|
buffer[i] = str[nextInt.Int64()]
|
||||||
|
}
|
||||||
|
return buffer
|
||||||
|
}
|
||||||
|
|
||||||
|
func GenAccessToken(token string) string {
|
||||||
|
if token != "" {
|
||||||
|
return token
|
||||||
|
}
|
||||||
|
now := time.Now()
|
||||||
|
return strings.ToUpper(Md5Hash(now.Format("2006A01B02CD15E04F05"), ""))
|
||||||
|
}
|
||||||
|
|
||||||
|
func Md5Hash(password, salt string) string {
|
||||||
|
m := md5.New()
|
||||||
|
m.Write([]byte(salt + password))
|
||||||
|
return hex.EncodeToString(m.Sum(nil))
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSM4IV 获取SM4的IV
|
||||||
|
func GetSM4IV() []byte {
|
||||||
|
return []byte("UISwD9fW6cFh9SNS")
|
||||||
|
}
|
||||||
|
|
||||||
|
func Padding(input []byte, mode int) []byte {
|
||||||
|
if input == nil {
|
||||||
|
return nil
|
||||||
|
} else {
|
||||||
|
var ret []byte
|
||||||
|
if mode == 1 {
|
||||||
|
p := 16 - len(input)%16
|
||||||
|
ret = make([]byte, len(input)+p)
|
||||||
|
copy(ret, input)
|
||||||
|
|
||||||
|
for i := 0; i < p; i++ {
|
||||||
|
ret[len(input)+i] = byte(p)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
p := input[len(input)-1]
|
||||||
|
ret = make([]byte, len(input)-int(p))
|
||||||
|
copy(ret, input[:len(input)-int(p)])
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,203 @@
|
||||||
|
package sm4
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/rand"
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/hex"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"github.com/ZZMarquis/gm/sm4"
|
||||||
|
"math/big"
|
||||||
|
"strings"
|
||||||
|
"voucher/internal/pkg/cmb/encrypt/encrypt_way/sm4/internal/sm2"
|
||||||
|
"voucher/internal/pkg/cmb/encrypt/encrypt_way/sm4/internal/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
func checkInData(reqData map[string]string, key string) (string, error) {
|
||||||
|
data, ok := reqData[key]
|
||||||
|
if !ok {
|
||||||
|
return "", errors.New("请求数据中不存在" + key)
|
||||||
|
}
|
||||||
|
return data, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func Sm4Decrypt(merchantId, privateKey, sopPublicKey, respJson string, isRequest bool) (string, error) {
|
||||||
|
var reqData map[string]string
|
||||||
|
err := json.Unmarshal([]byte(respJson), &reqData)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
keys := [4]string{}
|
||||||
|
if isRequest {
|
||||||
|
keys = [4]string{"request", "signature", "encryptKey", "accessToken"}
|
||||||
|
} else {
|
||||||
|
keys = [4]string{"response", "signature", "encryptKey", "accessToken"}
|
||||||
|
}
|
||||||
|
var inEncryptKey, inAccessToken, inData, inSignature string
|
||||||
|
|
||||||
|
for i := 0; i < 4; i++ {
|
||||||
|
data, err := checkInData(reqData, keys[i])
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
switch keys[i] {
|
||||||
|
case "request", "response":
|
||||||
|
inData = data
|
||||||
|
case "signature":
|
||||||
|
inSignature = data
|
||||||
|
case "encryptKey":
|
||||||
|
inEncryptKey = data
|
||||||
|
case "accessToken":
|
||||||
|
inAccessToken = data
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
checked := verify(fmt.Sprintf("%s%s%s", inData, inEncryptKey, inAccessToken), inSignature, sopPublicKey, merchantId)
|
||||||
|
if !checked {
|
||||||
|
return "", errors.New("签名验证失败")
|
||||||
|
}
|
||||||
|
|
||||||
|
priKey, err := sm2.ReadPrivateKeyFromHex(privateKey)
|
||||||
|
if err != nil {
|
||||||
|
return "", errors.New("读取私钥失败")
|
||||||
|
}
|
||||||
|
hexEncryptKey, err := hex.DecodeString(inEncryptKey)
|
||||||
|
if err != nil {
|
||||||
|
return "", errors.New("解密sm4key失败")
|
||||||
|
}
|
||||||
|
sm4Key, err := util.Sm2Decrypt(priKey, hexEncryptKey)
|
||||||
|
|
||||||
|
request, _ := base64.StdEncoding.DecodeString(inData)
|
||||||
|
|
||||||
|
encryptedSm4Key, err := sm4.CBCDecrypt(sm4Key, util.GetSM4IV(), request)
|
||||||
|
|
||||||
|
return string(util.Padding(encryptedSm4Key, 0)), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func Sm4Encrypt(merchantId, privateKey, sopPublicKey, inputJson, token string, isRequest bool) (string, error) {
|
||||||
|
sm4Key := util.GenerateSM4Key()
|
||||||
|
iv := util.GetSM4IV()
|
||||||
|
tmp, err := sm4.CBCEncrypt(sm4Key, iv, util.Padding([]byte(inputJson), 1))
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
responseMsg := base64.StdEncoding.EncodeToString(tmp)
|
||||||
|
responseMsg = addNewline(responseMsg)
|
||||||
|
|
||||||
|
pubKey, err := sm2.ReadPublicKeyFromHex(sopPublicKey)
|
||||||
|
if err != nil {
|
||||||
|
return "", errors.New("读取私钥失败")
|
||||||
|
}
|
||||||
|
encryptKeyBytes, err := util.Sm2Encrypt(pubKey, sm4Key)
|
||||||
|
encryptKey := strings.ToUpper(hex.EncodeToString(encryptKeyBytes))
|
||||||
|
|
||||||
|
accessToken := util.GenAccessToken(token)
|
||||||
|
signContent := fmt.Sprintf("%s%s%s", responseMsg, encryptKey, accessToken)
|
||||||
|
signature, err := sign(merchantId, privateKey, signContent)
|
||||||
|
|
||||||
|
var reqData map[string]string
|
||||||
|
|
||||||
|
if isRequest {
|
||||||
|
reqData = map[string]string{
|
||||||
|
"request": responseMsg,
|
||||||
|
"signature": signature,
|
||||||
|
"encryptKey": encryptKey,
|
||||||
|
"accessToken": accessToken,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
reqData = map[string]string{
|
||||||
|
"response": responseMsg,
|
||||||
|
"signature": signature,
|
||||||
|
"encryptKey": encryptKey,
|
||||||
|
"accessToken": accessToken,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
jsonStr, err := json.Marshal(reqData)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return string(jsonStr), err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GenerateKey 生成密钥对
|
||||||
|
func GenerateKey() (string, string) {
|
||||||
|
pri, _ := sm2.GenerateKey(rand.Reader)
|
||||||
|
hexPri := pri.D.Text(16)
|
||||||
|
// 获取公钥
|
||||||
|
publicKeyHex := publicKeyToString(&pri.PublicKey)
|
||||||
|
return strings.ToUpper(hexPri), publicKeyHex
|
||||||
|
}
|
||||||
|
|
||||||
|
// publicKeyToString 公钥sm2.PublicKey转字符串(与java中org.bouncycastle.crypto生成的公私钥完全互通使用)
|
||||||
|
func publicKeyToString(publicKey *sm2.PublicKey) string {
|
||||||
|
xBytes := publicKey.X.Bytes()
|
||||||
|
yBytes := publicKey.Y.Bytes()
|
||||||
|
|
||||||
|
// 确保坐标字节切片长度相同
|
||||||
|
byteLen := len(xBytes)
|
||||||
|
if len(yBytes) > byteLen {
|
||||||
|
byteLen = len(yBytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 为坐标补齐前导零
|
||||||
|
xBytes = append(make([]byte, byteLen-len(xBytes)), xBytes...)
|
||||||
|
yBytes = append(make([]byte, byteLen-len(yBytes)), yBytes...)
|
||||||
|
|
||||||
|
// 添加 "04" 前缀
|
||||||
|
publicKeyBytes := append([]byte{0x04}, append(xBytes, yBytes...)...)
|
||||||
|
|
||||||
|
return strings.ToUpper(hex.EncodeToString(publicKeyBytes))
|
||||||
|
}
|
||||||
|
|
||||||
|
func addNewline(str string) string {
|
||||||
|
lineLength := 76
|
||||||
|
var result strings.Builder
|
||||||
|
for i := 0; i < len(str); i++ {
|
||||||
|
if i > 0 && i%lineLength == 0 {
|
||||||
|
result.WriteString("\r\n")
|
||||||
|
}
|
||||||
|
result.WriteByte(str[i])
|
||||||
|
}
|
||||||
|
return result.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func sign(merchantId string, privateKeyHex string, signContent string) (string, error) {
|
||||||
|
privateKey, err := sm2.ReadPrivateKeyFromHex(privateKeyHex)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
r, s, err := sm2.Sm2Sign(privateKey, []byte(signContent), []byte(merchantId), rand.Reader)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return rSToSign(r, s), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func verify(content string, signature string, publicKeyStr string, merchantId string) bool {
|
||||||
|
pubKey, err := sm2.ReadPublicKeyFromHex(publicKeyStr)
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Sprintf("pubKeyBytes sm2 ReadPublicKeyFromHex err: %v", err))
|
||||||
|
}
|
||||||
|
r, s := signToRS(signature)
|
||||||
|
return sm2.Sm2Verify(pubKey, []byte(content), []byte(merchantId), r, s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func signToRS(signStr string) (*big.Int, *big.Int) {
|
||||||
|
signSub := strings.Split(signStr, "#")
|
||||||
|
if len(signSub) != 2 {
|
||||||
|
panic(fmt.Sprintf("err rs: %x", signSub))
|
||||||
|
}
|
||||||
|
r, _ := new(big.Int).SetString(signSub[0], 16)
|
||||||
|
s, _ := new(big.Int).SetString(signSub[1], 16)
|
||||||
|
return r, s
|
||||||
|
}
|
||||||
|
|
||||||
|
func rSToSign(r *big.Int, s *big.Int) string {
|
||||||
|
rStr := r.Text(16)
|
||||||
|
sStr := s.Text(16)
|
||||||
|
return fmt.Sprintf("%s#%s", rStr, sStr)
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,48 @@
|
||||||
|
package sm4
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
SELF_PRI = "EA7CB6F907A96264D6763F8C46AB96B476538D2ABC880A459E10BE5A1C30013D"
|
||||||
|
|
||||||
|
SELF_PUB = "04363DF574D4FE34EE58FB8A3F7CB08E6CA5EBB3B7335CBAE10A2900551F6450AB3AD25DBC0A76EFA9E6D44D2C51E3027483F7BFD09996457888BAFD1AF673817F"
|
||||||
|
|
||||||
|
PARTY_PRI = "EEB0A34DE1F05154E4B96E50BFC1F8B9750EEAE799F5708C7AFBABB9AFCFA1BF"
|
||||||
|
|
||||||
|
PARTY_PUB = "0420425DF33945C9D5596A33ED60ABEEFD0165C27BA7D6C95D37344E9223149FEAF4C10CEACD7EC88E8DD1E0BDEA71FD09BE2077A1BDC61BC45587B7CC3613F85A"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestGenerateKey(t *testing.T) {
|
||||||
|
hexPri, publicKeyHex := GenerateKey()
|
||||||
|
fmt.Println(hexPri, publicKeyHex)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSM4Encrypt(t *testing.T) {
|
||||||
|
t.Log(encrypt())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSM4Decrypt(t *testing.T) {
|
||||||
|
|
||||||
|
uid, en := encrypt()
|
||||||
|
//uid := "1729382256910270475"
|
||||||
|
//en := "{\"accessToken\":\"77F59F157466F309E703F43CFDCFFECF\",\"encryptKey\":\"04237241E4A465C18645985B3C1DAE987B5DDF1C077AA27D677C843C52E833A24711C63F2512D1A4C37E85A5C9CEAC94156C97460CB5E8966CAD2A0C2596A64ED69CF3306D031C4AADAA73D165FB7EEC34E5AAF532A301169847560329F7F1E40E9FD09EB191976BB49ABFE611E05158EF\",\"request\":\"mkuquQ1RB2AtZYzBtWtLPJ3MMULWj7I5RmK3dKENYVW8OgB//I/+MAD/XnjxYYJlRWM8uL/rWL9o\\r\\n0L7W9fSBvOXWwz8reiwcAF/JZ5dnacZtVe0NPujDfeVIJp+9ua7hxQcegcEsIRS9CQqtF/rJN7M9\\r\\nxxCLSzEiJY8bIdRLBsfmB0uVIE2144s2tH7Q8R2fSOVbiSTH1PW3Ye0kkyCcBw==\",\"signature\":\"ed99a2ce827a4d443e2639ccfb78865e913854e2d61d1746dafb640e19cbb4f8#3793b1cac082d9e1cdfc2b7b8387a8c29e44b2f3fb35b352d02f1b2c9321e0a7\"}"
|
||||||
|
decrypt, err := Sm4Decrypt(uid, SELF_PRI, PARTY_PUB, en, true)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
t.Log(decrypt)
|
||||||
|
}
|
||||||
|
|
||||||
|
func encrypt() (string, string) {
|
||||||
|
uid := strconv.FormatInt(int64(5476377146882523149), 10)
|
||||||
|
data := "{\"pay_channel_id\":1729382256910270476,\"out_trade_no\":\"asdadasdas\",\"order_type\":1,\"amount\":1,\"desc\":\"abc\",\"ext_json\":\"\",\"app_id\":5476377146882523149,\"timestamp\":1723096731}"
|
||||||
|
en, err := Sm4Encrypt(uid, PARTY_PRI, SELF_PUB, data, "", true)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return uid, en
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
package errcode
|
||||||
|
|
||||||
|
type EncryptErr struct {
|
||||||
|
Message string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *EncryptErr) Error() string {
|
||||||
|
return e.Message
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
AppKeyNotFound = &EncryptErr{Message: "资源未找到"}
|
||||||
|
AppEncryptFail = &EncryptErr{Message: "加密失败"}
|
||||||
|
AppDecryptFail = &EncryptErr{Message: "解密失败"}
|
||||||
|
)
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
package pojo
|
||||||
|
|
||||||
|
const (
|
||||||
|
RSA = 1
|
||||||
|
SM2 = 2
|
||||||
|
SM3 = 4
|
||||||
|
SM4 = 3
|
||||||
|
)
|
||||||
|
|
@ -0,0 +1,34 @@
|
||||||
|
package encrypt
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math/rand"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
const lettersString = "0123456789abcdefghijkmnpqrstuvwxyz"
|
||||||
|
|
||||||
|
/*
|
||||||
|
16位码,前15位随机字符串,最后一位通过前15位字符串计算校验生成
|
||||||
|
*/
|
||||||
|
|
||||||
|
func LotteryEncryptEncode(number int) string {
|
||||||
|
b := make([]byte, number)
|
||||||
|
var sum byte
|
||||||
|
for i := 0; i < number-1; i++ {
|
||||||
|
b[i] = lettersString[rand.Int63()%int64(len(lettersString))]
|
||||||
|
sum += b[i]
|
||||||
|
}
|
||||||
|
b[number-1] = lettersString[sum%byte(len(lettersString))]
|
||||||
|
return *(*string)(unsafe.Pointer(&b))
|
||||||
|
}
|
||||||
|
|
||||||
|
func LotteryEncryptDecode(str string) bool {
|
||||||
|
var sum byte
|
||||||
|
for i := 0; i < len(str)-1; i++ {
|
||||||
|
sum += str[i]
|
||||||
|
}
|
||||||
|
if lettersString[sum%byte(len(lettersString))] != str[len(str)-1] {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
package encrypt
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestLotteryEncryptEncode(t *testing.T) {
|
||||||
|
code := LotteryEncryptEncode(16)
|
||||||
|
t.Log(code)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLotteryEncryptDecode(t *testing.T) {
|
||||||
|
code := LotteryEncryptEncode(16)
|
||||||
|
t.Log(code)
|
||||||
|
result := LotteryEncryptDecode(code)
|
||||||
|
t.Log(result)
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,44 @@
|
||||||
|
package encrypt
|
||||||
|
|
||||||
|
import (
|
||||||
|
"voucher/internal/pkg/cmb/encrypt/encrypt_way/sm2"
|
||||||
|
"voucher/internal/pkg/cmb/encrypt/errcode"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewSm2(app *AppEncrypt) ApiCrypt {
|
||||||
|
return &SM2{
|
||||||
|
App: app,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *SM2) Encrypt(data string) (encryptData []byte, err error) {
|
||||||
|
|
||||||
|
if r.App.PrivateKey == "" {
|
||||||
|
return nil, errcode.AppKeyNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
encryptDataString, err := sm2.SM2Encrypt(data, r.App.PrivateKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errcode.AppEncryptFail
|
||||||
|
}
|
||||||
|
encryptData = []byte(encryptDataString)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *SM2) Decrypt(encryptData string) (decryptData []byte, err error) {
|
||||||
|
defer func() {
|
||||||
|
if e := recover(); e != nil {
|
||||||
|
err = errcode.AppDecryptFail
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
if r.App.PrivateKey == "" || r.App.PublicKey == "" {
|
||||||
|
return nil, errcode.AppKeyNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
decryptDataString, err := sm2.SM2Decrypt(encryptData, r.App.PublicKey, r.App.PrivateKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errcode.AppDecryptFail
|
||||||
|
}
|
||||||
|
decryptData = []byte(decryptDataString)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,132 @@
|
||||||
|
package encrypt
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/ecdsa"
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/hex"
|
||||||
|
"encoding/pem"
|
||||||
|
"fmt"
|
||||||
|
"github.com/emmansun/gmsm/sm2"
|
||||||
|
"github.com/emmansun/gmsm/smx509"
|
||||||
|
)
|
||||||
|
|
||||||
|
type KeyType string
|
||||||
|
|
||||||
|
const (
|
||||||
|
Raw KeyType = "raw"
|
||||||
|
Hex = "hex"
|
||||||
|
Base64 = "base64"
|
||||||
|
Pem = "pem"
|
||||||
|
)
|
||||||
|
|
||||||
|
const publicPemFormat = `-----BEGIN PUBLIC KEY-----
|
||||||
|
%s
|
||||||
|
-----END PUBLIC KEY-----`
|
||||||
|
|
||||||
|
const privatePemFormat = `-----BEGIN PRIVATE KEY-----
|
||||||
|
%s
|
||||||
|
-----END PRIVATE KEY-----`
|
||||||
|
|
||||||
|
func ParsePublicKey(key string, kt KeyType) (*ecdsa.PublicKey, error) {
|
||||||
|
if len(key) == 0 {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
switch kt {
|
||||||
|
case Raw:
|
||||||
|
return sm2.NewPublicKey([]byte(key))
|
||||||
|
case Hex:
|
||||||
|
pubBytes, err := hex.DecodeString(key)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return sm2.NewPublicKey(pubBytes)
|
||||||
|
case Base64:
|
||||||
|
pubBytes, err := base64.StdEncoding.DecodeString(key)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return sm2.NewPublicKey(pubBytes)
|
||||||
|
case Pem:
|
||||||
|
pubPEM := fmt.Sprintf(publicPemFormat, key)
|
||||||
|
block, _ := pem.Decode([]byte(pubPEM))
|
||||||
|
if block == nil {
|
||||||
|
return nil, fmt.Errorf("failed to parse PEM block containing the public key")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub, err := smx509.ParsePKIXPublicKey(block.Bytes)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !sm2.IsSM2PublicKey(pub) {
|
||||||
|
return nil, fmt.Errorf("not a SM2 public key")
|
||||||
|
}
|
||||||
|
|
||||||
|
if key, ok := pub.(*ecdsa.PublicKey); ok {
|
||||||
|
return key, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, fmt.Errorf("not a valid SM2 public key")
|
||||||
|
default:
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParsePrivateKey(key string, kt KeyType) (*sm2.PrivateKey, error) {
|
||||||
|
if len(key) == 0 {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
switch kt {
|
||||||
|
case Raw:
|
||||||
|
return sm2.NewPrivateKey([]byte(key))
|
||||||
|
case Hex:
|
||||||
|
priBytes, err := hex.DecodeString(key)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return sm2.NewPrivateKey(priBytes)
|
||||||
|
case Base64:
|
||||||
|
priBytes, err := base64.StdEncoding.DecodeString(key)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return sm2.NewPrivateKey(priBytes)
|
||||||
|
case Pem:
|
||||||
|
priPEM := fmt.Sprintf(privatePemFormat, key)
|
||||||
|
block, _ := pem.Decode([]byte(priPEM))
|
||||||
|
if block == nil {
|
||||||
|
return nil, fmt.Errorf("failed to parse PEM block containing the private key")
|
||||||
|
}
|
||||||
|
|
||||||
|
pri, err := smx509.ParsePKCS8PrivateKey(block.Bytes)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if pk, ok := pri.(*sm2.PrivateKey); ok {
|
||||||
|
return pk, nil
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("not a valid SM2 private key")
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("unsupported key type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func PriToPub(priKey string) (string, error) {
|
||||||
|
priBytes, err := hex.DecodeString(priKey)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
priObj, err := sm2.NewPrivateKey(priBytes)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
pubBytes, err := smx509.MarshalPKIXPublicKey(&priObj.PublicKey)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return hex.EncodeToString(pubBytes), nil
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,36 @@
|
||||||
|
package encrypt
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/rand"
|
||||||
|
em2 "github.com/emmansun/gmsm/sm2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SM3WithSM2Sign SM3WithSM2签名 Hex ToUpper
|
||||||
|
func SM3WithSM2Sign(key string, data string) ([]byte, error) {
|
||||||
|
//掌上生活文档是错误的,实际只需要对data进行本身进行签名,而不需要对data的sm3摘要进行签名
|
||||||
|
//hashed := sm3.Sm3Sum([]byte(data))
|
||||||
|
// 获取私钥
|
||||||
|
|
||||||
|
//hashed := em3.Sum([]byte(data))
|
||||||
|
// 获取私钥
|
||||||
|
privateKey, err := ParsePrivateKey(key, Hex)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
signed, err := privateKey.Sign(rand.Reader, []byte(data), em2.DefaultSM2SignerOpts)
|
||||||
|
return signed, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func Sm2Sm3VerySign(data, sign string, pubKey string) (bool, error) {
|
||||||
|
// 计算hash值
|
||||||
|
//hashed := em3.Sum([]byte(data))
|
||||||
|
// 获取公钥
|
||||||
|
publicKey, err := ParsePublicKey(pubKey, Hex)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
// 验证签名
|
||||||
|
ok := em2.VerifyASN1WithSM2(publicKey, nil, []byte(data), []byte(sign))
|
||||||
|
return ok, nil
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,43 @@
|
||||||
|
package encrypt
|
||||||
|
|
||||||
|
import (
|
||||||
|
"voucher/internal/pkg/cmb/encrypt/encrypt_way/sm4"
|
||||||
|
"voucher/internal/pkg/cmb/encrypt/errcode"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewSm4(app *AppEncrypt) ApiCrypt {
|
||||||
|
return &SM4{
|
||||||
|
App: app,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *SM4) Encrypt(data string) (encryptData []byte, err error) {
|
||||||
|
if r.App.MerchantPublicKey == "" || r.App.PrivateKey == "" {
|
||||||
|
return nil, errcode.AppKeyNotFound
|
||||||
|
}
|
||||||
|
encryptDataString, err := sm4.Sm4Encrypt(r.App.UniKEY, r.App.PrivateKey, r.App.MerchantPublicKey, data, "", true)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errcode.AppEncryptFail
|
||||||
|
}
|
||||||
|
encryptData = []byte(encryptDataString)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *SM4) Decrypt(encryptData string) (decryptData []byte, err error) {
|
||||||
|
defer func() {
|
||||||
|
if e := recover(); e != nil {
|
||||||
|
err = errcode.AppDecryptFail
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
if r.App.PrivateKey == "" || r.App.MerchantPublicKey == "" {
|
||||||
|
return nil, errcode.AppKeyNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
decryptDataString, err := sm4.Sm4Decrypt(r.App.UniKEY, r.App.PrivateKey, r.App.MerchantPublicKey, encryptData, true)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errcode.AppDecryptFail
|
||||||
|
}
|
||||||
|
decryptData = []byte(decryptDataString)
|
||||||
|
return
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,39 @@
|
||||||
|
package encrypt
|
||||||
|
|
||||||
|
import (
|
||||||
|
"voucher/internal/pkg/cmb/encrypt/pojo"
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
ApiCrypt interface {
|
||||||
|
Encrypt(data string) (encryptData []byte, err error)
|
||||||
|
Decrypt(encryptData string) (decryptData []byte, err error)
|
||||||
|
}
|
||||||
|
|
||||||
|
Rsa struct {
|
||||||
|
App *AppEncrypt
|
||||||
|
}
|
||||||
|
|
||||||
|
SM2 struct {
|
||||||
|
App *AppEncrypt
|
||||||
|
}
|
||||||
|
|
||||||
|
SM4 struct {
|
||||||
|
App *AppEncrypt
|
||||||
|
}
|
||||||
|
|
||||||
|
AppEncrypt struct {
|
||||||
|
UniKEY string
|
||||||
|
MerchantPublicKey string
|
||||||
|
PublicKey string
|
||||||
|
PrivateKey string
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
var ApiCryptMap = map[int32]func(en *AppEncrypt) ApiCrypt{
|
||||||
|
//pojo.RSA: NewRsa,
|
||||||
|
pojo.SM2: NewSm2,
|
||||||
|
pojo.SM4: NewSm4,
|
||||||
|
}
|
||||||
|
|
||||||
|
const CryptNotError = 0
|
||||||
|
|
@ -0,0 +1,136 @@
|
||||||
|
package cmb
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/cipher"
|
||||||
|
"crypto/rand"
|
||||||
|
"encoding/base64"
|
||||||
|
"fmt"
|
||||||
|
"github.com/ZZMarquis/gm/util"
|
||||||
|
tsm2 "github.com/tjfoc/gmsm/sm2"
|
||||||
|
"github.com/tjfoc/gmsm/sm4"
|
||||||
|
"github.com/tjfoc/gmsm/x509"
|
||||||
|
"strings"
|
||||||
|
"voucher/internal/pkg/cmb/encrypt"
|
||||||
|
)
|
||||||
|
|
||||||
|
const messageSeparator = byte(0x7C)
|
||||||
|
|
||||||
|
type Decrypts struct {
|
||||||
|
EncryptBody string
|
||||||
|
PrivateKey string
|
||||||
|
}
|
||||||
|
|
||||||
|
type Encrypts struct {
|
||||||
|
JsonParam string
|
||||||
|
SoaPubKey string
|
||||||
|
}
|
||||||
|
|
||||||
|
func EncryptBody(encrypts *Encrypts) (string, error) {
|
||||||
|
|
||||||
|
paddedPlaintext := util.PKCS5Padding([]byte(encrypts.JsonParam), sm4.BlockSize)
|
||||||
|
//随机生成对称密钥RandomKey和偏移向量RandomIV
|
||||||
|
randomKey := make([]byte, sm4.BlockSize)
|
||||||
|
randomIV := make([]byte, sm4.BlockSize)
|
||||||
|
if _, err := rand.Read(randomKey); err != nil {
|
||||||
|
return "", fmt.Errorf("生成randomKey失败:%v", err)
|
||||||
|
}
|
||||||
|
if _, err := rand.Read(randomIV); err != nil {
|
||||||
|
return "", fmt.Errorf("生成randomIV失败: %v", err)
|
||||||
|
}
|
||||||
|
// 使用对称密钥对报文进行加密,并对结果进行Base64编码
|
||||||
|
//对称加密算法为SM4/CBC/PKCS5Padding,密钥长度为16字节,明文长度不做要求,IV长度为16字节。
|
||||||
|
block, err := sm4.NewCipher(randomKey)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("IV加密失败: %v", err)
|
||||||
|
}
|
||||||
|
ciphertext := make([]byte, len(paddedPlaintext))
|
||||||
|
if len(paddedPlaintext)%sm4.BlockSize != 0 {
|
||||||
|
return "", fmt.Errorf("加密格式错误:%v", err)
|
||||||
|
}
|
||||||
|
mode := cipher.NewCBCEncrypter(block, randomIV)
|
||||||
|
mode.CryptBlocks(ciphertext, paddedPlaintext)
|
||||||
|
//ciphertext, err := sm4.Sm4Cbc(randomKey, randomIV, util.PKCS5Padding([]byte(encrypts.JsonParam), sm4.BlockSize))
|
||||||
|
|
||||||
|
ciphertextBase64 := base64.StdEncoding.EncodeToString(ciphertext)
|
||||||
|
|
||||||
|
//使用掌上生活的SM2算法公钥对“RandomKey|RandomIV”进行base64之后加密,并对结果进行Base64编码
|
||||||
|
//非对称加密算法为SM2,公钥长度为65字节,私钥长度为32字节。
|
||||||
|
keyAndIV := AssemblingByteArray(randomKey, randomIV)
|
||||||
|
keyAndIVBase64 := base64.StdEncoding.EncodeToString(keyAndIV)
|
||||||
|
pubk, err := x509.ReadPublicKeyFromHex(encrypts.SoaPubKey)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("解析公钥失败: %v", err)
|
||||||
|
}
|
||||||
|
sm2EncryptKeyAndIV, err := tsm2.Encrypt(pubk, []byte(keyAndIVBase64), rand.Reader, tsm2.C1C3C2)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("RandomKey|RandomIV加密失败: %v", err)
|
||||||
|
}
|
||||||
|
sm2EncryptKeyAndIVBase64 := base64.StdEncoding.EncodeToString(sm2EncryptKeyAndIV)
|
||||||
|
|
||||||
|
//将加密后的对称密钥以及加密后的报文进行拼接,使用“ | ”进行分割。
|
||||||
|
encryptedBody := fmt.Sprintf("%s|%s", sm2EncryptKeyAndIVBase64, ciphertextBase64)
|
||||||
|
return encryptedBody, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func DecryptBody(decrypts *Decrypts) ([]byte, error) {
|
||||||
|
var (
|
||||||
|
EncryptSlice []string
|
||||||
|
)
|
||||||
|
EncryptSlice = strings.Split(decrypts.EncryptBody, "|")
|
||||||
|
if len(EncryptSlice) != 2 {
|
||||||
|
return nil, fmt.Errorf("解密失败: 格式错误")
|
||||||
|
}
|
||||||
|
sm2EncryptKeyAndIV, err := base64.StdEncoding.DecodeString(EncryptSlice[0])
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("解密失败:向量格式错误: %v", err)
|
||||||
|
}
|
||||||
|
ciphertext, err := base64.StdEncoding.DecodeString(EncryptSlice[1])
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("解密失败:内容格式错误: %v", err)
|
||||||
|
}
|
||||||
|
prik, err := x509.ReadPrivateKeyFromHex(decrypts.PrivateKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("解密失败:密钥错误: %v", err)
|
||||||
|
}
|
||||||
|
keyAndIv, err := tsm2.Decrypt(prik, sm2EncryptKeyAndIV, tsm2.C1C3C2)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("解密失败: %v", err)
|
||||||
|
}
|
||||||
|
key, iv, err := Base64DecodeAndSplit(string(keyAndIv))
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("解密失败:解密后向量格式错误: %v", err)
|
||||||
|
}
|
||||||
|
block, err := sm4.NewCipher(key)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("IV加密失败: %v", err)
|
||||||
|
}
|
||||||
|
paddedPlaintext := make([]byte, len(ciphertext))
|
||||||
|
mode := cipher.NewCBCDecrypter(block, iv)
|
||||||
|
mode.CryptBlocks(paddedPlaintext, ciphertext)
|
||||||
|
res := util.PKCS5UnPadding(paddedPlaintext)
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
|
func VerifyBody(crypt string, signDataBase64 string, pubKey string) (bool, error) {
|
||||||
|
signByte, _ := base64.StdEncoding.DecodeString(signDataBase64)
|
||||||
|
return encrypt.Sm2Sm3VerySign(crypt, string(signByte), pubKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
func SignBody(sign string, privateKey string) (string, error) {
|
||||||
|
//签名
|
||||||
|
signData, err := encrypt.SM3WithSM2Sign(privateKey, sign)
|
||||||
|
return base64.StdEncoding.EncodeToString(signData), err
|
||||||
|
}
|
||||||
|
|
||||||
|
func Base64DecodeAndSplit(encoded string) ([]byte, []byte, error) {
|
||||||
|
// 解码 Base64 字符串
|
||||||
|
decodedBytes, err := base64.StdEncoding.DecodeString(encoded)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 拆分字节切片
|
||||||
|
firstParam := decodedBytes[:sm4.BlockSize]
|
||||||
|
secondParam := decodedBytes[sm4.BlockSize+1:] // +1 以跳过分隔符本身
|
||||||
|
|
||||||
|
return firstParam, secondParam, nil
|
||||||
|
}
|
||||||
|
|
@ -62,6 +62,7 @@ func Encrypt(sopPublicKey, input string) (string, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
fmt.Println(base64.StdEncoding.EncodeToString([]byte(kvTmp)))
|
||||||
|
|
||||||
return fmt.Sprintf("%s|%s", base64.StdEncoding.EncodeToString([]byte(kvTmp)), base64.StdEncoding.EncodeToString(encryptedBody)), nil
|
return fmt.Sprintf("%s|%s", base64.StdEncoding.EncodeToString([]byte(kvTmp)), base64.StdEncoding.EncodeToString(encryptedBody)), nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
package sm2
|
package sm2
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
"voucher/internal/pkg/cmb/sm2/model"
|
"voucher/internal/pkg/cmb/sm2/model"
|
||||||
"voucher/internal/pkg/cmb/sm2/sdk"
|
"voucher/internal/pkg/cmb/sm2/sdk"
|
||||||
"voucher/internal/pkg/cmb/sm2/util"
|
"voucher/internal/pkg/cmb/sm2/util"
|
||||||
|
|
@ -13,6 +15,50 @@ var (
|
||||||
enStr = "BI2O20YuamIpOpXH/RJDtB9gWIpNZPjFbTpbvX45lG8mcma7Cab1yRpE3rcM33oJ8xJ3mbbIvXf/1N507i97eXYQSbLzOBcl/8PyaB4dQ/Ub7QQncN+Npuif4Qp6s11tobdTVOFlz9S9i0VC"
|
enStr = "BI2O20YuamIpOpXH/RJDtB9gWIpNZPjFbTpbvX45lG8mcma7Cab1yRpE3rcM33oJ8xJ3mbbIvXf/1N507i97eXYQSbLzOBcl/8PyaB4dQ/Ub7QQncN+Npuif4Qp6s11tobdTVOFlz9S9i0VC"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func BenchmarkSm2_Encrypt(b *testing.B) {
|
||||||
|
for j := 0; j < 1000; j++ {
|
||||||
|
for i := 0; i < 300; i++ {
|
||||||
|
go func() {
|
||||||
|
_, err := NewSm2().
|
||||||
|
SetHexPublicKey(pubHexKey).
|
||||||
|
SetStringData(`{"name":"zhangxx","phoneNo":"137xxxxxxxx"}`).
|
||||||
|
SetSdk(sdk.NewCmbLifeSdk()).
|
||||||
|
SetCipherType(model.C1C3C2).
|
||||||
|
Encrypt().
|
||||||
|
ToBase64String()
|
||||||
|
if err != nil {
|
||||||
|
//("sm2 encrypt error:", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
}
|
||||||
|
|
||||||
|
select {}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkSm2_Decrypt(b *testing.B) {
|
||||||
|
s, err := util.HexToSignature("abd5a608aad610840bd387263c81f8813e7ab4b121cd69f0d69ac4736cc29978d44c47d9bbf497c712e067c320d3e6b8ef70063da284cc957440e36733f0ff81")
|
||||||
|
if err != nil {
|
||||||
|
//fmt.Println("hex to signature error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = NewSm2().
|
||||||
|
SetUid([]byte("opv22s05zaezfccc")).
|
||||||
|
SetSignature(model.Signature(s)).
|
||||||
|
SetHexPublicKey("04c443e81459725ae9979ce44ce8c0e1a45fdf6e884c2fc1e615db02909698b7fdd81fab5404060e2ab849b9e1529397ee4c45ae0d768aebf48d943f953320f78e").
|
||||||
|
SetStringData("apiCode=conductor.cashier.queryTransStatus&appId=bolz18v22s05zaezfccc×tamp=2022070417330581653&version=1.0.0").
|
||||||
|
Verify().
|
||||||
|
ToBool()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("sm2 verify error:", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//t.Log("sm2 verify result:", verify)
|
||||||
|
}
|
||||||
|
|
||||||
func TestSm2_Encrypt(t *testing.T) {
|
func TestSm2_Encrypt(t *testing.T) {
|
||||||
encrypt, err := NewSm2().
|
encrypt, err := NewSm2().
|
||||||
SetHexPublicKey(pubHexKey).
|
SetHexPublicKey(pubHexKey).
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,8 @@ package cmb
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
_ "gitea.cdlsxd.cn/self-tools/l_crypt"
|
||||||
"testing"
|
"testing"
|
||||||
v1 "voucher/api/v1"
|
v1 "voucher/api/v1"
|
||||||
)
|
)
|
||||||
|
|
@ -166,3 +168,41 @@ func TestGenerateSm2KeyAndEncryptDecrypt(t *testing.T) {
|
||||||
}
|
}
|
||||||
t.Log(aa)
|
t.Log(aa)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestZhEncryptVerify(t *testing.T) {
|
||||||
|
pukKey := "04838f74275e6f4f2373d4e6e974ac790c10ab6f9c17e273cf0c84848c6838979c158315932e36f0b9444442f145e4671b1ee5d43d5d63913a70d4d0d52cc3c0d6"
|
||||||
|
|
||||||
|
content := "accessToken.json?aid=9dad6d3900ec3ffabd80e46522a10ead&cmbKeyAlias=SM2_CMBLIFE&date=20250305115032&encryptBody=BHeko/ZYFzQOJn6Q3y46X1AjNz8Nh5fq1FfMuWebh+TangLnlK5iFqePCst4rjG/FKJInijiKO2Qq18sJULlMEEgri05s+bHHDKM+Y+73crAbCnIhHbZxUjt8A0cq2rKjzkl8bxW33dU18uuiTEAmsAvKvmZgE6zJ1eDyjFWefHEIFJaKCNY2cTQOInt|8UYFZFTkx0DovPhaWCbdBkAqbgGmegT14F5gwXLJ6G1uWdYNvX+i5QWAYUGtd8u9&keyAlias=CO_PUB_KEY_SM2&mid=f806c259d86e3b9aa956c98d475b6af7&random=286d97b1d8ed4bbf822b004470c92ae8"
|
||||||
|
signDataBase64 := "MEUCIAg1zxnKG+X8t/hlwEoyL/T33iKgle09S6bfb3eZh/FqAiEAxtK5TdGAo+JpK7JmL15tT4nlQyrUzC14flu0Tq+9Svo="
|
||||||
|
|
||||||
|
ok, err := VerifyBody(content, signDataBase64, pukKey)
|
||||||
|
fmt.Println(ok, err)
|
||||||
|
}
|
||||||
|
func TestZhEncryptDecrypt(t *testing.T) {
|
||||||
|
priKey := "f6a8d2f412e289686aba6a0f33cad1a64367d0ba012046ee0fbbefd3ffd675bd"
|
||||||
|
|
||||||
|
//content := "BAdcIauIjNx3LsrplpJiZoljE4hCiGHra6ulhgG1qL0tKcAeenX+Z9VaHfXLSdkji1fYBpdZiiI35R0vFtnXPXJCJdHsGbfbae+PzNznYQS3KM8/90Y/FIWzSoszfUiF6fAuv8I6v9kQuqHUTidHeHyICDoyvJ0nhbNyUyg85bAKd6TmkVX1MgXLQ81m|5KfR/5UkpVBEQv1dx+iJbojOykNRuDV8Gsy3QOIlRI+cZvafRRPUUG6eeixnPMumhOvyZwsSG/OBeg0U/lSlAepg12tXWcQ601wjgyLaKN1iMvb1DCtfnJFAm8EWAc2SLH3NQuyhxGe/jgCXvj0wGphh4vBUzm8la8i8Aij0BI5lfgU5OzglkKDln6zHN3vBHDqOurEh18eU6z1bfvNnDpzdwEcygcEIH/6lGiqVnGH+C2+QpcKeCnj5qKGFiuSC"
|
||||||
|
//content := "BDUuuPClIlUKDJdpHtNBIU6u5JTetrVG53AfKDSrhah9Q0QZWAj3K5pZF8G/HFtzj/KvbrHfP/gnHri0L7L91HaKYYc1vqy/q8Z69v7MEiIWL2LeLOsc2b0cHmnt7Qey5aZzYVbZJJNhus2gvhahGwOPpPL50JFC8IqAlU4+E/kUBgx+RgzAIkLLs8Se|k1WOt2Eb+xkxSobw/1DaLblguznhnsk1ga87sfqrrPATRuTyszzdVrRtCUuUHRKJ+vQwlZAR1ypkBC1vPMdt8zccI/CeNof+4Ap23enbQwTJQ4KRvij4kbJd6ycY97B+vHnI+oSnwfmjK0EWNUeRrCr2Uau7yxmzlFwJHprbgPZpHVuUjAzMIXNixOfxSTdc9dL+j3/tR7+yookvd9W3hNm96XZFPTheI0FqUKkbKWUUcVs4lC1/yEgjwXFcD4NM"
|
||||||
|
content := "BDqANzEC9vq82FTu9wR89Ou/8o2J2vD7yeoExb1Uxl2PSA9EsvI7YQNGSkSdxoloqRBn4vIrA8Uh+FmWtJ9wbFEEtbC+Epj8qHWN8S+lH9JEnvJhPhEtMgmHeuGDtvzHMACvc7KoBmJ/6XZIoEyLeRgkJnCTVVxnD78KMJ4eXzj7C2ErkISJ5r3qcL9t|cPw/uy87PW74aeU1RwGwUsYXZlXYkG0m+BoO/3qT4rLSIWsHvBN76yQJlTk89aa9VALdtwcU7H0q7ivCLZ9uflB6YFkg5QRQoj1b2AFFU4TPkpgAdCBAKs5+6z9cwDn+QQyuamKAnp1wJfMj7Ksa3HxLwCxTh/w2dF6LuruLFWZstMK1g9ID7tVTPqc05VNmktmNhc97BYuIH66xa0ZFgr+0i/3hFMx0hNTOau5UWFM6v4Lb1iM/v9ggM5ZvAheUspfgvRUhpD2TZvgyneWxqg=="
|
||||||
|
|
||||||
|
//decryptStr, err := Decrypt(priKey, content)
|
||||||
|
//if err != nil {
|
||||||
|
// t.Log(err)
|
||||||
|
// return
|
||||||
|
//}
|
||||||
|
//签名
|
||||||
|
rs, err := DecryptBody(&Decrypts{content, priKey})
|
||||||
|
|
||||||
|
fmt.Println(rs, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestZhEncryptEncrypt(t *testing.T) {
|
||||||
|
pukKey := "04a702106cf530dc981e44cd515b394747cfd6bb059247696b188b25281ea4278fe7c6e34a83680110eec71becd31f5db14abc671e5d8e67ce7ca3c6b3adc86674"
|
||||||
|
content := `{"name":"zhangxx","phoneNo":"137xxxxxxxx"}`
|
||||||
|
xx, err := Encrypt(pukKey, content)
|
||||||
|
t.Log(xx, err)
|
||||||
|
//签名
|
||||||
|
rs, err := EncryptBody(&Encrypts{content, pukKey})
|
||||||
|
|
||||||
|
fmt.Println(rs, err)
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ import (
|
||||||
"github.com/go-kratos/kratos/v2/middleware/recovery"
|
"github.com/go-kratos/kratos/v2/middleware/recovery"
|
||||||
"github.com/go-kratos/kratos/v2/middleware/validate"
|
"github.com/go-kratos/kratos/v2/middleware/validate"
|
||||||
"github.com/go-kratos/kratos/v2/transport/http"
|
"github.com/go-kratos/kratos/v2/transport/http"
|
||||||
|
"github.com/go-kratos/kratos/v2/transport/http/pprof"
|
||||||
"github.com/gorilla/handlers"
|
"github.com/gorilla/handlers"
|
||||||
http2 "net/http"
|
http2 "net/http"
|
||||||
v1 "voucher/api/v1"
|
v1 "voucher/api/v1"
|
||||||
|
|
@ -28,6 +29,7 @@ func NewHTTPServer(
|
||||||
) *http.Server {
|
) *http.Server {
|
||||||
//构建 server
|
//构建 server
|
||||||
srv := buildHTTPServer(c, accessLogger, log)
|
srv := buildHTTPServer(c, accessLogger, log)
|
||||||
|
srv.Handle("/debug/pprof/", pprof.NewHandler())
|
||||||
|
|
||||||
srv.Route("/").GET("ping", func(ctx http.Context) error {
|
srv.Route("/").GET("ping", func(ctx http.Context) error {
|
||||||
return ctx.String(http2.StatusOK, "pong")
|
return ctx.String(http2.StatusOK, "pong")
|
||||||
|
|
|
||||||
|
|
@ -157,9 +157,7 @@ components:
|
||||||
properties:
|
properties:
|
||||||
transactionId:
|
transactionId:
|
||||||
type: string
|
type: string
|
||||||
description: |-
|
description: "业务参数\r\n 唯一流水号,需支持14天内幂等"
|
||||||
业务参数
|
|
||||||
唯一流水号,需支持14天内幂等
|
|
||||||
activityId:
|
activityId:
|
||||||
type: string
|
type: string
|
||||||
description: 外部合作方权益批次号
|
description: 外部合作方权益批次号
|
||||||
|
|
@ -183,25 +181,19 @@ components:
|
||||||
properties:
|
properties:
|
||||||
activityId:
|
activityId:
|
||||||
type: string
|
type: string
|
||||||
description: |-
|
description: "业务参数\r\n 外部合作方权益批次号"
|
||||||
业务参数
|
|
||||||
外部合作方权益批次号
|
|
||||||
api.v1.CmbQueryRequest:
|
api.v1.CmbQueryRequest:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
codeNo:
|
codeNo:
|
||||||
type: string
|
type: string
|
||||||
description: |-
|
description: "业务参数\r\n 外部合作方权益批次号"
|
||||||
业务参数
|
|
||||||
外部合作方权益批次号
|
|
||||||
api.v1.CmbReply:
|
api.v1.CmbReply:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
respCode:
|
respCode:
|
||||||
type: string
|
type: string
|
||||||
description: |-
|
description: "响应公共参数\r\n 接口调用返回码,1000 成功,1001 失败"
|
||||||
响应公共参数
|
|
||||||
接口调用返回码,1000 成功,1001 失败
|
|
||||||
respMsg:
|
respMsg:
|
||||||
type: string
|
type: string
|
||||||
description: 返回话术,失败信息落此字段
|
description: 返回话术,失败信息落此字段
|
||||||
|
|
@ -225,9 +217,7 @@ components:
|
||||||
properties:
|
properties:
|
||||||
mid:
|
mid:
|
||||||
type: string
|
type: string
|
||||||
description: |-
|
description: "请求公共参数\r\n 合作方唯一ID,32位定长"
|
||||||
请求公共参数
|
|
||||||
合作方唯一ID,32位定长
|
|
||||||
aid:
|
aid:
|
||||||
type: string
|
type: string
|
||||||
description: 应用唯一ID,32位定长
|
description: 应用唯一ID,32位定长
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue