Browse Source

在线客服

zhengchengwu 4 years ago
parent
commit
b81812685e

+ 5 - 0
controllers/kefu/router_collector.go View File

@@ -0,0 +1,5 @@
1
+package kefu
2
+
3
+func RegisterRouters() {
4
+	TencentUsersigApiRegistRouters()
5
+}

+ 64 - 0
controllers/kefu/tencent_usersig_api_controller.go View File

@@ -0,0 +1,64 @@
1
+package kefu
2
+
3
+import (
4
+	base_ctl "SCRM/controllers"
5
+	"SCRM/enums"
6
+	"SCRM/service/tencentim_service"
7
+	"SCRM/utils"
8
+	"strconv"
9
+
10
+	"github.com/astaxie/beego"
11
+)
12
+
13
+type TencentUsersigApiController struct {
14
+	base_ctl.BaseAuthAPIController
15
+}
16
+
17
+func TencentUsersigApiRegistRouters() {
18
+	beego.Router("/api/tencent/usersig", &TencentUsersigApiController{}, "get:GetUsersig")
19
+
20
+}
21
+
22
+//GetUsersig 签名
23
+func (c *TencentUsersigApiController) GetUsersig() {
24
+
25
+	adminUserInfo := c.GetAdminUserInfo()
26
+
27
+	adminId := adminUserInfo.AdminUser.Id
28
+	Indentifier := "Org_" + strconv.FormatInt(adminUserInfo.CurrentOrgId, 10)
29
+	appIDAt3rd := Indentifier
30
+	sig, err := tencentim_service.CreateUserSig(Indentifier, appIDAt3rd)
31
+	if err != nil {
32
+		utils.ErrorLog("usersig err : %v", err)
33
+		c.ServeFailJSONWithSGJErrorCode(enums.ErrorCodeParamWrong)
34
+		return
35
+	}
36
+
37
+
38
+	orgInfo := adminUserInfo.Orgs[adminUserInfo.CurrentOrgId]
39
+	IdentifierNick := ""
40
+	HeadURL := "http://jk.kuyicloud.com/static/images/ico_gjh.png"
41
+	if orgInfo != nil {
42
+		if len(orgInfo.OrgName) > 0 {
43
+			IdentifierNick = orgInfo.OrgName
44
+		}
45
+		if len(orgInfo.OrgLogo) > 0 {
46
+			HeadURL = orgInfo.OrgLogo
47
+		}
48
+	}
49
+
50
+	returnData := map[string]interface{}{
51
+		"sig":            sig,
52
+		"adminId":        adminId,
53
+		"Indentifier":    Indentifier,
54
+		"appIDAt3rd":     appIDAt3rd,
55
+		"IdentifierNick": IdentifierNick,
56
+		"HeadURL":        HeadURL,
57
+		"sdkAppID":       tencentim_service.ThisSDKAppId,
58
+		"accountType":    tencentim_service.ThisAccType,
59
+		"selToAdmin":     "AdminKeFu",
60
+	}
61
+
62
+	c.ServeSuccessJSON(returnData)
63
+	return
64
+}

+ 1 - 1
main.go View File

