-
Notifications
You must be signed in to change notification settings - Fork 4
/
utilSignature.go
441 lines (380 loc) · 12.9 KB
/
utilSignature.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
// Copyright (c) 2023 gpress Authors.
//
// This file is part of gpress.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package main
import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/sha256"
"encoding/hex"
"errors"
"fmt"
dcrdEcdsa "github.com/decred/dcrd/dcrec/secp256k1/v4/ecdsa"
"golang.org/x/crypto/ripemd160"
"golang.org/x/crypto/sha3"
"math/big"
"strings"
)
func verifySecp256k1Signature(senderAddress string, signatureData string, signature string) (bool, error) {
signatureBytes, err := fromHex(signature)
if err != nil {
return false, err
}
if len(signatureBytes) < 65 {
return false, errors.New("invalid signature")
}
// 计算消息的哈希,包括 MetaMask 的消息前缀
prefix := fmt.Sprintf("\x19Ethereum Signed Message:\n%d%s",
len(signatureData), signatureData)
messageBytes := []byte(prefix)
messageHash := keccak256Hash(messageBytes)
r, s, v := signatureBytes[:32], signatureBytes[32:64], signatureBytes[64]
sig, err := hex.DecodeString(fmt.Sprintf("%x%x%x", v, r, s))
if err != nil {
return false, err
}
hash, err := hex.DecodeString(fmt.Sprintf("%x", messageHash))
if err != nil {
return false, err
}
dcrdPublicKey, _, err := dcrdEcdsa.RecoverCompact(sig, hash)
if err != nil {
return false, err
}
pubKeyBytes := dcrdPublicKey.SerializeUncompressed()[1:]
pubKeyHash := keccak256Hash(pubKeyBytes)
address := ""
if len(pubKeyHash) > 20 {
address = fmt.Sprintf("0x%x", pubKeyHash[len(pubKeyHash)-20:])
}
return strings.ToLower(address) == strings.ToLower(senderAddress), nil
}
func fromHex(s string) ([]byte, error) {
if len(s) >= 2 && s[0] == '0' && (s[1] == 'x' || s[1] == 'X') {
s = s[2:]
}
if len(s)%2 == 1 {
s = "0" + s
}
return hex.DecodeString(s)
}
func keccak256Hash(data []byte) []byte {
d := sha3.NewLegacyKeccak256()
d.Write(data)
return d.Sum(nil)
}
// XuperChain使用NIST标准的公钥
func verifyXuperSignature(chainAddress string, sig, msg []byte) (valid bool, err error) {
/*k := &ecdsa.PublicKey{}
err = json.Unmarshal([]byte(chainAddress), k)
if err != nil {
return false, err //json有问题
}
k.Curve = elliptic.P256()
// 判断是否是NIST标准的公钥
isNistCurve := checkKeyCurve(k)
if isNistCurve == false {
return false, fmt.Errorf("this cryptography curve[%s] has not been supported yet.", k.Params().Name)
}
r, s, err := unmarshalECDSASignature(sig)
if err != nil {
return false, fmt.Errorf("failed to unmarshal the ecdsa signature [%s]", err)
}
return ecdsa.Verify(k, msg, r, s), nil*/
s2 := string(sig)
signature, e := hex.DecodeString(s2)
if e != nil {
return false, err
}
r := new(big.Int).SetBytes(signature[:32])
s := new(big.Int).SetBytes(signature[32:64])
publicKeyX := new(big.Int).SetBytes(signature[64:96])
publicKeyY := new(big.Int).SetBytes(signature[96:128])
data := signature[128:]
if string(msg) != string(data) { //数据不一致
return false, errors.New("原始数据不一致")
}
/*x := new(big.Int)
y := new(big.Int)
e = x.UnmarshalText(publicKeyX)
if e != nil {
return false, e
}
e = y.UnmarshalText(publicKeyY)
if e != nil {
return false, e
}*/
pub := ecdsa.PublicKey{Curve: elliptic.P256(), X: publicKeyX, Y: publicKeyY}
pubKey := &pub
verifyAddress, _ := verifyAddressUsingPublicKey(chainAddress, pubKey)
if !verifyAddress {
return false, errors.New("签名中的公钥和address不匹配")
}
return ecdsa.Verify(pubKey, data, r, s), nil
}
// 判断是否是NIST标准的公钥
func checkKeyCurve(k *ecdsa.PublicKey) bool {
if k.X == nil || k.Y == nil {
return false
}
switch k.Params().Name {
case "P-256": // NIST
return true
default: // 不支持的密码学类型
return false
}
}
type ECDSASignature struct {
R, S *big.Int
}
/*
// hashSha256 使用sha256计算hash值
func hashSha256(str string) string {
hashByte := sha256.Sum256([]byte(str))
hashStr := hex.EncodeToString(hashByte[:])
return hashStr
}
// use DER-encoded ASN.1 octet standard to represent the signature
// 与比特币算法一样,基于DER-encoded ASN.1 octet标准,来表达使用椭圆曲线签名算法返回的结果
func MarshalECDSASignature(r, s *big.Int) ([]byte, error) {
return asn1.Marshal(ECDSASignature{r, s})
}
// 将公钥序列化成byte数组
func MarshalPublicKey(publicKey *ecdsa.PublicKey) []byte {
return elliptic.Marshal(publicKey.Curve, publicKey.X, publicKey.Y)
}
func unmarshalECDSASignature(rawSig []byte) (*big.Int, *big.Int, error) {
sig := new(ECDSASignature)
_, err := asn1.Unmarshal(rawSig, sig)
if err != nil {
return nil, nil, fmt.Errorf("failed to unmashal the signature [%v] to R & S, and the error is [%s]", rawSig, err)
}
if sig.R == nil {
return nil, nil, errors.New("invalid signature, R is nil")
}
if sig.S == nil {
return nil, nil, errors.New("invalid signature, S is nil")
}
if sig.R.Sign() != 1 {
return nil, nil, errors.New("invalid signature, R must be larger than zero")
}
if sig.S.Sign() != 1 {
return nil, nil, errors.New("invalid signature, S must be larger than zero")
}
return sig.R, sig.S, nil
}
*/
// 验证钱包地址是否和指定的公钥match
// 如果成功,返回true和对应的密码学标记位;如果失败,返回false和默认的密码学标记位0
func verifyAddressUsingPublicKey(address string, pub *ecdsa.PublicKey) (bool, uint8) {
//base58反解回byte[]数组
slice := base58Decode(address)
//检查是否是合法的base58编码
if len(slice) < 1 {
return false, 0
}
//拿到密码学标记位
byteVersion := slice[:1]
nVersion := uint8(byteVersion[0])
realAddress, error := getAddressFromPublicKey(pub)
if error != nil {
return false, 0
}
if realAddress == address {
return true, nVersion
}
return false, 0
}
// 返回33位长度的地址
func getAddressFromPublicKey(pub *ecdsa.PublicKey) (string, error) {
//using SHA256 and Ripemd160 for hash summary
//data := elliptic.Marshal(pub.Curve, pub.X, pub.Y)
// 将ECDSA公钥转换为ECDH公钥
ecdhPublicKey, err := pub.ECDH()
if err != nil {
return "", err
}
// 替换废弃的 elliptic.Marshal 函数
data := ecdhPublicKey.Bytes()
outputSha256 := hashUsingSha256(data)
OutputRipemd160 := hashUsingRipemd160(outputSha256)
//暂时只支持一个字节长度,也就是uint8的密码学标志位
// 判断是否是nist标准的私钥
nVersion := 1
switch pub.Params().Name {
case "P-256": // NIST
case "SM2-P-256": // 国密
nVersion = 2
default: // 不支持的密码学类型
return "", fmt.Errorf("this cryptography[%v] has not been supported yet", pub.Params().Name)
}
bufVersion := []byte{byte(nVersion)}
strSlice := make([]byte, len(bufVersion)+len(OutputRipemd160))
copy(strSlice, bufVersion)
copy(strSlice[len(bufVersion):], OutputRipemd160)
//using double SHA256 for future risks
checkCode := doubleSha256(strSlice)
simpleCheckCode := checkCode[:4]
slice := make([]byte, len(strSlice)+len(simpleCheckCode))
copy(slice, strSlice)
copy(slice[len(strSlice):], simpleCheckCode)
//使用base58编码,手写不容易出错。
//相比Base64,Base58不使用数字"0",字母大写"O",字母大写"I",和字母小写"l",以及"+"和"/"符号。
strEnc := base58Encode(slice)
return strEnc, nil
}
func hashUsingSha256(data []byte) []byte {
h := sha256.New()
h.Write(data)
out := h.Sum(nil)
return out
}
// 执行2次SHA256,这是为了防止SHA256算法被攻破。
func doubleSha256(data []byte) []byte {
return hashUsingSha256(hashUsingSha256(data))
}
// Ripemd160,这种hash算法可以缩短长度
func hashUsingRipemd160(data []byte) []byte {
h := ripemd160.New()
h.Write(data)
out := h.Sum(nil)
return out
}
/*
type Signature struct {
KeyID string `json:"keyId"`
Algorithm string `json:"algorithm"`
Headers string `json:"headers"`
Value string `json:"signature"`
}
func parseSignature(signatureString string) (*Signature, error) {
//逗号分割签名的字符串
s1 := strings.Split(signatureString, ",")
sigMap := make(map[string]string, 0)
for _, s2 := range s1 {
dIndex := strings.Index(s2, "=")
if dIndex < 0 {
continue
}
sigMap[s2[:dIndex]] = strings.Trim(strings.TrimSpace(s2[dIndex+1:]), `"`)
}
sigByte, _ := json.Marshal(sigMap)
// 解析签名字符串,提取相关信息
signature := &Signature{}
err := json.Unmarshal(sigByte, signature)
if err != nil {
return nil, err
}
return signature, nil
}
func verifySignature(signature *Signature, data string) (bool, error) {
switch signature.Algorithm {
case "rsa-sha256":
publicKey, err := getRSAPublicKeyPem(signature.KeyID)
if err != nil {
return false, err
}
// 验证签名
hashed := sha256.Sum256([]byte(data))
signatureBytes, err := base64.StdEncoding.DecodeString(signature.Value)
if err != nil {
return false, err
}
err = rsa.VerifyPKCS1v15(publicKey, crypto.SHA256, hashed[:], signatureBytes)
if err != nil {
return false, err
}
case "secp256k1": //以太坊账号的签名算法
// KeyID应为 chain://域名[address],合约地址,链ID 域名下的 publicKey 值
// 主要就是要解析IP地址,备选 #域名[address]#合约地址#链ID,通过#后缀跟上信息,前端点击时,使用js ajax获取需要处理的数据
// KeyID应为 address,用于和签名数据里获取的address进行比较
// 这里KeyID暂时定为address,实际应该为区块链域名,从域名反查合约获取address.这里比较简单
return verifySecp256k1Signature(signature.KeyID, data, signature.Value)
}
return true, nil
}
func getRSAPublicKeyPem(publicKeyID string) (*rsa.PublicKey, error) {
// 根据公钥 ID 获取对应的公钥
// 这里使用假数据,实际使用时需要替换为真实的公钥获取逻辑
//publicKeyPEM := "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAr9HicDyHYlpGVYVHrm7j\nU7Nq4z9SeynK8UUi+JoBWuotChg2oSDQtWuj+zdQSKM3g27+sqNNw/BuZp85BVT6\n8PRyamTHjVrZPj6JIC+A/EGeJTqycODoMTDTTdz3evxBUbPAH7By91VrMNE5i8zl\nJ40IqAYYNLjmUdvQliGmGpX/xmPAfIeJ/mMQ3kCq/2uSICrL1ORicAB/qqXgyPsB\nWZCTYOOdJsV9bbbhAQUqRjevZrRIdaVcrIObxTDY0VgtBJgsElGNxbnb/g4vfPgy\nWdi/E0qLSRyayml8lGZhPccgY3PnqGO765X/j0tra/I4JIjLC0AOV0nLs0fLmH72\nEwIDAQAB\n-----END PUBLIC KEY-----\n"
publicKeyPEM, err := responseJsonValue(publicKeyID, "publicKey.publicKeyPem", publicKeyID)
if err != nil {
return nil, err
}
if publicKeyPEM == nil {
return nil, errors.New("获取公钥值为nil")
}
// 解析公钥 PEM 格式
block, _ := pem.Decode([]byte(publicKeyPEM.(string)))
if block == nil || block.Type != "PUBLIC KEY" {
return nil, errors.New("公钥解析失败")
}
// 解析公钥 DER 格式
publicKeyInterface, err := x509.ParsePKIXPublicKey(block.Bytes)
if err != nil {
return nil, err
}
// 转换为 RSA 公钥
publicKey, ok := publicKeyInterface.(*rsa.PublicKey)
if !ok {
return nil, errors.New("公钥类型错误")
}
return publicKey, nil
}
func buildSignatureData(c *app.RequestContext, headers string) string {
// 构建签名字符串
var comparisonStrings []string
signedHeaders := strings.Split(headers, " ")
for _, header := range signedHeaders {
value := ""
header = strings.TrimSpace(header)
if header == "(request-target)" {
method := string(c.Method())
method = strings.ToLower(method)
uri := string(c.Request.URI().Path())
value = fmt.Sprintf("%s %s", method, uri)
} else {
value = string(c.GetHeader(header))
}
comparisonStrings = append(comparisonStrings, header+": "+value)
}
return strings.Join(comparisonStrings, "\n")
}
// generateRSASignature 对字符串签名,并将签名结果进行 Base64 编码
func generateRSASignature(signingString string) (string, error) {
// 读取私钥文件
privateKeyFile := datadir + "pem/private.pem"
privateKeyPEM, err := os.ReadFile(privateKeyFile)
if err != nil {
return "", fmt.Errorf("读取私钥文件失败:%w", err)
}
// 解析私钥
privateKeyBlock, _ := pem.Decode(privateKeyPEM)
privateKey, err := x509.ParsePKCS1PrivateKey(privateKeyBlock.Bytes)
if err != nil {
return "", fmt.Errorf("解析私钥失败:%w", err)
}
// 对签名前的字符串进行签名
hashed := sha256.Sum256([]byte(signingString))
signature, err := rsa.SignPKCS1v15(rand.Reader, privateKey, crypto.SHA256, hashed[:])
if err != nil {
return "", fmt.Errorf("签名失败:%w", err)
}
// 将签名结果进行 Base64 编码
signatureBase64 := base64.StdEncoding.EncodeToString(signature)
return signatureBase64, nil
}
*/