Skip to content

Commit 9a8869c

Browse files
author
石昌琦
committed
feat: add v2 support
1 parent b5cdc0b commit 9a8869c

File tree

6 files changed

+24
-211
lines changed

6 files changed

+24
-211
lines changed

README.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44

55
访问验证码服务,创建验证码场景并获取配置信息
66

7-
[国内应用地址](https://rivers.chaitin.cn/?rc=KYWFRHCKNYWJ7VAZQZSDNJUOAUSXZ4XB&app_scope=scaptcha)
7+
[v1应用地址](https://rivers.chaitin.cn/?rc=KYWFRHCKNYWJ7VAZQZSDNJUOAUSXZ4XB&app_scope=scaptcha)
8+
[v2应用地址](https://captcha.app.safepoint.cloud/)
89

910
### 业务接入
1011
[前端业务接入 demo](https://github.com/chaitin/scaptcha-sdk-golang/-/blob/main/demo.html)

cmd/demo/demo.go

+2-5
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,8 @@ import (
77
)
88

99
func main() {
10-
publicKeyStr := `-----BEGIN PUBLIC KEY-----
11-
***
12-
-----END PUBLIC KEY-----
13-
`
14-
verifyJWTokenken := "eyJhbGciOiJSUzM4NCIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3MzU4MTQ5MzMsImlhdCI6MTczNTgxNDYzMywiaXNzIjoiY2hhaXRpbi9zLWNhcHRjaGEiLCJ2aWQiOiI3N2M4M2E2MjNmZDQxMmJlYjczMDYxZDA3MjgzOWIzYyJ9.lna1PIMJ1zM6vwytnEn_6TEjkMb7-ycVRjYRnbDqqVNcjc35OYZ-dpNDPaMOtL7UJPhu7FHNbOV7BjnrGv-XAU_qHQcdTF7jCjV2J8rOQWSyF8htQ5d1Cvm0R2k1A_zsEYmCfAP8S7Dd_kFyShxUfSPmtIbSk8le1VOa3hfxgsBV8QtwxIZDD5l2TjCprYTbLv6vTu7PFZS5cMV68EZ1PvyuJzu9VEUEkhnSjh859mZLOUOQfO5d6M1oAFoBvRKLTLLvd5GGGmUkto40IKW7Gjh5jFEpaKNUX9GBpUMrqWz5fpwNK08oMQEqOdIdr2nfpsSxuZIiK-2QC9X6rlZwPw"
10+
publicKeyStr := `publicKeyStr`
11+
verifyJWTokenken := "this-is-a-token"
1512
// 创建验证器
1613
verifier, err := verify.NewTokenVerifier(publicKeyStr)
1714
if err != nil {

go.mod

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@ module github.com/chaitin/scaptcha-sdk-golang
22

33
go 1.23.3
44

5-
require github.com/golang-jwt/jwt/v5 v5.2.1
5+
require github.com/golang-jwt/jwt v3.2.2+incompatible

go.sum

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk=
2-
github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
1+
github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
2+
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=

utils/rsa.go

+8-195
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
11
package utils
22

33
import (
4-
"bytes"
5-
"crypto"
6-
"crypto/rand"
74
"crypto/rsa"
85
"crypto/x509"
6+
"encoding/base64"
97
"encoding/pem"
108
"errors"
119
"strings"
@@ -20,10 +18,6 @@ const (
2018

2119
kPKCS8Prefix = "-----BEGIN PRIVATE KEY-----"
2220
KPKCS8Suffix = "-----END PRIVATE KEY-----"
23-
24-
kPublicKeyType = "PUBLIC KEY"
25-
kPrivateKeyType = "PRIVATE KEY"
26-
kRSAPrivateKeyType = "RSA PRIVATE KEY"
2721
)
2822

2923
var (
@@ -32,6 +26,10 @@ var (
3226
)
3327

3428
func FormatPublicKey(raw string) []byte {
29+
newRaw, err := base64.StdEncoding.DecodeString(raw)
30+
if err == nil {
31+
raw = string(newRaw)
32+
}
3533
return formatKey(raw, kPublicKeyPrefix, kPublicKeySuffix, 64)
3634
}
3735

@@ -75,209 +73,24 @@ func ParsePKCS8PrivateKey(data []byte) (key *rsa.PrivateKey, err error) {
7573
}
7674

7775
key, ok := rawKey.(*rsa.PrivateKey)
78-
if ok == false {
76+
if !ok {
7977
return nil, ErrPrivateKeyFailedToLoad
8078
}
8179

8280
return key, err
8381
}
8482

85-
func ParsePublicKey(data []byte) (key *rsa.PublicKey, err error) {
83+
func ParsePublicKey(data []byte) (key any, err error) {
8684
var block *pem.Block
8785
block, _ = pem.Decode(data)
8886
if block == nil {
8987
return nil, ErrPublicKeyFailedToLoad
9088
}
9189

92-
var pubInterface interface{}
93-
pubInterface, err = x509.ParsePKIXPublicKey(block.Bytes)
90+
key, err = x509.ParsePKIXPublicKey(block.Bytes)
9491
if err != nil {
9592
return nil, err
9693
}
97-
key, ok := pubInterface.(*rsa.PublicKey)
98-
if !ok {
99-
return nil, ErrPublicKeyFailedToLoad
100-
}
10194

10295
return key, err
10396
}
104-
105-
func packageData(data []byte, packageSize int) (r [][]byte) {
106-
src := make([]byte, len(data))
107-
copy(src, data)
108-
109-
r = make([][]byte, 0)
110-
if len(src) <= packageSize {
111-
return append(r, src)
112-
}
113-
for len(src) > 0 {
114-
p := src[:packageSize]
115-
r = append(r, p)
116-
src = src[packageSize:]
117-
if len(src) <= packageSize {
118-
r = append(r, src)
119-
break
120-
}
121-
}
122-
return r
123-
}
124-
125-
// RSAEncrypt 使用公钥 key 对数据 data 进行 RSA 加密
126-
func RSAEncrypt(plaintext, key []byte) ([]byte, error) {
127-
pubKey, err := ParsePublicKey(key)
128-
if err != nil {
129-
return nil, err
130-
}
131-
132-
return RSAEncryptWithKey(plaintext, pubKey)
133-
}
134-
135-
// RSAEncryptWithKey 使用公钥 key 对数据 data 进行 RSA 加密
136-
func RSAEncryptWithKey(plaintext []byte, key *rsa.PublicKey) ([]byte, error) {
137-
pData := packageData(plaintext, key.N.BitLen()/8-11)
138-
ciphertext := make([]byte, 0, 0)
139-
140-
for _, d := range pData {
141-
c, e := rsa.EncryptPKCS1v15(rand.Reader, key, d)
142-
if e != nil {
143-
return nil, e
144-
}
145-
ciphertext = append(ciphertext, c...)
146-
}
147-
148-
return ciphertext, nil
149-
}
150-
151-
// RSADecryptWithPKCS1 使用私钥 key 对数据 data 进行 RSA 解密,key 的格式为 pkcs1
152-
func RSADecryptWithPKCS1(ciphertext, key []byte) ([]byte, error) {
153-
priKey, err := ParsePKCS1PrivateKey(key)
154-
if err != nil {
155-
return nil, err
156-
}
157-
158-
return RSADecryptWithKey(ciphertext, priKey)
159-
}
160-
161-
// RSADecryptWithPKCS8 使用私钥 key 对数据 data 进行 RSA 解密,key 的格式为 pkcs8
162-
func RSADecryptWithPKCS8(ciphertext, key []byte) ([]byte, error) {
163-
priKey, err := ParsePKCS8PrivateKey(key)
164-
if err != nil {
165-
return nil, err
166-
}
167-
168-
return RSADecryptWithKey(ciphertext, priKey)
169-
}
170-
171-
// RSADecryptWithKey 使用私钥 key 对数据 data 进行 RSA 解密
172-
func RSADecryptWithKey(ciphertext []byte, key *rsa.PrivateKey) ([]byte, error) {
173-
pData := packageData(ciphertext, key.PublicKey.N.BitLen()/8)
174-
plaintext := make([]byte, 0, 0)
175-
176-
for _, d := range pData {
177-
p, e := rsa.DecryptPKCS1v15(rand.Reader, key, d)
178-
if e != nil {
179-
return nil, e
180-
}
181-
plaintext = append(plaintext, p...)
182-
}
183-
return plaintext, nil
184-
}
185-
186-
func RSASignWithPKCS1(plaintext, key []byte, hash crypto.Hash) ([]byte, error) {
187-
priKey, err := ParsePKCS1PrivateKey(key)
188-
if err != nil {
189-
return nil, err
190-
}
191-
return RSASignWithKey(plaintext, priKey, hash)
192-
}
193-
194-
func RSASignWithPKCS8(plaintext, key []byte, hash crypto.Hash) ([]byte, error) {
195-
priKey, err := ParsePKCS8PrivateKey(key)
196-
if err != nil {
197-
return nil, err
198-
}
199-
return RSASignWithKey(plaintext, priKey, hash)
200-
}
201-
202-
func RSASignWithKey(plaintext []byte, key *rsa.PrivateKey, hash crypto.Hash) ([]byte, error) {
203-
h := hash.New()
204-
h.Write(plaintext)
205-
hashed := h.Sum(nil)
206-
return rsa.SignPKCS1v15(rand.Reader, key, hash, hashed)
207-
}
208-
209-
func RSAVerify(ciphertext, sign, key []byte, hash crypto.Hash) error {
210-
pubKey, err := ParsePublicKey(key)
211-
if err != nil {
212-
return err
213-
}
214-
return RSAVerifyWithKey(ciphertext, sign, pubKey, hash)
215-
}
216-
217-
func RSAVerifyWithKey(ciphertext, sign []byte, key *rsa.PublicKey, hash crypto.Hash) error {
218-
h := hash.New()
219-
h.Write(ciphertext)
220-
hashed := h.Sum(nil)
221-
return rsa.VerifyPKCS1v15(key, hash, hashed, sign)
222-
}
223-
224-
func getPublicKeyBytes(publicKey *rsa.PublicKey) ([]byte, error) {
225-
pubDer, err := x509.MarshalPKIXPublicKey(publicKey)
226-
if err != nil {
227-
return nil, err
228-
}
229-
230-
pubBlock := &pem.Block{Type: kPublicKeyType, Bytes: pubDer}
231-
232-
var pubBuf bytes.Buffer
233-
if err = pem.Encode(&pubBuf, pubBlock); err != nil {
234-
return nil, err
235-
}
236-
return pubBuf.Bytes(), nil
237-
}
238-
239-
func GenRSAKeyWithPKCS1(bits int) (privateKey, publicKey []byte, err error) {
240-
priKey, err := rsa.GenerateKey(rand.Reader, bits)
241-
if err != nil {
242-
return nil, nil, err
243-
}
244-
priDer := x509.MarshalPKCS1PrivateKey(priKey)
245-
priBlock := &pem.Block{Type: kRSAPrivateKeyType, Bytes: priDer}
246-
247-
var priBuf bytes.Buffer
248-
if err = pem.Encode(&priBuf, priBlock); err != nil {
249-
return nil, nil, err
250-
}
251-
252-
publicKey, err = getPublicKeyBytes(&priKey.PublicKey)
253-
if err != nil {
254-
return nil, nil, err
255-
}
256-
privateKey = priBuf.Bytes()
257-
return privateKey, publicKey, err
258-
}
259-
260-
func GenRSAKeyWithPKCS8(bits int) (privateKey, publicKey []byte, err error) {
261-
priKey, err := rsa.GenerateKey(rand.Reader, bits)
262-
if err != nil {
263-
return nil, nil, err
264-
}
265-
priDer, err := x509.MarshalPKCS8PrivateKey(priKey)
266-
if err != nil {
267-
return nil, nil, err
268-
}
269-
priBlock := &pem.Block{Type: kPrivateKeyType, Bytes: priDer}
270-
271-
var priBuf bytes.Buffer
272-
if err = pem.Encode(&priBuf, priBlock); err != nil {
273-
return nil, nil, err
274-
}
275-
276-
publicKey, err = getPublicKeyBytes(&priKey.PublicKey)
277-
if err != nil {
278-
return nil, nil, err
279-
}
280-
privateKey = priBuf.Bytes()
281-
282-
return privateKey, publicKey, err
283-
}

verify.go

+9-7
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
11
package verify
22

33
import (
4-
"crypto/rsa"
54
"fmt"
65
"sync"
76
"time"
87

98
"github.com/chaitin/scaptcha-sdk-golang/utils"
10-
"github.com/golang-jwt/jwt/v5"
9+
"github.com/golang-jwt/jwt"
1110
)
1211

1312
// 可能需要返回给用户业务的数据,v-id、score等
@@ -17,7 +16,8 @@ type VerifyClaims struct {
1716

1817
// TokenVerifier 处理 JWT token 的验证和防重放
1918
type TokenVerifier struct {
20-
publicKey *rsa.PublicKey
19+
publicKey any
20+
// publicKey *rsa.PublicKey
2121
// 使用 sync.Map 替代普通 map,专门用于并发场景
2222
usedTokens sync.Map
2323
// 清理间隔
@@ -74,9 +74,6 @@ func (v *TokenVerifier) Stop() {
7474
// VerifyToken 验证 token 并防止重放攻击
7575
func (v *TokenVerifier) VerifyToken(tokenString string) (bool, VerifyClaims, error) {
7676
token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
77-
if _, ok := token.Method.(*jwt.SigningMethodRSA); !ok {
78-
return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
79-
}
8077
return v.publicKey, nil
8178
})
8279
if err != nil {
@@ -89,7 +86,12 @@ func (v *TokenVerifier) VerifyToken(tokenString string) (bool, VerifyClaims, err
8986
}
9087

9188
// 验证过期时间
92-
exp, err := claims.GetExpirationTime()
89+
expClaim, ok := claims["exp"].(float64)
90+
if !ok {
91+
err = fmt.Errorf("exp claim not found or invalid type")
92+
return false, VerifyClaims{}, err
93+
}
94+
exp := time.Unix(int64(expClaim), 0)
9395
if err != nil {
9496
return false, VerifyClaims{}, fmt.Errorf("exp not found")
9597
}

0 commit comments

Comments
 (0)