@@ -19,7 +19,7 @@ func main() {
19 19
 	//微信开放平台
20 20
 	go func() {
21 21
 		// jobcron.RequestComponentAccessToken()
22
-		jobcron.RequestMpWechatAccessToken()
22
+		// jobcron.RequestMpWechatAccessToken()
23 23
 		// jobcron.ReqJsapiTicket()
24 24
 	}()
25 25
 	jobcron.StartOpenWechatCron()

+ 2 - 0
routers/router.go View File

@@ -9,6 +9,7 @@ import (
9 9
 	"SCRM/controllers/members"
10 10
 	"SCRM/controllers/mpwechat"
11 11
 	"SCRM/controllers/role"
12
+	"SCRM/controllers/kefu"
12 13
 
13 14
 	"github.com/astaxie/beego"
14 15
 	"github.com/astaxie/beego/plugins/cors"
@@ -31,4 +32,5 @@ func init() {
31 32
 	marketing_tool.RegisterRouters()
32 33
 	article.RegisterRouters()
33 34
 	mpwechat.RegisterRouters()
35
+	kefu.RegisterRouters()
34 36
 }

+ 28 - 0
service/tencentim_service/tencent_im_service.go View File

@@ -0,0 +1,28 @@
1
+package tencentim_service
2
+
3
+import (
4
+	"SCRM/utils/tencentsig"
5
+)
6
+
7
+//正式
8
+const (
9
+	ThisAccType    = 29296
10
+	ThisSDKAppId   = "1400103109"
11
+	ThisIdentifier = "kuyiyun" //  admin
12
+	ThisPriKey     = `-----BEGIN PRIVATE KEY-----
13
+MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgpM/saduI49IF+DeL
14
+jEjyU1ryKIEKTyRLekw4w4qeC7ShRANCAAQHinjtGLt6WF2wOfEKA1xomfcS19/i
15
+ZyymFHJlKR49WASx/9XEbm2/TNZUn/qbe5jc4HhujQR7IUVKVFtEU6Oa
16
+-----END PRIVATE KEY-----`
17
+	ThisPubKey = `-----BEGIN PUBLIC KEY-----
18
+MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEB4p47Ri7elhdsDnxCgNcaJn3Etff
19
+4mcsphRyZSkePVgEsf/VxG5tv0zWVJ/6m3uY3OB4bo0EeyFFSlRbRFOjmg==
20
+-----END PUBLIC KEY-----`
21
+)
22
+
23
+func CreateUserSig(Identifier, AppId3rd string) (userSig string, err error) {
24
+	conf := tencentsig.NewConf(ThisSDKAppId, Identifier, AppId3rd).WithExpire(86400)
25
+	userSig, err = conf.GenUserSig(ThisPriKey)
26
+
27
+	return
28
+}

+ 21 - 0
utils/tencentsig/LICENSE View File

@@ -0,0 +1,21 @@
1
+MIT License
2
+
3
+Copyright (c) 2018 yinheli
4
+
5
+Permission is hereby granted, free of charge, to any person obtaining a copy
6
+of this software and associated documentation files (the "Software"), to deal
7
+in the Software without restriction, including without limitation the rights
8
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+copies of the Software, and to permit persons to whom the Software is
10
+furnished to do so, subject to the following conditions:
11
+
12
+The above copyright notice and this permission notice shall be included in all
13
+copies or substantial portions of the Software.
14
+
15
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+SOFTWARE.

+ 12 - 0
utils/tencentsig/Makefile View File

@@ -0,0 +1,12 @@
1
+all: test
2
+
3
+.PHONY: fmt
4
+fmt:
5
+	@gofmt -s -l -w *.go
6
+
7
+.PHONY: test
8
+test:
9
+	go test -v -coverprofile .cover.out ./...
10
+	@go tool cover -func=.cover.out
11
+	@go tool cover -html=.cover.out -o .cover.html
12
+	@rm .cover.out

+ 15 - 0
utils/tencentsig/README.md View File

@@ -0,0 +1,15 @@
1
+# 腾讯登录服务(Tencent Login Service,TLS) userSig 生成
2
+
3
+> 这是纯go实现的版本,官方提供的是 cgo 版本,部分用户可能在 mac 等系统上会遇到编译失败的问题,故实现了这个版本
4
+
5
+
6
+支持的 curve 包括: prime256v1, secp256k1
7
+
8
+> 之前腾讯云只支持 secp256k1,后来测试发现有 prime256v1 的密钥
9
+
10
+具体使用请参考 `usersig_test.go`
11
+
12
+## 参考资料
13
+
14
+* https://cloud.tencent.com/document/product/269/1510
15
+* http://bbs.qcloud.com/thread-21826-1-1.html

+ 350 - 0
utils/tencentsig/bitelliptic.go View File

@@ -0,0 +1,350 @@
1
+package tencentsig
2
+
3
+// @see https://github.com/ThePiachu/Golang-Koblitz-elliptic-curve-DSA-library/blob/master/bitelliptic/bitelliptic.go
4
+
5
+import (
6
+	"crypto/elliptic"
7
+	"io"
8
+	"math/big"
9
+	"sync"
10
+)
11
+
12
+// A BitCurve represents a Koblitz Curve with a=0.
13
+// See http://www.hyperelliptic.org/EFD/g1p/auto-shortw.html
14
+type BitCurve struct {
15
+	P       *big.Int // the order of the underlying field
16
+	N       *big.Int // the order of the base point
17
+	B       *big.Int // the constant of the BitCurve equation
18
+	Gx, Gy  *big.Int // (x,y) of the base point
19
+	BitSize int      // the size of the underlying field
20
+}
21
+
22
+func (BitCurve *BitCurve) Params() *elliptic.CurveParams {
23
+	return &elliptic.CurveParams{P: BitCurve.P, N: BitCurve.N, B: BitCurve.B, Gx: BitCurve.Gx, Gy: BitCurve.Gy, BitSize: BitCurve.BitSize}
24
+}
25
+
26
+// IsOnBitCurve returns true if the given (x,y) lies on the BitCurve.
27
+func (BitCurve *BitCurve) IsOnCurve(x, y *big.Int) bool {
28
+	// y² = x³ + b
29
+	y2 := new(big.Int).Mul(y, y) //y²
30
+	y2.Mod(y2, BitCurve.P)       //y²%P
31
+
32
+	x3 := new(big.Int).Mul(x, x) //x²
33
+	x3.Mul(x3, x)                //x³
34
+
35
+	x3.Add(x3, BitCurve.B) //x³+B
36
+	x3.Mod(x3, BitCurve.P) //(x³+B)%P
37
+
38
+	return x3.Cmp(y2) == 0
39
+}
40
+
41
+//TODO: double check if the function is okay
42
+// affineFromJacobian reverses the Jacobian transform. See the comment at the
43
+// top of the file.
44
+func (BitCurve *BitCurve) affineFromJacobian(x, y, z *big.Int) (xOut, yOut *big.Int) {
45
+	zinv := new(big.Int).ModInverse(z, BitCurve.P)
46
+	zinvsq := new(big.Int).Mul(zinv, zinv)
47
+
48
+	xOut = new(big.Int).Mul(x, zinvsq)
49
+	xOut.Mod(xOut, BitCurve.P)
50
+	zinvsq.Mul(zinvsq, zinv)
51
+	yOut = new(big.Int).Mul(y, zinvsq)
52
+	yOut.Mod(yOut, BitCurve.P)
53
+	return
54
+}
55
+
56
+// Add returns the sum of (x1,y1) and (x2,y2)
57
+func (BitCurve *BitCurve) Add(x1, y1, x2, y2 *big.Int) (*big.Int, *big.Int) {
58
+	z := new(big.Int).SetInt64(1)
59
+	return BitCurve.affineFromJacobian(BitCurve.addJacobian(x1, y1, z, x2, y2, z))
60
+}
61
+
62
+// addJacobian takes two points in Jacobian coordinates, (x1, y1, z1) and
63
+// (x2, y2, z2) and returns their sum, also in Jacobian form.
64
+func (BitCurve *BitCurve) addJacobian(x1, y1, z1, x2, y2, z2 *big.Int) (*big.Int, *big.Int, *big.Int) {
65
+	// See http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#addition-add-2007-bl
66
+	z1z1 := new(big.Int).Mul(z1, z1)
67
+	z1z1.Mod(z1z1, BitCurve.P)
68
+	z2z2 := new(big.Int).Mul(z2, z2)
69
+	z2z2.Mod(z2z2, BitCurve.P)
70
+
71
+	u1 := new(big.Int).Mul(x1, z2z2)
72
+	u1.Mod(u1, BitCurve.P)
73
+	u2 := new(big.Int).Mul(x2, z1z1)
74
+	u2.Mod(u2, BitCurve.P)
75
+	h := new(big.Int).Sub(u2, u1)
76
+	if h.Sign() == -1 {
77
+		h.Add(h, BitCurve.P)
78
+	}
79
+	i := new(big.Int).Lsh(h, 1)
80
+	i.Mul(i, i)
81
+	j := new(big.Int).Mul(h, i)
82
+
83
+	s1 := new(big.Int).Mul(y1, z2)
84
+	s1.Mul(s1, z2z2)
85
+	s1.Mod(s1, BitCurve.P)
86
+	s2 := new(big.Int).Mul(y2, z1)
87
+	s2.Mul(s2, z1z1)
88
+	s2.Mod(s2, BitCurve.P)
89
+	r := new(big.Int).Sub(s2, s1)
90
+	if r.Sign() == -1 {
91
+		r.Add(r, BitCurve.P)
92
+	}
93
+	r.Lsh(r, 1)
94
+	v := new(big.Int).Mul(u1, i)
95
+
96
+	x3 := new(big.Int).Set(r)
97
+	x3.Mul(x3, x3)
98
+	x3.Sub(x3, j)
99
+	x3.Sub(x3, v)
100
+	x3.Sub(x3, v)
101
+	x3.Mod(x3, BitCurve.P)
102
+
103
+	y3 := new(big.Int).Set(r)
104
+	v.Sub(v, x3)
105
+	y3.Mul(y3, v)
106
+	s1.Mul(s1, j)
107
+	s1.Lsh(s1, 1)
108
+	y3.Sub(y3, s1)
109
+	y3.Mod(y3, BitCurve.P)
110
+
111
+	z3 := new(big.Int).Add(z1, z2)
112
+	z3.Mul(z3, z3)
113
+	z3.Sub(z3, z1z1)
114
+	if z3.Sign() == -1 {
115
+		z3.Add(z3, BitCurve.P)
116
+	}
117
+	z3.Sub(z3, z2z2)
118
+	if z3.Sign() == -1 {
119
+		z3.Add(z3, BitCurve.P)
120
+	}
121
+	z3.Mul(z3, h)
122
+	z3.Mod(z3, BitCurve.P)
123
+
124
+	return x3, y3, z3
125
+}
126
+
127
+// Double returns 2*(x,y)
128
+func (BitCurve *BitCurve) Double(x1, y1 *big.Int) (*big.Int, *big.Int) {
129
+	z1 := new(big.Int).SetInt64(1)
130
+	return BitCurve.affineFromJacobian(BitCurve.doubleJacobian(x1, y1, z1))
131
+}
132
+
133
+// doubleJacobian takes a point in Jacobian coordinates, (x, y, z), and
134
+// returns its double, also in Jacobian form.
135
+func (BitCurve *BitCurve) doubleJacobian(x, y, z *big.Int) (*big.Int, *big.Int, *big.Int) {
136
+	// See http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#doubling-dbl-2009-l
137
+
138
+	a := new(big.Int).Mul(x, x) //X1²
139
+	b := new(big.Int).Mul(y, y) //Y1²
140
+	c := new(big.Int).Mul(b, b) //B²
141
+
142
+	d := new(big.Int).Add(x, b) //X1+B
143
+	d.Mul(d, d)                 //(X1+B)²
144
+	d.Sub(d, a)                 //(X1+B)²-A
145
+	d.Sub(d, c)                 //(X1+B)²-A-C
146
+	d.Mul(d, big.NewInt(2))     //2*((X1+B)²-A-C)
147
+
148
+	e := new(big.Int).Mul(big.NewInt(3), a) //3*A
149
+	f := new(big.Int).Mul(e, e)             //E²
150
+
151
+	x3 := new(big.Int).Mul(big.NewInt(2), d) //2*D
152
+	x3.Sub(f, x3)                            //F-2*D
153
+	x3.Mod(x3, BitCurve.P)
154
+
155
+	y3 := new(big.Int).Sub(d, x3)                  //D-X3
156
+	y3.Mul(e, y3)                                  //E*(D-X3)
157
+	y3.Sub(y3, new(big.Int).Mul(big.NewInt(8), c)) //E*(D-X3)-8*C
158
+	y3.Mod(y3, BitCurve.P)
159
+
160
+	z3 := new(big.Int).Mul(y, z) //Y1*Z1
161
+	z3.Mul(big.NewInt(2), z3)    //3*Y1*Z1
162
+	z3.Mod(z3, BitCurve.P)
163
+
164
+	return x3, y3, z3
165
+}
166
+
167
+//TODO: double check if it is okay
168
+// ScalarMult returns k*(Bx,By) where k is a number in big-endian form.
169
+func (BitCurve *BitCurve) ScalarMult(Bx, By *big.Int, k []byte) (*big.Int, *big.Int) {
170
+	// We have a slight problem in that the identity of the group (the
171
+	// point at infinity) cannot be represented in (x, y) form on a finite
172
+	// machine. Thus the standard add/double algorithm has to be tweaked
173
+	// slightly: our initial state is not the identity, but x, and we
174
+	// ignore the first true bit in |k|.  If we don't find any true bits in
175
+	// |k|, then we return nil, nil, because we cannot return the identity
176
+	// element.
177
+
178
+	Bz := new(big.Int).SetInt64(1)
179
+	x := Bx
180
+	y := By
181
+	z := Bz
182
+
183
+	seenFirstTrue := false
184
+	for _, b := range k {
185
+		for bitNum := 0; bitNum < 8; bitNum++ {
186
+			if seenFirstTrue {
187
+				x, y, z = BitCurve.doubleJacobian(x, y, z)
188
+			}
189
+			if b&0x80 == 0x80 {
190
+				if !seenFirstTrue {
191
+					seenFirstTrue = true
192
+				} else {
193
+					x, y, z = BitCurve.addJacobian(Bx, By, Bz, x, y, z)
194
+				}
195
+			}
196
+			b <<= 1
197
+		}
198
+	}
199
+
200
+	if !seenFirstTrue {
201
+		return nil, nil
202
+	}
203
+
204
+	return BitCurve.affineFromJacobian(x, y, z)
205
+}
206
+
207
+// ScalarBaseMult returns k*G, where G is the base point of the group and k is
208
+// an integer in big-endian form.
209
+func (BitCurve *BitCurve) ScalarBaseMult(k []byte) (*big.Int, *big.Int) {
210
+	return BitCurve.ScalarMult(BitCurve.Gx, BitCurve.Gy, k)
211
+}
212
+
213
+var mask = []byte{0xff, 0x1, 0x3, 0x7, 0xf, 0x1f, 0x3f, 0x7f}
214
+
215
+//TODO: double check if it is okay
216
+// GenerateKey returns a public/private key pair. The private key is generated
217
+// using the given reader, which must return random data.
218
+func (BitCurve *BitCurve) GenerateKey(rand io.Reader) (priv []byte, x, y *big.Int, err error) {
219
+	byteLen := (BitCurve.BitSize + 7) >> 3
220
+	priv = make([]byte, byteLen)
221
+
222
+	for x == nil {
223
+		_, err = io.ReadFull(rand, priv)
224
+		if err != nil {
225
+			return
226
+		}
227
+		// We have to mask off any excess bits in the case that the size of the
228
+		// underlying field is not a whole number of bytes.
229
+		priv[0] &= mask[BitCurve.BitSize%8]
230
+		// This is because, in tests, rand will return all zeros and we don't
231
+		// want to get the point at infinity and loop forever.
232
+		priv[1] ^= 0x42
233
+		x, y = BitCurve.ScalarBaseMult(priv)
234
+	}
235
+	return
236
+}
237
+
238
+// Marshal converts a point into the form specified in section 4.3.6 of ANSI
239
+// X9.62.
240
+func (BitCurve *BitCurve) Marshal(x, y *big.Int) []byte {
241
+	byteLen := (BitCurve.BitSize + 7) >> 3
242
+
243
+	ret := make([]byte, 1+2*byteLen)
244
+	ret[0] = 4 // uncompressed point
245
+
246
+	xBytes := x.Bytes()
247
+	copy(ret[1+byteLen-len(xBytes):], xBytes)
248
+	yBytes := y.Bytes()
249
+	copy(ret[1+2*byteLen-len(yBytes):], yBytes)
250
+	return ret
251
+}
252
+
253
+// Unmarshal converts a point, serialised by Marshal, into an x, y pair. On
254
+// error, x = nil.
255
+func (BitCurve *BitCurve) Unmarshal(data []byte) (x, y *big.Int) {
256
+	byteLen := (BitCurve.BitSize + 7) >> 3
257
+	if len(data) != 1+2*byteLen {
258
+		return
259
+	}
260
+	if data[0] != 4 { // uncompressed form
261
+		return
262
+	}
263
+	x = new(big.Int).SetBytes(data[1 : 1+byteLen])
264
+	y = new(big.Int).SetBytes(data[1+byteLen:])
265
+	return
266
+}
267
+
268
+//curve parameters taken from:
269
+//http://www.secg.org/collateral/sec2_final.pdf
270
+
271
+var initonce sync.Once
272
+var secp160k1 *BitCurve
273
+var secp192k1 *BitCurve
274
+var secp224k1 *BitCurve
275
+var secp256k1 *BitCurve
276
+
277
+func initAll() {
278
+	initS160()
279
+	initS192()
280
+	initS224()
281
+	initS256()
282
+}
283
+
284
+func initS160() {
285
+	// See SEC 2 section 2.4.1
286
+	secp160k1 = new(BitCurve)
287
+	secp160k1.P, _ = new(big.Int).SetString("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFAC73", 16)
288
+	secp160k1.N, _ = new(big.Int).SetString("0100000000000000000001B8FA16DFAB9ACA16B6B3", 16)
289
+	secp160k1.B, _ = new(big.Int).SetString("0000000000000000000000000000000000000007", 16)
290
+	secp160k1.Gx, _ = new(big.Int).SetString("3B4C382CE37AA192A4019E763036F4F5DD4D7EBB", 16)
291
+	secp160k1.Gy, _ = new(big.Int).SetString("938CF935318FDCED6BC28286531733C3F03C4FEE", 16)
292
+	secp160k1.BitSize = 160
293
+}
294
+
295
+func initS192() {
296
+	// See SEC 2 section 2.5.1
297
+	secp192k1 = new(BitCurve)
298
+	secp192k1.P, _ = new(big.Int).SetString("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFEE37", 16)
299
+	secp192k1.N, _ = new(big.Int).SetString("FFFFFFFFFFFFFFFFFFFFFFFE26F2FC170F69466A74DEFD8D", 16)
300
+	secp192k1.B, _ = new(big.Int).SetString("000000000000000000000000000000000000000000000003", 16)
301
+	secp192k1.Gx, _ = new(big.Int).SetString("DB4FF10EC057E9AE26B07D0280B7F4341DA5D1B1EAE06C7D", 16)
302
+	secp192k1.Gy, _ = new(big.Int).SetString("9B2F2F6D9C5628A7844163D015BE86344082AA88D95E2F9D", 16)
303
+	secp192k1.BitSize = 192
304
+}
305
+
306
+func initS224() {
307
+	// See SEC 2 section 2.6.1
308
+	secp224k1 = new(BitCurve)
309
+	secp224k1.P, _ = new(big.Int).SetString("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFE56D", 16)
310
+	secp224k1.N, _ = new(big.Int).SetString("010000000000000000000000000001DCE8D2EC6184CAF0A971769FB1F7", 16)
311
+	secp224k1.B, _ = new(big.Int).SetString("00000000000000000000000000000000000000000000000000000005", 16)
312
+	secp224k1.Gx, _ = new(big.Int).SetString("A1455B334DF099DF30FC28A169A467E9E47075A90F7E650EB6B7A45C", 16)
313
+	secp224k1.Gy, _ = new(big.Int).SetString("7E089FED7FBA344282CAFBD6F7E319F7C0B0BD59E2CA4BDB556D61A5", 16)
314
+	secp224k1.BitSize = 224
315
+}
316
+
317
+func initS256() {
318
+	// See SEC 2 section 2.7.1
319
+	secp256k1 = new(BitCurve)
320
+	secp256k1.P, _ = new(big.Int).SetString("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F", 16)
321
+	secp256k1.N, _ = new(big.Int).SetString("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141", 16)
322
+	secp256k1.B, _ = new(big.Int).SetString("0000000000000000000000000000000000000000000000000000000000000007", 16)
323
+	secp256k1.Gx, _ = new(big.Int).SetString("79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798", 16)
324
+	secp256k1.Gy, _ = new(big.Int).SetString("483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8", 16)
325
+	secp256k1.BitSize = 256
326
+}
327
+
328
+// S160 returns a BitCurve which implements secp160k1 (see SEC 2 section 2.4.1)
329
+func S160() *BitCurve {
330
+	initonce.Do(initAll)
331
+	return secp160k1
332
+}
333
+
334
+// S192 returns a BitCurve which implements secp192k1 (see SEC 2 section 2.5.1)
335
+func S192() *BitCurve {
336
+	initonce.Do(initAll)
337
+	return secp192k1
338
+}
339
+
340
+// S224 returns a BitCurve which implements secp224k1 (see SEC 2 section 2.6.1)
341
+func S224() *BitCurve {
342
+	initonce.Do(initAll)
343
+	return secp224k1
344
+}
345
+
346
+// S256 returns a BitCurve which implements secp256k1 (see SEC 2 section 2.7.1)
347
+func S256() *BitCurve {
348
+	initonce.Do(initAll)
349
+	return secp256k1
350
+}

+ 8 - 0
utils/tencentsig/go.mod View File

@@ -0,0 +1,8 @@
1
+module github.com/yinheli/tencentsig
2
+
3
+require (
4
+	github.com/davecgh/go-spew v1.1.0
5
+	github.com/pmezard/go-difflib v1.0.0
6
+	github.com/stretchr/objx v0.1.0
7
+	github.com/stretchr/testify v1.2.1
8
+)

+ 7 - 0
utils/tencentsig/go.sum View File

@@ -0,0 +1,7 @@
1
+github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
2
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
3
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
4
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
5
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
6
+github.com/stretchr/testify v1.2.1 h1:52QO5WkIUcHGIR7EnGagH88x1bUzqGXTC5/1bDTUQ7U=
7
+github.com/stretchr/testify v1.2.1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=

+ 31 - 0
utils/tencentsig/parse.go View File

@@ -0,0 +1,31 @@
1
+package tencentsig
2
+
3
+import (
4
+	"crypto/x509/pkix"
5
+	"encoding/asn1"
6
+)
7
+
8
+var (
9
+	oidPublicKeyECDSA = asn1.ObjectIdentifier{1, 2, 840, 10045, 2, 1}
10
+	oidNamedCurveS256 = asn1.ObjectIdentifier{1, 3, 132, 0, 10}
11
+)
12
+
13
+type pkcs8 struct {
14
+	Version    int
15
+	Algo       pkix.AlgorithmIdentifier
16
+	PrivateKey []byte
17
+	// optional attributes omitted.
18
+}
19
+
20
+type publicKeyInfo struct {
21
+	Raw       asn1.RawContent
22
+	Algorithm pkix.AlgorithmIdentifier
23
+	PublicKey asn1.BitString
24
+}
25
+
26
+type ecPrivateKey struct {
27
+	Version       int
28
+	PrivateKey    []byte
29
+	NamedCurveOID asn1.ObjectIdentifier `asn1:"optional,explicit,tag:0"`
30
+	PublicKey     asn1.BitString        `asn1:"optional,explicit,tag:1"`
31
+}

+ 256 - 0
utils/tencentsig/usersig.go View File

@@ -0,0 +1,256 @@
1
+package tencentsig
2
+
3
+import (
4
+	"bytes"
5
+	"compress/zlib"
6
+	"crypto"
7
+	"crypto/ecdsa"
8
+	"crypto/elliptic"
9
+	"crypto/rand"
10
+	"crypto/sha256"
11
+	"crypto/x509"
12
+	"encoding/asn1"
13
+	"encoding/base64"
14
+	"encoding/hex"
15
+	"encoding/json"
16
+	"encoding/pem"
17
+	"fmt"
18
+	"io/ioutil"
19
+	"math/big"
20
+	"strings"
21
+	"time"
22
+)
23
+
24
+const (
25
+	accountType   = "29296"
26
+	version       = "201512300000"
27
+	defaultExpire = 3600 * 24 * 180
28
+)
29
+
30
+var (
31
+	tlsReplace = map[string]string{
32
+		"+": "*",
33
+		"/": "-",
34
+		"=": "_",
35
+	}
36
+)
37
+
38
+type Conf struct {
39
+	AccountType string `json:"TLS.account_type"`
40
+	Identifier  string `json:"TLS.identifier"`
41
+	AppidAt3rd  string `json:"TLS.appid_at_3rd"`
42
+	SdkAppid    string `json:"TLS.sdk_appid"`
43
+	ExpireAfter string `json:"TLS.expire_after"`
44
+	Version     string `json:"TLS.version"`
45
+	Time        string `json:"TLS.time"`
46
+	Sig         string `json:"TLS.sig"`
47
+}
48
+
49
+func NewConf(sdkAppId string, identifier string, appidAt3rd string) *Conf {
50
+	return &Conf{
51
+		AccountType: accountType,
52
+		Identifier:  identifier,
53
+		AppidAt3rd:  appidAt3rd,
54
+		SdkAppid:    sdkAppId,
55
+		ExpireAfter: fmt.Sprintf("%d", defaultExpire),
56
+		Version:     version,
57
+		Time:        fmt.Sprintf("%d", time.Now().Unix()),
58
+	}
59
+}
60
+
61
+func (c *Conf) WithExpire(expireInSeconds int) *Conf {
62
+	c.ExpireAfter = fmt.Sprintf("%d", expireInSeconds)
63
+	return c
64
+}
65
+
66
+func (c *Conf) GenUserSig(pemPrivateKey string) (string, error) {
67
+	var err error
68
+	c.Sig, err = c.sign(pemPrivateKey)
69
+	if err != nil {
70
+		return "", err
71
+	}
72
+	data, _ := json.Marshal(c)
73
+
74
+	var b bytes.Buffer
75
+	z := zlib.NewWriter(&b)
76
+	z.Write(data)
77
+	z.Close()
78
+
79
+	return base64Encode(b.Bytes()), nil
80
+}
81
+
82
+func VerifyUserSig(pemPublicKey string, userSig string) (*Conf, bool, error) {
83
+	data, err := base64Decode(userSig)
84
+	if err != nil {
85
+		return nil, false, err
86
+	}
87
+	reader, err := zlib.NewReader(bytes.NewReader(data))
88
+	if err != nil {
89
+		return nil, false, err
90
+	}
91
+
92
+	data, err = ioutil.ReadAll(reader)
93
+	if err != nil {
94
+		return nil, false, err
95
+	}
96
+
97
+	var conf Conf
98
+	err = json.Unmarshal(data, &conf)
99
+	if err != nil {
100
+		return nil, false, err
101
+	}
102
+
103
+	block, _ := pem.Decode([]byte(pemPublicKey))
104
+
105
+	pk, err := x509.ParsePKIXPublicKey(block.Bytes)
106
+	if err != nil {
107
+		if strings.Contains(err.Error(), "unsupported elliptic curve") {
108
+			var pki publicKeyInfo
109
+			if _, err := asn1.Unmarshal(block.Bytes, &pki); err != nil {
110
+				return nil, false, err
111
+			}
112
+
113
+			asn1Data := pki.PublicKey.RightAlign()
114
+			fmt.Println(hex.EncodeToString(asn1Data))
115
+			paramsData := pki.Algorithm.Parameters.FullBytes
116
+			namedCurveOID := new(asn1.ObjectIdentifier)
117
+			_, err = asn1.Unmarshal(paramsData, namedCurveOID)
118
+			if err != nil {
119
+				return nil, false, err
120
+			}
121
+
122
+			if namedCurveOID.Equal(oidNamedCurveS256) {
123
+				pubk := new(ecdsa.PublicKey)
124
+				pubk.Curve = S256()
125
+				pubk.X, pubk.Y = elliptic.Unmarshal(pubk.Curve, asn1Data)
126
+				pk = pubk
127
+			}
128
+		} else {
129
+			return nil, false, err
130
+		}
131
+	}
132
+
133
+	pubKey := pk.(*ecdsa.PublicKey)
134
+
135
+	content := conf.signContent()
136
+	hashed := sha256.Sum256([]byte(content))
137
+
138
+	signature, _ := base64.StdEncoding.DecodeString(conf.Sig)
139
+	r, s, err := pointsFromDER(signature)
140
+	if err != nil {
141
+		return nil, false, err
142
+	}
143
+
144
+	res := ecdsa.Verify(pubKey, hashed[:], r, s)
145
+	return &conf, res, nil
146
+}
147
+
148
+func (c *Conf) signContent() string {
149
+	var builder strings.Builder
150
+
151
+	builder.WriteString("TLS.appid_at_3rd:")
152
+	builder.WriteString(c.AppidAt3rd)
153
+	builder.WriteString("\n")
154
+
155
+	builder.WriteString("TLS.account_type:")
156
+	builder.WriteString(c.AccountType)
157
+	builder.WriteString("\n")
158
+
159
+	builder.WriteString("TLS.identifier:")
160
+	builder.WriteString(c.Identifier)
161
+	builder.WriteString("\n")
162
+
163
+	builder.WriteString("TLS.sdk_appid:")
164
+	builder.WriteString(c.SdkAppid)
165
+	builder.WriteString("\n")
166
+
167
+	builder.WriteString("TLS.time:")
168
+	builder.WriteString(c.Time)
169
+	builder.WriteString("\n")
170
+
171
+	builder.WriteString("TLS.expire_after:")
172
+	builder.WriteString(c.ExpireAfter)
173
+	builder.WriteString("\n")
174
+
175
+	return builder.String()
176
+}
177
+
178
+func (c *Conf) sign(privateKey string) (string, error) {
179
+	block, _ := pem.Decode([]byte(privateKey))
180
+
181
+	pk, err := x509.ParsePKCS8PrivateKey(block.Bytes)
182
+	if err != nil {
183
+		if strings.Contains(err.Error(), "unknown elliptic curve") {
184
+			var privKey pkcs8
185
+			if _, err := asn1.Unmarshal(block.Bytes, &privKey); err != nil {
186
+				return "", err
187
+			}
188
+
189
+			if privKey.Algo.Algorithm.Equal(oidPublicKeyECDSA) {
190
+				namedCurveOID := new(asn1.ObjectIdentifier)
191
+				asn1.Unmarshal(privKey.Algo.Parameters.FullBytes, namedCurveOID)
192
+				if namedCurveOID.Equal(oidNamedCurveS256) {
193
+					var ecPrivKey ecPrivateKey
194
+					asn1.Unmarshal(privKey.PrivateKey, &ecPrivKey)
195
+
196
+					k := new(ecdsa.PrivateKey)
197
+					k.Curve = S256()
198
+					d := new(big.Int)
199
+					d.SetBytes(ecPrivKey.PrivateKey)
200
+					k.D = d
201
+					k.X, k.Y = S256().ScalarBaseMult(d.Bytes())
202
+					pk = k
203
+				}
204
+			}
205
+		} else {
206
+			return "", err
207
+		}
208
+	}
209
+
210
+	priv := pk.(*ecdsa.PrivateKey)
211
+
212
+	content := c.signContent()
213
+
214
+	hashed := sha256.Sum256([]byte(content))
215
+
216
+	sig, err := priv.Sign(rand.Reader, hashed[:], crypto.SHA256)
217
+	if err != nil {
218
+		return "", err
219
+	}
220
+	return base64.StdEncoding.EncodeToString(sig), nil
221
+}
222
+
223
+func base64Encode(data []byte) string {
224
+	res := base64.StdEncoding.EncodeToString(data)
225
+	for k, v := range tlsReplace {
226
+		res = strings.Replace(res, k, v, -1)
227
+	}
228
+	return res
229
+}
230
+
231
+func base64Decode(data string) ([]byte, error) {
232
+	for k, v := range tlsReplace {
233
+		data = strings.Replace(data, v, k, -1)
234
+	}
235
+	return base64.StdEncoding.DecodeString(data)
236
+}
237
+
238
+func pointsFromDER(der []byte) (R, S *big.Int, err error) {
239
+	R, S = &big.Int{}, &big.Int{}
240
+
241
+	data := asn1.RawValue{}
242
+	if _, err = asn1.Unmarshal(der, &data); err != nil {
243
+		return
244
+	}
245
+
246
+	// The format of our DER string is 0x02 + rlen + r + 0x02 + slen + s
247
+	rLen := data.Bytes[1] // The entire length of R + offset of 2 for 0x02 and rlen
248
+	r := data.Bytes[2 : rLen+2]
249
+	// Ignore the next 0x02 and slen bytes and just take the start of S to the end of the byte array
250
+	s := data.Bytes[rLen+4:]
251
+
252
+	R.SetBytes(r)
253
+	S.SetBytes(s)
254
+
255
+	return
256
+}

+ 97 - 0
utils/tencentsig/usersig_test.go View File

@@ -0,0 +1,97 @@
1
+package tencentsig
2
+
3
+import (
4
+	"encoding/json"
5
+	"github.com/stretchr/testify/assert"
6
+	"log"
7
+	"os"
8
+	"strings"
9
+	"testing"
10
+)
11
+
12
+var (
13
+	sdkAppId   = "1234567890"
14
+	identifier = "zhangshan"
15
+
16
+	l = log.New(os.Stdout, "[tencentsig - test] ", log.LstdFlags)
17
+)
18
+
19
+func doVerifyUserSig(t *testing.T, pemPrivateKey, pemPublicKey string) {
20
+	conf := NewConf(sdkAppId, identifier).WithExpire(defaultExpire)
21
+	userSig, err := conf.GenUserSig(pemPrivateKey)
22
+	assert.Nil(t, err)
23
+
24
+	l.Print("userSig:", userSig)
25
+
26
+	c, valid, err := VerifyUserSig(pemPublicKey, userSig)
27
+
28
+	assert.Nil(t, err)
29
+	assert.True(t, valid)
30
+
31
+	b, _ := json.MarshalIndent(c, "", "  ")
32
+	l.Print("conf:", string(b))
33
+}
34
+
35
+// 准备
36
+
37
+/*
38
+
39
+# list curves
40
+openssl ecparam -list_curves
41
+
42
+
43
+# prime256v1 curve
44
+
45
+openssl ecparam -name prime256v1 -genkey -noout -out private.pem
46
+openssl pkcs8 -topk8 -nocrypt -in private.pem -out private-pkcs8.pem && cat private-pkcs8.pem
47
+openssl ec -in private.pem -pubout
48
+
49
+
50
+# secp256k1 curve
51
+
52
+openssl ecparam -name secp256k1 -genkey -noout -out private.pem
53
+openssl pkcs8 -topk8 -nocrypt -in private.pem -out private-pkcs8.pem && cat private-pkcs8.pem
54
+openssl ec -in private.pem -pubout
55
+*/
56
+
57
+func TestVerifyUserSig(t *testing.T) {
58
+	// prime256v1
59
+	doVerifyUserSig(
60
+		t,
61
+
62
+		strings.TrimSpace(`
63
+-----BEGIN PRIVATE KEY-----
64
+MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgJzqd3dF+O6vd+bGJ
65
+7tGA7TLsWNzbYBKRGELEA65ywQahRANCAATIBFu6F5SlqrPFkuhi46IRXXKyEiuU
66
+g8pP+n3L5ZSiW3o0N58P0Ix77PrRVSXLfHd5VqeyF2CWWDUQZyA/butY
67
+-----END PRIVATE KEY-----
68
+		`),
69
+
70
+		strings.TrimSpace(`
71
+-----BEGIN PUBLIC KEY-----
72
+MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEyARbuheUpaqzxZLoYuOiEV1yshIr
73
+lIPKT/p9y+WUolt6NDefD9CMe+z60VUly3x3eVanshdgllg1EGcgP27rWA==
74
+-----END PUBLIC KEY-----
75
+		`),
76
+	)
77
+
78
+	// secp256k1
79
+	doVerifyUserSig(
80
+		t,
81
+
82
+		strings.TrimSpace(`
83
+-----BEGIN PRIVATE KEY-----
84
+MIGEAgEAMBAGByqGSM49AgEGBSuBBAAKBG0wawIBAQQgkRrBHsxAXy4ssvSYsJIM
85
+TUzzLIHOeUQ/QKygM3JhvDahRANCAATyucyxciWHFclVxRPW7zJ6d51F5au6xnZk
86
+bjkiDOpa6gl8JhdeWcKLYgRb5raHNq/JYUYJSrsH29whxdx0lpq7
87
+-----END PRIVATE KEY-----
88
+		`),
89
+
90
+		strings.TrimSpace(`
91
+-----BEGIN PUBLIC KEY-----
92
+MFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAE8rnMsXIlhxXJVcUT1u8yenedReWrusZ2
93
+ZG45IgzqWuoJfCYXXlnCi2IEW+a2hzavyWFGCUq7B9vcIcXcdJaauw==
94
+-----END PUBLIC KEY-----
95
+		`),
96
+	)
97
+}