diff --git a/app/example/cmd/make_key.go b/app/example/cmd/make_key.go index 0860667b..24fc88fb 100644 --- a/app/example/cmd/make_key.go +++ b/app/example/cmd/make_key.go @@ -29,7 +29,7 @@ var MakeKeyCmd = &cobra.Command{ Run: func(cmd *cobra.Command, args []string) { // key.NewRsa().Make() - key.NewDSA().Make() + // key.NewDSA().Make() // key.KeyCheck() @@ -37,6 +37,10 @@ var MakeKeyCmd = &cobra.Command{ // key.ShowTorrent() + key.ShowBerP12() + + // key.NewGoEcdh().Make() + fmt.Println("生成各种证书成功") }, } diff --git a/app/example/controller/data.go b/app/example/controller/data.go index 248f68a3..0c8903c7 100644 --- a/app/example/controller/data.go +++ b/app/example/controller/data.go @@ -22,7 +22,7 @@ import ( "github.com/deatil/lakego-filesystem/filesystem" "github.com/deatil/go-cryptobin/ssh" - _ "github.com/deatil/go-cryptobin/ber" + _ "github.com/deatil/go-cryptobin/ber/asn1" _ "github.com/deatil/go-cryptobin/argon2" _ "github.com/deatil/go-cryptobin/bencode" _ "github.com/deatil/go-cryptobin/dh/dh" @@ -37,6 +37,7 @@ import ( _ "github.com/deatil/go-cryptobin/pkcs8s" _ "github.com/deatil/go-cryptobin/pkcs12" _ "github.com/deatil/go-cryptobin/jceks" + _ "github.com/deatil/go-cryptobin/elgamal" _ "github.com/deatil/go-cryptobin/cryptobin/ca" _ "github.com/deatil/go-cryptobin/cryptobin/dsa" // _ "github.com/deatil/go-cryptobin/cryptobin/ecdh" @@ -406,7 +407,7 @@ func (this *Data) Error(ctx *gin.Context) { ToBase64String() obj2Pub, _ := fs.Get("./runtime/key/key-pem/rsa/2048/rsa-pkcs8-pbe-en-SHA1AndDES.pub") obj2cyptde := obj2. - FromBase64String("Pok10M8e9u1WicbS08/IvoKChoYXfKbljcJYr6srL5TkaAJTYD4thgPDV/EzRvCqfJsQyDb0cOqM2kmwKDt5zl+Amf6TitTPKb9LxCCuKcz6VKHtoUZ+t4ENZM4y2bjRNjkChWdjjEb0kjoljWoaZ+zoWl+6QWRRug6NQJag78J3crqVA34iulsygC/sVEy/LKSJ76PBDx9srdqXpf03HiJgYUSso7YnZ3RT+AS13GgZy7BFZskrjIX2Qw64X8Ydtt5TrfMckjxf0QWdNSwmFxSeNh1Cn2gozG9sJl7yiELNiG0JqRDIOYQTpszj314W5CYEIa/y4eRTDmiNiKr3cA=="). + FromBase64String("QiDWcgThaEoeg664xpH6sIMNCfSfI2d/xvseMoCKNFeVK/xTX7xG+kNxZyQ21wXW1ljvhidRL7A+7eZnHEizoNQY1EJUsgDDMfuQxox0UwVe3PJ1VCtTg9XczbwTi2NHSSzmg2Y2QhSnmNzWmZMDM4xGUc/gLL21ynUwpKs0zLWk4mBOeZHX80o8MCUSwue+YZXsxE+vrP60PlC9wg/qgrlyCySVZ5X4RNdPJ1dKW/9e3vrNx4goSTNn4olytaVQ9bFlL1fNI7mkHbF4qy3OcDdQvTbfj4DqFRAn2VImzWwwdi0kuYoHZJfdZeZ1xPDHJ/Q5WA2BKBBo7ra2TPbf8A=="). FromPKCS8PublicKey([]byte(obj2Pub)). Verify([]byte("test-pass")). ToVerify() diff --git a/app/example/key/key_goecdh.go b/app/example/key/key_goecdh.go index d0d54081..7e2ff383 100644 --- a/app/example/key/key_goecdh.go +++ b/app/example/key/key_goecdh.go @@ -37,6 +37,9 @@ func (this GoEcdh) Make() { this.pkcs8(obj, curve) this.pkcs8En(obj, curve) + + this.pkcs8ECDH(obj, curve) + this.pkcs8EnECDH(obj, curve) } } @@ -49,7 +52,7 @@ func (this GoEcdh) pkcs8(obj cGoEcdh, name string) { CreatePublicKey(). ToKeyString() - file := fmt.Sprintf("%s/%s/%s-pkcs8", this.path, name, this.name) + file := fmt.Sprintf("%s/go_ecdh/%s/%s-pkcs8", this.path, name, this.name) this.fs.Put(file, priKey) this.fs.Put(file + ".pub", pubKey) @@ -66,7 +69,7 @@ func (this GoEcdh) pkcs8En(obj cGoEcdh, name string) { CreatePublicKey(). ToKeyString() - file := fmt.Sprintf("%s/%s/%s-pkcs8-en-%s-%s", this.path, name, this.name, c, h) + file := fmt.Sprintf("%s/go_ecdh/%s/%s-pkcs8-en-%s-%s", this.path, name, this.name, c, h) this.fs.Put(file, priKey) this.fs.Put(file + ".pub", pubKey) @@ -82,7 +85,59 @@ func (this GoEcdh) pkcs8En(obj cGoEcdh, name string) { CreatePublicKey(). ToKeyString() - file := fmt.Sprintf("%s/%s/%s-pkcs8-pbe-en-%s", this.path, name, this.name, c2) + file := fmt.Sprintf("%s/go_ecdh/%s/%s-pkcs8-pbe-en-%s", this.path, name, this.name, c2) + + this.fs.Put(file, priKey) + this.fs.Put(file + ".pub", pubKey) + } + +} + +// ================= + +func (this GoEcdh) pkcs8ECDH(obj cGoEcdh, name string) { + // 生成证书 + priKey := obj. + CreateECDHPrivateKey(). + ToKeyString() + pubKey := obj. + CreateECDHPublicKey(). + ToKeyString() + + file := fmt.Sprintf("%s/ecdh/%s/%s-pkcs8", this.path, name, this.name) + + this.fs.Put(file, priKey) + this.fs.Put(file + ".pub", pubKey) +} + +func (this GoEcdh) pkcs8EnECDH(obj cGoEcdh, name string) { + for _, c := range Pkcs8Ciphers { + for _, h := range Pkcs8Hashes { + // 生成证书 + priKey := obj. + CreateECDHPrivateKeyWithPassword(this.pass, c, h). + ToKeyString() + pubKey := obj. + CreateECDHPublicKey(). + ToKeyString() + + file := fmt.Sprintf("%s/ecdh/%s/%s-pkcs8-en-%s-%s", this.path, name, this.name, c, h) + + this.fs.Put(file, priKey) + this.fs.Put(file + ".pub", pubKey) + } + } + + for _, c2 := range Pkcs8PbeCiphers { + // 生成证书 + priKey := obj. + CreateECDHPrivateKeyWithPassword(this.pass, c2). + ToKeyString() + pubKey := obj. + CreateECDHPublicKey(). + ToKeyString() + + file := fmt.Sprintf("%s/ecdh/%s/%s-pkcs8-pbe-en-%s", this.path, name, this.name, c2) this.fs.Put(file, priKey) this.fs.Put(file + ".pub", pubKey) diff --git a/pkg/lakego-pkg/go-cryptobin/ber/asn1/ber_asn1_test.go b/pkg/lakego-pkg/go-cryptobin/ber/asn1/ber_asn1_test.go index 467a2383..b328782a 100644 --- a/pkg/lakego-pkg/go-cryptobin/ber/asn1/ber_asn1_test.go +++ b/pkg/lakego-pkg/go-cryptobin/ber/asn1/ber_asn1_test.go @@ -2,7 +2,6 @@ package asn1 import ( "bytes" - "encoding/asn1" "encoding/hex" "fmt" "math" @@ -13,12 +12,6 @@ import ( "time" ) -// Compatibility vars for ber_asn1_test.go -var ( - NullRawValue = asn1.NullRawValue - NullBytes = asn1.NullBytes -) - type boolTest struct { in []byte ok bool @@ -161,8 +154,8 @@ type bitStringTest struct { var bitStringTestData = []bitStringTest{ {[]byte{}, false, []byte{}, 0}, {[]byte{0x00}, true, []byte{}, 0}, - {[]byte{0x07, 0x00}, true, []byte{0x00}, 7}, - {[]byte{0x07, 0x40}, true, []byte{0x40}, 7}, + {[]byte{0x07, 0x00}, true, []byte{0x00}, 1}, + {[]byte{0x07, 0x40}, false, []byte{0x40}, 7}, {[]byte{0x08, 0x00}, false, []byte{}, 0}, } @@ -173,7 +166,7 @@ func TestBitString(t *testing.T) { t.Errorf("#%d: Incorrect error result (did fail? %v, expected: %v)", i, err == nil, test.ok) } if err == nil { - if test.bitLength != ret.PaddingBits || !bytes.Equal(ret.Bytes, test.out) { + if test.bitLength != ret.BitLength || !bytes.Equal(ret.Bytes, test.out) { t.Errorf("#%d: Bad result: %v (expected %v %v)", i, ret, test.out, test.bitLength) } } @@ -437,17 +430,17 @@ var unmarshalTestData = []struct { out interface{} }{ {[]byte{0x02, 0x01, 0x42}, newInt(0x42)}, - {[]byte{0x05, 0x00}, &asn1.RawValue{0, 5, false, []byte{}, []byte{0x05, 0x00}}}, + {[]byte{0x05, 0x00}, &RawValue{0, 5, false, false, []byte{}, []byte{0x05, 0x00}}}, {[]byte{0x30, 0x08, 0x06, 0x06, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d}, &TestObjectIdentifierStruct{[]int{1, 2, 840, 113549}}}, - {[]byte{0x03, 0x04, 0x06, 0x6e, 0x5d, 0xc0}, &BitString{[]byte{110, 93, 192}, 6}}, + {[]byte{0x03, 0x04, 0x06, 0x6e, 0x5d, 0xc0}, &BitString{[]byte{110, 93, 192}, 18}}, {[]byte{0x30, 0x09, 0x02, 0x01, 0x01, 0x02, 0x01, 0x02, 0x02, 0x01, 0x03}, &[]int{1, 2, 3}}, {[]byte{0x02, 0x01, 0x10}, newInt(16)}, {[]byte{0x13, 0x04, 't', 'e', 's', 't'}, newString("test")}, {[]byte{0x16, 0x04, 't', 'e', 's', 't'}, newString("test")}, // Ampersand is allowed in PrintableString due to mistakes by major CAs. {[]byte{0x13, 0x05, 't', 'e', 's', 't', '&'}, newString("test&")}, - {[]byte{0x16, 0x04, 't', 'e', 's', 't'}, &asn1.RawValue{0, 22, false, []byte("test"), []byte("\x16\x04test")}}, - {[]byte{0x04, 0x04, 1, 2, 3, 4}, &asn1.RawValue{0, 4, false, []byte{1, 2, 3, 4}, []byte{4, 4, 1, 2, 3, 4}}}, + {[]byte{0x16, 0x04, 't', 'e', 's', 't'}, &RawValue{0, 22, false, false, []byte("test"), []byte("\x16\x04test")}}, + {[]byte{0x04, 0x04, 1, 2, 3, 4}, &RawValue{0, 4, false, false, []byte{1, 2, 3, 4}, []byte{4, 4, 1, 2, 3, 4}}}, {[]byte{0x30, 0x03, 0x81, 0x01, 0x01}, &TestContextSpecificTags{1}}, {[]byte{0x30, 0x08, 0xa1, 0x03, 0x02, 0x01, 0x01, 0x02, 0x01, 0x02}, &TestContextSpecificTags2{1, 2}}, {[]byte{0x30, 0x03, 0x81, 0x01, '@'}, &TestContextSpecificTags3{"@"}}, @@ -467,6 +460,7 @@ func TestUnmarshal(t *testing.T) { if err != nil { t.Errorf("Unmarshal failed at index %d %v", i, err) } + if !reflect.DeepEqual(val, test.out) { t.Errorf("#%d:\nhave %#v\nwant %#v", i, val, test.out) } @@ -481,7 +475,7 @@ type Certificate struct { type TBSCertificate struct { Version int `asn1:"optional,explicit,default:0,tag:0"` - SerialNumber asn1.RawValue + SerialNumber RawValue SignatureAlgorithm AlgorithmIdentifier Issuer RDNSequence Validity Validity @@ -593,7 +587,7 @@ func TestObjectIdentifierEqual(t *testing.T) { var derEncodedSelfSignedCert = Certificate{ TBSCertificate: TBSCertificate{ Version: 0, - SerialNumber: asn1.RawValue{Class: 0, Tag: 2, IsCompound: false, Bytes: []uint8{0x0, 0x8c, 0xc3, 0x37, 0x92, 0x10, 0xec, 0x2c, 0x98}, FullBytes: []byte{2, 9, 0x0, 0x8c, 0xc3, 0x37, 0x92, 0x10, 0xec, 0x2c, 0x98}}, + SerialNumber: RawValue{Class: 0, Tag: 2, IsCompound: false, Bytes: []uint8{0x0, 0x8c, 0xc3, 0x37, 0x92, 0x10, 0xec, 0x2c, 0x98}, FullBytes: []byte{2, 9, 0x0, 0x8c, 0xc3, 0x37, 0x92, 0x10, 0xec, 0x2c, 0x98}}, SignatureAlgorithm: AlgorithmIdentifier{Algorithm: ObjectIdentifier{1, 2, 840, 113549, 1, 1, 5}}, Issuer: RDNSequence{ RelativeDistinguishedNameSET{AttributeTypeAndValue{Type: ObjectIdentifier{2, 5, 4, 6}, Value: "XX"}}, @@ -627,7 +621,7 @@ var derEncodedSelfSignedCert = Certificate{ 0x2a, 0xf7, 0x58, 0x9c, 0xf2, 0xc7, 0x70, 0x45, 0xdc, 0x8f, 0xde, 0xec, 0x35, 0x7d, 0x2, 0x3, 0x1, 0x0, 0x1, }, - PaddingBits: 0, + BitLength: 592, }, }, }, @@ -641,7 +635,7 @@ var derEncodedSelfSignedCert = Certificate{ 0xd9, 0x1e, 0xde, 0x14, 0xa5, 0xed, 0x76, 0xbf, 0x11, 0x6f, 0xe3, 0x60, 0xaa, 0xfa, 0x88, 0x21, 0x49, 0x4, 0x35, }, - PaddingBits: 0, + BitLength: 512, }, } @@ -966,7 +960,7 @@ func TestUnexportedStructField(t *testing.T) { } func TestNull(t *testing.T) { - unmarshaled := asn1.RawValue{} + unmarshaled := RawValue{} if _, err := Unmarshal(NullBytes, &unmarshaled); err != nil { t.Fatal(err) } @@ -984,7 +978,7 @@ func TestNull(t *testing.T) { func TestExplicitTagRawValueStruct(t *testing.T) { type foo struct { - A asn1.RawValue `asn1:"optional,explicit,tag:5"` + A RawValue `asn1:"optional,explicit,tag:5"` B []byte `asn1:"optional,explicit,tag:6"` } before := foo{B: []byte{1, 2, 3}} @@ -1007,10 +1001,10 @@ func TestExplicitTagRawValueStruct(t *testing.T) { func TestTaggedRawValue(t *testing.T) { type taggedRawValue struct { - A asn1.RawValue `asn1:"tag:5"` + A RawValue `asn1:"tag:5"` } type untaggedRawValue struct { - A asn1.RawValue + A RawValue } const isCompound = 0x20 const tag = 5 @@ -1019,11 +1013,11 @@ func TestTaggedRawValue(t *testing.T) { shouldMatch bool derBytes []byte }{ - {false, []byte{0x30, 3, asn1.TagInteger, 1, 1}}, - {true, []byte{0x30, 3, (asn1.ClassContextSpecific << 6) | tag, 1, 1}}, - {true, []byte{0x30, 3, (asn1.ClassContextSpecific << 6) | tag | isCompound, 1, 1}}, - {false, []byte{0x30, 3, (asn1.ClassApplication << 6) | tag | isCompound, 1, 1}}, - {false, []byte{0x30, 3, (asn1.ClassPrivate << 6) | tag | isCompound, 1, 1}}, + {false, []byte{0x30, 3, byte(TagInteger), 1, 1}}, + {true, []byte{0x30, 3, (byte(TagClassContextSpecific) << 6) | tag, 1, 1}}, + {true, []byte{0x30, 3, (byte(TagClassContextSpecific) << 6) | tag | isCompound, 1, 1}}, + {false, []byte{0x30, 3, (byte(TagClassApplication) << 6) | tag | isCompound, 1, 1}}, + {false, []byte{0x30, 3, (byte(TagClassPrivate) << 6) | tag | isCompound, 1, 1}}, } for i, test := range tests { diff --git a/pkg/lakego-pkg/go-cryptobin/ber/asn1/ber_test.go b/pkg/lakego-pkg/go-cryptobin/ber/asn1/ber_test.go index d8d75ab1..6af287b9 100644 --- a/pkg/lakego-pkg/go-cryptobin/ber/asn1/ber_test.go +++ b/pkg/lakego-pkg/go-cryptobin/ber/asn1/ber_test.go @@ -2,6 +2,15 @@ package asn1 import ( "math" + // "bytes" + "errors" + "unicode/utf16" + "testing" + "crypto/hmac" + "crypto/sha1" + "encoding/base64" + + cryptobin_pbkdf "github.com/deatil/go-cryptobin/kdf/pbkdf" ) func init() { @@ -67,9 +76,169 @@ var berUnmarshalTestData = []struct { in []byte out interface{} }{ - {[]byte{0x30, 0x80, 0x31, 0x80, 0x02, 0x01, 0x01, 0x02, 0x01, 0x02, 0x02, 0x01, 0x03, 0x00, 0x00, 0x00, 0x00}, &TestSet{Ints: []int{1, 2, 3}}}, - {[]byte{0x30, 0x80, 0x13, 0x03, 0x66, 0x6f, 0x6f, 0x02, 0x01, 0x22, 0x02, 0x01, 0x33, 0x00, 0x00}, &TestElementsAfterString{"foo", 0x22, 0x33}}, - {[]byte{0x30, 0x80, 0xa2, 0x80, 0x31, 0x08, 0xa1, 0x03, 0x02, 0x01, 0x01, 0x02, 0x01, 0x02, 0x00, 0x00, + { + []byte{0x30, 0x80, 0x31, 0x80, 0x02, 0x01, 0x01, 0x02, 0x01, 0x02, 0x02, 0x01, 0x03, 0x00, 0x00, 0x00, 0x00}, + &TestSet{Ints: []int{1, 2, 3}}, + }, + { + []byte{0x30, 0x80, 0x13, 0x03, 0x66, 0x6f, 0x6f, 0x02, 0x01, 0x22, 0x02, 0x01, 0x33, 0x00, 0x00}, + &TestElementsAfterString{"foo", 0x22, 0x33}, + }, + { + []byte{0x30, 0x80, 0xa2, 0x80, 0x31, 0x08, 0xa1, 0x03, 0x02, 0x01, 0x01, 0x02, 0x01, 0x02, 0x00, 0x00, 0x60, 0x80, 0x30, 0x80, 0x02, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, &TestExplicitIndefinite{TestContextSpecificTags2{1, 2}, []int{2}}}, } + +var sm2Pkcs12 = "MIACAQMwgAYJKoZIhvcNAQcBoIAkgASCA+gwgDCABgkqhkiG9w0BBwGggCSABIHNMIHKMIHHBgsqhkiG" + + "9w0BDAoBAqB2MHQwKAYKKoZIhvcNAQwBAzAaBBRBCcN+h46YwoEjCwYsx5R2Ggq3HgICBAAESFoOSL36" + + "Ku1nQYesdqh09xuQFCbr5Ozm5+aF91Bbs0tdRheyKY8JvC4VCzX2AsCrevOFb3io6teNdkcmFOeDOhSE" + + "VYuzJIHZZTFAMBkGCSqGSIb3DQEJFDEMHgoAQQAgAEsAZQB5MCMGCSqGSIb3DQEJFTEWBBSlqw4sTrXP" + + "Y1Io0OetvRz8sQyBQgAAAAAAADCABgkqhkiG9w0BBwaggDCAAgEAMIAGCSqGSIb3DQEHATAoBgoqhkiG" + + "9w0BDAEGMBoEFJb7ThL0KhfqB5ov1gQFeYRZWmZ/AgIEAKCABIICwBv1XgH3DfbaauQS27Gb036glq1K" + + "n/seDdCLdUROkxMa1HzXiyeDGB48ekgHYSLqzCNdnry2NZvMWoVPTaYvgF04DhZxPTcSYxWOPQL2+LX0" + + "GwEdjidQvGF1jze+R4uUxyXg9HXmxJ7jtl5djgHsPVeKIaQXSHQCcM1gYwsGDkV14zhrUfDiCw7LxMMg" + + "9To+x3g0Tx/ZcuCF5gmj8jgzsM7AqlPp/+UrVri/LB+mDE/IhRWL3Bkp+wBrTrIoQFLGQVQS3McWX+tx" + + "C4OXtLzTjoTu5VosvXDDxDhsrSfZNNztZTw6z2l48IY5O7vMUsFkW7eCkiLuek5ck1uhv50lqNvEEbsk" + + "uMj7j3fyZnBZAj0ieODo3Uu6fdKpTy0ysmKcPDMMES/5ASjDz3Zk/56vLr09s7uTTH4+xOZViP/T5y28" + + "40qrcefN2fmtOtyuUGO71ul+/LpXKch4atDR9jSv5ovyXhKKxfCOfHW2oV43aJwA66+uElR+ZsjwyLmA" + + "p0f12HdxeKJIWX4yDQiAJ9n/F3W3nBMpmZBNwEVdUE6+OoZUU93dD6BExMC27DuiaH622Mi2ydfkW+l2" + + "frehlTl0CmontrmpkJ30u/U25x6fI8wB0aXVd3IzWPYe0yMdnPZlOLajjer2DU6T4KD6spR3Cp0Vg1GW" + + "XTTj3gROAw9tKbJCWKLCkadhzHSnJ1Y9edjcwmIOWBZtM94julcKeviMW0DSwHojJy4bD2DO7fQv+JPg" + + "uG6Xlm5zajxOsnuUy0AzqRywTKplCQwa/U9i65FNBhsRSNE2Cmx8EXuWMxigQO3gyyQsUMrIpoUSzzQL" + + "vcIE5UCMvP69G5r5C9TSvQ2pKeAvkUIckMZkaA/lMqKkM55dOeFa60AX/Qj2WO0Yu6y18eaMSXnvwMWv" + + "B2UywrHDBB4iEu2+kNke7EUSzQbItlBZzJgAAAAAAAAAAAAAAAAAAAAAAAAwPTAhMAkGBSsOAwIaBQAE" + + "FJbJE8lKP08n4Y6jWYZcGrL6sy2gBBSkmhIJK3GKeGqMUxotT1on92p3FgICBAAAAA==" + +type AlgorithmIdentifier2 struct { + Algorithm ObjectIdentifier + Parameters RawValue `asn1:"optional"` +} + +type contentInfo struct { + ContentType ObjectIdentifier + Content RawValue `asn1:"tag:0,explicit,optional"` +} + +// from PKCS#7: +type digestInfo struct { + Algorithm AlgorithmIdentifier2 + Digest []byte +} + +type macData struct { + Mac digestInfo + MacSalt []byte + Iterations int `asn1:"optional,default:1"` +} + +func (this macData) Verify(message []byte, password []byte) bool { + h := sha1.New + + hashSize := h().Size() + + key := cryptobin_pbkdf.Key(h, hashSize, 64, this.MacSalt, password, this.Iterations, 3, hashSize) + + mac := hmac.New(h, key) + mac.Write(message) + expectedMAC := mac.Sum(nil) + + if !hmac.Equal(this.Mac.Digest, expectedMAC) { + return false + } + + return true +} + +type pfxPdu struct { + Version int + AuthSafe contentInfo + MacData macData `asn1:"optional"` +} + +func Test_SM2Pkcs12(t *testing.T) { + ber, err := base64.StdEncoding.DecodeString(sm2Pkcs12) + if err != nil { + t.Errorf("err: %v", err) + } + + pfx := new(pfxPdu) + if _, err = Unmarshal(ber, pfx); err != nil { + t.Errorf("Unmarshal err: %v", err) + } + + if pfx.Version != 3 { + t.Errorf("Version err: %d", pfx.Version) + } + + if _, err = Unmarshal(pfx.AuthSafe.Content.Bytes, &pfx.AuthSafe.Content); err != nil { + t.Errorf("Unmarshal2 err: %v", err) + } + + data := pfx.AuthSafe.Content.Bytes + // data = bytes.TrimRight(data, string([]byte{0})) + + var authenticatedSafes = make([]RawValue, 0) + + for { + var authenticatedSafe RawValue + data, err = Unmarshal(data, &authenticatedSafe) + if err != nil { + t.Errorf("Unmarshal octet err: %v", err) + } + + authenticatedSafes = append(authenticatedSafes, authenticatedSafe) + + if len(data) == 0 { + break + } + } + + password := "12345678" + newPassword, err := testBmpStringZeroTerminated(password) + if err != nil { + t.Errorf("password err: %v", err) + } + + checked := pfx.MacData.Verify(data, newPassword) + if !checked { + // t.Errorf("password is error") + } + + // t.Errorf("authenticatedSafes data: %#v", authenticatedSafes) + +} + +// bmpStringZeroTerminated returns s encoded in UCS-2 with a zero terminator. +func testBmpStringZeroTerminated(s string) ([]byte, error) { + // References: + // https://tools.ietf.org/html/rfc7292#appendix-B.1 + // The above RFC provides the info that BMPStrings are NULL terminated. + + ret, err := testBmpString(s) + if err != nil { + return nil, err + } + + return append(ret, 0, 0), nil +} + +// bmpString returns s encoded in UCS-2 +func testBmpString(s string) ([]byte, error) { + // References: + // https://tools.ietf.org/html/rfc7292#appendix-B.1 + // https://en.wikipedia.org/wiki/Plane_(Unicode)#Basic_Multilingual_Plane + // - non-BMP characters are encoded in UTF 16 by using a surrogate pair of 16-bit codes + // EncodeRune returns 0xfffd if the rune does not need special encoding + + ret := make([]byte, 0, 2*len(s)+2) + + for _, r := range s { + if t, _ := utf16.EncodeRune(r); t != 0xfffd { + return nil, errors.New("pkcs12: string contains characters that cannot be encoded in UCS-2") + } + ret = append(ret, byte(r/256), byte(r%256)) + } + + return ret, nil +} diff --git a/pkg/lakego-pkg/go-cryptobin/ber/asn1/bitstring.go b/pkg/lakego-pkg/go-cryptobin/ber/asn1/bitstring.go index bbc68411..f8ed0cdc 100644 --- a/pkg/lakego-pkg/go-cryptobin/ber/asn1/bitstring.go +++ b/pkg/lakego-pkg/go-cryptobin/ber/asn1/bitstring.go @@ -1,41 +1,55 @@ package asn1 import ( - "fmt" "reflect" ) type BitString struct { - Bytes []byte - PaddingBits int + Bytes []byte // bits packed into bytes. + BitLength int // length in bits. } -var bitStringType = reflect.TypeOf(BitString{}) - -func NewBitString(b []byte, paddingBits int) (BitString, error) { - bitString := BitString{ - Bytes: b, +// At returns the bit at the given index. If the index is out of range it +// returns 0. +func (b BitString) At(i int) int { + if i < 0 || i >= b.BitLength { + return 0 } + x := i / 8 + y := 7 - uint(i%8) + return int(b.Bytes[x]>>y) & 1 +} - if paddingBits > 7 { - return bitString, fmt.Errorf("too many padding bits: expecting <= 7, got: %d", paddingBits) +// RightAlign returns a slice where the padding bits are at the beginning. The +// slice may share memory with the BitString. +func (b BitString) RightAlign() []byte { + shift := uint(8 - (b.BitLength % 8)) + if shift == 8 || len(b.Bytes) == 0 { + return b.Bytes } - if len(b) == 0 && paddingBits != 0 { - return bitString, fmt.Errorf("empty bit string, but got %d padding bits", paddingBits) + a := make([]byte, len(b.Bytes)) + a[0] = b.Bytes[0] >> shift + for i := 1; i < len(b.Bytes); i++ { + a[i] = b.Bytes[i-1] << (8 - shift) + a[i] |= b.Bytes[i] >> shift } - bitString.PaddingBits = paddingBits - - return bitString, nil + return a } +var bitStringType = reflect.TypeOf(BitString{}) + type bitStringEncoder BitString -func (e bitStringEncoder) encode() ([]byte, error) { - buf := make([]byte, len(e.Bytes)+1) - buf[0] = byte(e.PaddingBits) - copy(buf[1:], e.Bytes) +func (b bitStringEncoder) length() int { + return len(b.Bytes) + 1 +} + +func (b bitStringEncoder) encode() ([]byte, error) { + buf := make([]byte, len(b.Bytes)+1) + buf[0] = byte((8 - b.BitLength%8) % 8) + copy(buf[1:], b.Bytes) return buf, nil } diff --git a/pkg/lakego-pkg/go-cryptobin/ber/asn1/bool.go b/pkg/lakego-pkg/go-cryptobin/ber/asn1/bool.go index 63667a03..20343670 100644 --- a/pkg/lakego-pkg/go-cryptobin/ber/asn1/bool.go +++ b/pkg/lakego-pkg/go-cryptobin/ber/asn1/bool.go @@ -7,6 +7,26 @@ type Flag bool type boolEncoder bool +func NewBoolEncoder(d bool) boolEncoder { + i := boolEncoder(d) + + return i +} + +func NewBoolEncoderWithInt(d int) boolEncoder { + i := boolEncoder(false) + + if d != 0 { + i = boolEncoder(true) + } + + return i +} + +func (b boolEncoder) length() int { + return 1 +} + func (e boolEncoder) encode() ([]byte, error) { if e { return []byte{0xff}, nil diff --git a/pkg/lakego-pkg/go-cryptobin/ber/asn1/decode.go b/pkg/lakego-pkg/go-cryptobin/ber/asn1/decode.go index 3c01a03d..f946781e 100644 --- a/pkg/lakego-pkg/go-cryptobin/ber/asn1/decode.go +++ b/pkg/lakego-pkg/go-cryptobin/ber/asn1/decode.go @@ -9,7 +9,6 @@ import ( "reflect" "unicode/utf8" "unicode/utf16" - "encoding/asn1" ) // We start by dealing with each of the primitive types in turn. @@ -133,17 +132,19 @@ func parseBigInt(bytes []byte) (*big.Int, error) { // parseBitString parses an ASN.1 bit string from the given byte slice and returns it. func parseBitString(bytes []byte) (ret BitString, err error) { if len(bytes) == 0 { - err = SyntaxError{Msg: "zero length BIT STRING"} + err = SyntaxError{"zero length BIT STRING"} return } paddingBits := int(bytes[0]) - if paddingBits > 7 || len(bytes) == 1 && paddingBits > 0 { - err = SyntaxError{Msg: "invalid padding bits in BIT STRING"} + if paddingBits > 7 || + len(bytes) == 1 && paddingBits > 0 || + bytes[len(bytes)-1]&((1<= 31 { + b |= 0x1f + dst = append(dst, b) + dst = appendBase128Int(dst, int64(t.tag)) + } else { + b |= uint8(t.tag) + dst = append(dst, b) + } + + // it is Indefinite + // 非定长模式 + if t.isIndefinite { + dst = append(dst, byte(0x80)) + } else { + if t.length >= 128 { + l := lengthLength(t.length) + dst = append(dst, 0x80|byte(l)) + dst = appendLength(dst, t.length) + } else { + dst = append(dst, byte(t.length)) + } + } + + return dst +} + +func appendLength(dst []byte, i int) []byte { + n := lengthLength(i) + + for ; n > 0; n-- { + dst = append(dst, byte(i>>uint((n-1)*8))) + } + + return dst +} + +func lengthLength(i int) (numBytes int) { + numBytes = 1 + + for i > 255 { + numBytes++ + i >>= 8 + } + + return +} + +func base128IntLength(n int64) int { + if n == 0 { + return 1 + } + + l := 0 + for i := n; i > 0; i >>= 7 { + l++ + } + + return l +} + +func appendBase128Int(dst []byte, n int64) []byte { + l := base128IntLength(n) + + for i := l - 1; i >= 0; i-- { + o := byte(n >> uint(i*7)) + o &= 0x7f + if i != 0 { + o |= 0x80 + } + + dst = append(dst, o) + } + + return dst +} + func isEmpty(value reflect.Value) bool { if value.Type() == nullType { return false diff --git a/pkg/lakego-pkg/go-cryptobin/ber/asn1/encode_test.go b/pkg/lakego-pkg/go-cryptobin/ber/asn1/encode_test.go index 34780537..9e5ffbee 100644 --- a/pkg/lakego-pkg/go-cryptobin/ber/asn1/encode_test.go +++ b/pkg/lakego-pkg/go-cryptobin/ber/asn1/encode_test.go @@ -236,15 +236,12 @@ var marshalTests = []marshalTest{ {time.Date(1991, time.May, 6, 23, 45, 40, 0, time.UTC), "17113931303530363233343534302b30303030"}, // BIT STRING - {BitString{[]byte{0x80}, 7}, "03020780"}, - {BitString{[]byte{0x81, 0xf0}, 4}, "03030481f0"}, - {BitString{[]byte{0b01101110, 0b01011101, 0b11000000}, 6}, "0304066e5dc0"}, + {BitString{[]byte{0x80}, 8}, "03020080"}, + {BitString{[]byte{0x81, 0xf0}, 16}, "03030081f0"}, + {BitString{[]byte{0b01101110, 0b01011101, 0b11000000}, 24}, "0304006e5dc0"}, {BitString{[]byte{}, 0}, "030100"}, - {BitString{[]byte{0x40}, 4}, "03020440"}, - {BitString{[]byte{0x80}, 7}, "03020780"}, - {BitString{[]byte{0x00, 0x00}, 7}, "0303070000"}, - {BitString{[]byte{0xe0}, 5}, "030205e0"}, - {BitString{[]byte{0x01}, 0}, "03020001"}, + {BitString{[]byte{0x40}, 8}, "03020040"}, + {BitString{[]byte{0x80}, 8}, "03020080"}, // omitempty {omitEmptyTest{[]string{}}, "3000"}, @@ -272,6 +269,29 @@ func TestMarshal(t *testing.T) { } } + +func TestMarshal2(t *testing.T) { + data := []byte{0x81, 0xf0} + in := BitString{data, len(data)*8} + + res, err := Marshal(in) + if err != nil { + t.Errorf("failed: %s", err) + } + + var data2 BitString + _, err = Unmarshal(res, &data2) + if err != nil { + t.Errorf("failed: %s", err) + } + + res2 := data2.RightAlign() + + if !bytes.Equal(res2, data) { + t.Errorf("got: %x want %x", res2, data) + } +} + type marshalWithOptionsTest struct { in interface{} out string @@ -324,7 +344,7 @@ var marshalWithOptionsTests = []marshalWithOptionsTest{ // explicit {true, "a2030101ff", "tag:2,explicit"}, {1, "a203020101", "tag:2,explicit"}, - {BitString{[]byte{0x56}, 1}, "a20403020156", "tag:2,explicit"}, + {BitString{[]byte{0x56}, 8}, "a20403020056", "tag:2,explicit"}, {[]byte{0x56}, "a203040156", "tag:2,explicit"}, {"Jones", "1a054a6f6e6573", "visible"}, {"Jones", "43054a6f6e6573", "visible,application,tag:3"}, diff --git a/pkg/lakego-pkg/go-cryptobin/ber/asn1/integer.go b/pkg/lakego-pkg/go-cryptobin/ber/asn1/integer.go index 3f6aefbf..9c2bdc2d 100644 --- a/pkg/lakego-pkg/go-cryptobin/ber/asn1/integer.go +++ b/pkg/lakego-pkg/go-cryptobin/ber/asn1/integer.go @@ -3,6 +3,7 @@ package asn1 import ( "math/big" ) + // ENUMERATED // An Enumerated is represented as a plain int. @@ -58,11 +59,11 @@ func makeBigInt(n *big.Int) (encoder, error) { for i := range bytes { bytes[i] ^= 0xff } - + if len(bytes) == 0 || bytes[0]&0x80 == 0 { return multiEncoder([]encoder{byteFFEncoder, bytesEncoder(bytes)}), nil } - + return bytesEncoder(bytes), nil } else if n.Sign() == 0 { // Zero is written as a single 0 zero rather than no bytes. @@ -74,7 +75,7 @@ func makeBigInt(n *big.Int) (encoder, error) { // looking like a negative number. return multiEncoder([]encoder{byte00Encoder, bytesEncoder(bytes)}), nil } - + return bytesEncoder(bytes), nil } -} \ No newline at end of file +} diff --git a/pkg/lakego-pkg/go-cryptobin/ber/asn1/null.go b/pkg/lakego-pkg/go-cryptobin/ber/asn1/null.go index 15a3866f..ee2017d2 100644 --- a/pkg/lakego-pkg/go-cryptobin/ber/asn1/null.go +++ b/pkg/lakego-pkg/go-cryptobin/ber/asn1/null.go @@ -8,6 +8,11 @@ type Null struct{} type nullEncoder Null +func (b nullEncoder) length() int { + return 1 +} + func (e nullEncoder) encode() ([]byte, error) { return nil, nil } + diff --git a/pkg/lakego-pkg/go-cryptobin/ber/asn1/oid.go b/pkg/lakego-pkg/go-cryptobin/ber/asn1/oid.go index 2db353dd..9b818840 100644 --- a/pkg/lakego-pkg/go-cryptobin/ber/asn1/oid.go +++ b/pkg/lakego-pkg/go-cryptobin/ber/asn1/oid.go @@ -71,6 +71,10 @@ var objectIdentifierType = reflect.TypeOf(ObjectIdentifier{}) type objectIdentifierEncoder ObjectIdentifier +func (b objectIdentifierEncoder) length() int { + return len(b) +} + func (e objectIdentifierEncoder) encode() ([]byte, error) { b := new(bytes.Buffer) diff --git a/pkg/lakego-pkg/go-cryptobin/ber/asn1/real.go b/pkg/lakego-pkg/go-cryptobin/ber/asn1/real.go index d63bd55a..e4aec41c 100644 --- a/pkg/lakego-pkg/go-cryptobin/ber/asn1/real.go +++ b/pkg/lakego-pkg/go-cryptobin/ber/asn1/real.go @@ -8,6 +8,10 @@ import ( type realEncoder float64 +func (b realEncoder) length() int { + return 2 +} + func (e realEncoder) encode() ([]byte, error) { // ECMA-63 // https://www.ecma-international.org/wp-content/uploads/ECMA-63_1st_edition_september_1980.pdf @@ -15,16 +19,16 @@ func (e realEncoder) encode() ([]byte, error) { n := float64(e) switch { - case math.IsInf(n, 1): - return []byte{0x40}, nil - case math.IsInf(n, -1): - return []byte{0x41}, nil - case math.IsNaN(n): - return []byte{0x42}, nil - case n == 0.0: - if math.Signbit(n) { - return []byte{0x43}, nil - } + case math.IsInf(n, 1): + return []byte{0x40}, nil + case math.IsInf(n, -1): + return []byte{0x41}, nil + case math.IsNaN(n): + return []byte{0x42}, nil + case n == 0.0: + if math.Signbit(n) { + return []byte{0x43}, nil + } } nString := []byte(strconv.FormatFloat(n, 'G', -1, 64)) diff --git a/pkg/lakego-pkg/go-cryptobin/ber/asn1/string.go b/pkg/lakego-pkg/go-cryptobin/ber/asn1/string.go index 241f25b7..94f97904 100644 --- a/pkg/lakego-pkg/go-cryptobin/ber/asn1/string.go +++ b/pkg/lakego-pkg/go-cryptobin/ber/asn1/string.go @@ -5,13 +5,12 @@ import ( "unicode" ) -// RawContent is used to signal that the undecoded, DER data needs to be -// preserved for a struct. To use it, the first field of the struct must have -// this type. It's an error for any of the other fields to have this type. -type RawContent []byte - type stringEncoder string +func (b stringEncoder) length() int { + return len(b) +} + func (e stringEncoder) encode() ([]byte, error) { return []byte(e), nil } diff --git a/pkg/lakego-pkg/go-cryptobin/cryptobin/ecdh/create.go b/pkg/lakego-pkg/go-cryptobin/cryptobin/ecdh/create.go index 317088f8..b43b4f48 100644 --- a/pkg/lakego-pkg/go-cryptobin/cryptobin/ecdh/create.go +++ b/pkg/lakego-pkg/go-cryptobin/cryptobin/ecdh/create.go @@ -3,6 +3,7 @@ package ecdh import ( "errors" "crypto/rand" + "crypto/x509" "encoding/pem" cryptobin_ecdh "github.com/deatil/go-cryptobin/ecdh" @@ -36,7 +37,7 @@ func (this Ecdh) CreatePrivateKey() Ecdh { return this.AppendError(err) } - privateKey, err := cryptobin_ecdh.MarshalPrivateKey(this.privateKey) + privateKey, err := x509.MarshalPKCS8PrivateKey(this.privateKey) if err != nil { return this.AppendError(err) } @@ -65,7 +66,7 @@ func (this Ecdh) CreatePrivateKeyWithPassword(password string, opts ...any) Ecdh } // 生成私钥 - privateKey, err := cryptobin_ecdh.MarshalPrivateKey(this.privateKey) + privateKey, err := x509.MarshalPKCS8PrivateKey(this.privateKey) if err != nil { return this.AppendError(err) } @@ -94,6 +95,87 @@ func (this Ecdh) CreatePublicKey() Ecdh { return this.AppendError(err) } + publicKeyBytes, err := x509.MarshalPKIXPublicKey(this.publicKey) + if err != nil { + return this.AppendError(err) + } + + publicBlock := &pem.Block{ + Type: "PUBLIC KEY", + Bytes: publicKeyBytes, + } + + this.keyData = pem.EncodeToMemory(publicBlock) + + return this +} + +// ======================= + +// 生成私钥 pem 数据, 库自使用的 asn1 格式 +func (this Ecdh) CreateECDHPrivateKey() Ecdh { + if this.privateKey == nil { + err := errors.New("Ecdh: privateKey error.") + return this.AppendError(err) + } + + privateKey, err := cryptobin_ecdh.MarshalPrivateKey(this.privateKey) + if err != nil { + return this.AppendError(err) + } + + privateBlock := &pem.Block{ + Type: "PRIVATE KEY", + Bytes: privateKey, + } + + this.keyData = pem.EncodeToMemory(privateBlock) + + return this +} + +// 生成 PKCS8 私钥带密码 pem 数据, 库自使用的 asn1 格式 +func (this Ecdh) CreateECDHPrivateKeyWithPassword(password string, opts ...any) Ecdh { + if this.privateKey == nil { + err := errors.New("Ecdh: privateKey error.") + return this.AppendError(err) + } + + opt, err := cryptobin_pkcs8s.ParseOpts(opts...) + if err != nil { + return this.AppendError(err) + } + + // 生成私钥 + privateKey, err := cryptobin_ecdh.MarshalPrivateKey(this.privateKey) + if err != nil { + return this.AppendError(err) + } + + // 生成加密数据 + privateBlock, err := cryptobin_pkcs8s.EncryptPEMBlock( + rand.Reader, + "ENCRYPTED PRIVATE KEY", + privateKey, + []byte(password), + opt, + ) + if err != nil { + return this.AppendError(err) + } + + this.keyData = pem.EncodeToMemory(privateBlock) + + return this +} + +// 生成公钥 pem 数据, 库自使用的 asn1 格式 +func (this Ecdh) CreateECDHPublicKey() Ecdh { + if this.publicKey == nil { + err := errors.New("Ecdh: publicKey error.") + return this.AppendError(err) + } + publicKeyBytes, err := cryptobin_ecdh.MarshalPublicKey(this.publicKey) if err != nil { return this.AppendError(err) @@ -109,6 +191,8 @@ func (this Ecdh) CreatePublicKey() Ecdh { return this } +// ======================= + // 根据公钥和私钥生成对称密钥 func (this Ecdh) CreateSecretKey() Ecdh { if this.privateKey == nil { diff --git a/pkg/lakego-pkg/go-cryptobin/cryptobin/ecdh/from.go b/pkg/lakego-pkg/go-cryptobin/cryptobin/ecdh/from.go index 0128069d..71346c6e 100644 --- a/pkg/lakego-pkg/go-cryptobin/cryptobin/ecdh/from.go +++ b/pkg/lakego-pkg/go-cryptobin/cryptobin/ecdh/from.go @@ -90,6 +90,89 @@ func (this Ecdh) FromPublicKeyDer(der []byte) Ecdh { // ========== +// 私钥, 库自使用的 asn1 格式 +func (this Ecdh) FromECDHPrivateKey(key []byte) Ecdh { + parsedKey, err := this.ParseECDHPrivateKeyFromPEM(key) + if err != nil { + return this.AppendError(err) + } + + this.privateKey = parsedKey.(*ecdh.PrivateKey) + + return this +} + +// 私钥, 库自使用的 asn1 格式 +func FromECDHPrivateKey(key []byte) Ecdh { + return defaultECDH.FromECDHPrivateKey(key) +} + +// 私钥带密码, 库自使用的 asn1 格式 +func (this Ecdh) FromECDHPrivateKeyWithPassword(key []byte, password string) Ecdh { + parsedKey, err := this.ParseECDHPrivateKeyFromPEMWithPassword(key, password) + if err != nil { + return this.AppendError(err) + } + + this.privateKey = parsedKey.(*ecdh.PrivateKey) + + return this +} + +// 私钥, 库自使用的 asn1 格式 +func FromECDHPrivateKeyWithPassword(key []byte, password string) Ecdh { + return defaultECDH.FromECDHPrivateKeyWithPassword(key, password) +} + +// 公钥, 库自使用的 asn1 格式 +func (this Ecdh) FromECDHPublicKey(key []byte) Ecdh { + parsedKey, err := this.ParseECDHPublicKeyFromPEM(key) + if err != nil { + return this.AppendError(err) + } + + this.publicKey = parsedKey.(*ecdh.PublicKey) + + return this +} + +// 公钥, 库自使用的 asn1 格式 +func FromECDHPublicKey(key []byte) Ecdh { + return defaultECDH.FromECDHPublicKey(key) +} + +// ========== + +// DER 私钥, 库自使用的 asn1 格式 +func (this Ecdh) FromECDHPrivateKeyDer(der []byte) Ecdh { + key := cryptobin_tool.EncodeDerToPem(der, "PRIVATE KEY") + + parsedKey, err := this.ParseECDHPrivateKeyFromPEM(key) + if err != nil { + return this.AppendError(err) + } + + this.privateKey = parsedKey.(*ecdh.PrivateKey) + + return this +} + +// DER 公钥, 库自使用的 asn1 格式 +func (this Ecdh) FromECDHPublicKeyDer(der []byte) Ecdh { + key := cryptobin_tool.EncodeDerToPem(der, "PUBLIC KEY") + + parsedKey, err := this.ParseECDHPublicKeyFromPEM(key) + if err != nil { + return this.AppendError(err) + } + + this.publicKey = parsedKey.(*ecdh.PublicKey) + + return this +} + +// ========== + // 生成密钥 func (this Ecdh) GenerateKey() Ecdh { privateKey, err := this.curve.GenerateKey(rand.Reader) @@ -104,6 +187,6 @@ func (this Ecdh) GenerateKey() Ecdh { } // 生成密钥 -func GenerateKey() Ecdh { - return defaultECDH.GenerateKey() +func GenerateKey(name string) Ecdh { + return defaultECDH.SetCurve(name).GenerateKey() } diff --git a/pkg/lakego-pkg/go-cryptobin/cryptobin/ecdh/parse.go b/pkg/lakego-pkg/go-cryptobin/cryptobin/ecdh/parse.go index 1e0d7465..aed1f511 100644 --- a/pkg/lakego-pkg/go-cryptobin/cryptobin/ecdh/parse.go +++ b/pkg/lakego-pkg/go-cryptobin/cryptobin/ecdh/parse.go @@ -3,8 +3,10 @@ package ecdh import ( "errors" "crypto" - "encoding/pem" + "crypto/x509" "crypto/ecdh" + "crypto/ecdsa" + "encoding/pem" cryptobin_ecdh "github.com/deatil/go-cryptobin/ecdh" cryptobin_pkcs8s "github.com/deatil/go-cryptobin/pkcs8s" @@ -26,6 +28,109 @@ func (this Ecdh) ParsePrivateKeyFromPEM(key []byte) (crypto.PrivateKey, error) { return nil, ErrKeyMustBePEMEncoded } + // Parse the key + var parsedKey any + if parsedKey, err = x509.ParsePKCS8PrivateKey(block.Bytes); err != nil { + return nil, err + } + + switch pkey := parsedKey.(type) { + case *ecdh.PrivateKey: + return pkey, nil + case *ecdsa.PrivateKey: + priKey, err := pkey.ECDH() + if err != nil { + return nil, err + } + + return priKey, nil + } + + return nil, ErrNotPrivateKey +} + +// 解析私钥带密码 +func (this Ecdh) ParsePrivateKeyFromPEMWithPassword(key []byte, password string) (crypto.PrivateKey, error) { + var err error + + // Parse PEM block + var block *pem.Block + if block, _ = pem.Decode(key); block == nil { + return nil, ErrKeyMustBePEMEncoded + } + + var blockDecrypted []byte + if blockDecrypted, err = cryptobin_pkcs8s.DecryptPEMBlock(block, []byte(password)); err != nil { + return nil, err + } + + var parsedKey any + if parsedKey, err = x509.ParsePKCS8PrivateKey(blockDecrypted); err != nil { + return nil, err + } + + switch pkey := parsedKey.(type) { + case *ecdh.PrivateKey: + return pkey, nil + case *ecdsa.PrivateKey: + priKey, err := pkey.ECDH() + if err != nil { + return nil, err + } + + return priKey, nil + } + + return nil, ErrNotPrivateKey +} + +// 解析公钥 +func (this Ecdh) ParsePublicKeyFromPEM(key []byte) (crypto.PublicKey, error) { + var err error + + // Parse PEM block + var block *pem.Block + if block, _ = pem.Decode(key); block == nil { + return nil, ErrKeyMustBePEMEncoded + } + + // Parse the key + var parsedKey any + if parsedKey, err = x509.ParsePKIXPublicKey(block.Bytes); err != nil { + if cert, err := x509.ParseCertificate(block.Bytes); err == nil { + parsedKey = cert.PublicKey + } else { + return nil, err + } + } + + switch pkey := parsedKey.(type) { + case *ecdh.PublicKey: + return pkey, nil + case *ecdsa.PublicKey: + pubKey, err := pkey.ECDH() + if err != nil { + return nil, err + } + + return pubKey, nil + } + + return nil, ErrNotPublicKey +} + +// ========================================== + +// 解析私钥 +func (this Ecdh) ParseECDHPrivateKeyFromPEM(key []byte) (crypto.PrivateKey, error) { + var err error + + // Parse PEM block + var block *pem.Block + if block, _ = pem.Decode(key); block == nil { + return nil, ErrKeyMustBePEMEncoded + } + // Parse the key var parsedKey any if parsedKey, err = cryptobin_ecdh.ParsePrivateKey(block.Bytes); err != nil { @@ -34,6 +139,7 @@ func (this Ecdh) ParsePrivateKeyFromPEM(key []byte) (crypto.PrivateKey, error) { var pkey *ecdh.PrivateKey var ok bool + if pkey, ok = parsedKey.(*ecdh.PrivateKey); !ok { return nil, ErrNotPrivateKey } @@ -42,7 +148,7 @@ func (this Ecdh) ParsePrivateKeyFromPEM(key []byte) (crypto.PrivateKey, error) { } // 解析私钥带密码 -func (this Ecdh) ParsePrivateKeyFromPEMWithPassword(key []byte, password string) (crypto.PrivateKey, error) { +func (this Ecdh) ParseECDHPrivateKeyFromPEMWithPassword(key []byte, password string) (crypto.PrivateKey, error) { var err error // Parse PEM block @@ -63,6 +169,7 @@ func (this Ecdh) ParsePrivateKeyFromPEMWithPassword(key []byte, password string) var pkey *ecdh.PrivateKey var ok bool + if pkey, ok = parsedKey.(*ecdh.PrivateKey); !ok { return nil, ErrNotPrivateKey } @@ -71,7 +178,7 @@ func (this Ecdh) ParsePrivateKeyFromPEMWithPassword(key []byte, password string) } // 解析公钥 -func (this Ecdh) ParsePublicKeyFromPEM(key []byte) (crypto.PublicKey, error) { +func (this Ecdh) ParseECDHPublicKeyFromPEM(key []byte) (crypto.PublicKey, error) { var err error // Parse PEM block @@ -88,6 +195,7 @@ func (this Ecdh) ParsePublicKeyFromPEM(key []byte) (crypto.PublicKey, error) { var pkey *ecdh.PublicKey var ok bool + if pkey, ok = parsedKey.(*ecdh.PublicKey); !ok { return nil, ErrNotPublicKey } diff --git a/pkg/lakego-pkg/go-cryptobin/cryptobin/elgamal/check.go b/pkg/lakego-pkg/go-cryptobin/cryptobin/elgamal/check.go new file mode 100644 index 00000000..8aa1f2b8 --- /dev/null +++ b/pkg/lakego-pkg/go-cryptobin/cryptobin/elgamal/check.go @@ -0,0 +1,22 @@ +package elgamal + +// 检测公钥私钥是否匹配 +func (this EIGamal) CheckKeyPair() bool { + // 私钥导出的公钥 + pubKeyFromPriKey := this.MakePublicKey(). + CreatePublicKey(). + ToKeyString() + + // 公钥数据 + pubKeyFromPubKey := this.CreatePublicKey().ToKeyString() + + if pubKeyFromPriKey == "" || pubKeyFromPubKey == "" { + return false + } + + if pubKeyFromPriKey == pubKeyFromPubKey { + return true + } + + return false +} diff --git a/pkg/lakego-pkg/go-cryptobin/cryptobin/elgamal/create.go b/pkg/lakego-pkg/go-cryptobin/cryptobin/elgamal/create.go new file mode 100644 index 00000000..bd9d16a0 --- /dev/null +++ b/pkg/lakego-pkg/go-cryptobin/cryptobin/elgamal/create.go @@ -0,0 +1,257 @@ +package elgamal + +import ( + "errors" + "crypto/rand" + "crypto/x509" + "encoding/pem" + + cryptobin_tool "github.com/deatil/go-cryptobin/tool" + cryptobin_elgamal "github.com/deatil/go-cryptobin/elgamal" + cryptobin_pkcs8 "github.com/deatil/go-cryptobin/pkcs8" + cryptobin_pkcs8s "github.com/deatil/go-cryptobin/pkcs8s" +) + +type ( + // 配置 + Opts = cryptobin_pkcs8.Opts + // PBKDF2 配置 + PBKDF2Opts = cryptobin_pkcs8.PBKDF2Opts + // Scrypt 配置 + ScryptOpts = cryptobin_pkcs8.ScryptOpts +) + +var ( + // 获取 Cipher 类型 + GetCipherFromName = cryptobin_pkcs8.GetCipherFromName + // 获取 hash 类型 + GetHashFromName = cryptobin_pkcs8.GetHashFromName +) + +// 生成私钥 pem 数据 +// elgamal := New().GenerateKey("L2048N256") +// priKey := elgamal.CreatePrivateKey().ToKeyString() +func (this EIGamal) CreatePrivateKey() EIGamal { + return this.CreatePKCS1PrivateKey() +} + +// 生成私钥带密码 pem 数据 +// CreatePrivateKeyWithPassword("123", "AES256CBC") +// PEMCipher: DESCBC | DESEDE3CBC | AES128CBC | AES192CBC | AES256CBC +func (this EIGamal) CreatePrivateKeyWithPassword(password string, opts ...string) EIGamal { + return this.CreatePKCS1PrivateKeyWithPassword(password, opts...) +} + +// 生成公钥 pem 数据 +func (this EIGamal) CreatePublicKey() EIGamal { + return this.CreatePKCS1PublicKey() +} + +// ========== + +// 生成 pkcs1 私钥 pem 数据 +func (this EIGamal) CreatePKCS1PrivateKey() EIGamal { + if this.privateKey == nil { + err := errors.New("elgamal: privateKey error.") + return this.AppendError(err) + } + + privateKeyBytes, err := cryptobin_elgamal.MarshalPKCS1PrivateKey(this.privateKey) + if err != nil { + return this.AppendError(err) + } + + privateBlock := &pem.Block{ + Type: "EIGamal PRIVATE KEY", + Bytes: privateKeyBytes, + } + + this.keyData = pem.EncodeToMemory(privateBlock) + + return this +} + +// 生成 pkcs1 私钥带密码 pem 数据 +// CreatePKCS1PrivateKeyWithPassword("123", "AES256CBC") +// PEMCipher: DESCBC | DESEDE3CBC | AES128CBC | AES192CBC | AES256CBC +func (this EIGamal) CreatePKCS1PrivateKeyWithPassword(password string, opts ...string) EIGamal { + if this.privateKey == nil { + err := errors.New("elgamal: privateKey error.") + return this.AppendError(err) + } + + opt := "AES256CBC" + if len(opts) > 0 { + opt = opts[0] + } + + // 加密方式 + cipher, err := cryptobin_tool.GetPEMCipher(opt) + if err != nil { + err := errors.New("elgamal: PEMCipher not exists.") + return this.AppendError(err) + } + + // 生成私钥 + x509PrivateKey, err := cryptobin_elgamal.MarshalPKCS1PrivateKey(this.privateKey) + if err != nil { + return this.AppendError(err) + } + + // 生成加密数据 + privateBlock, err := x509.EncryptPEMBlock( + rand.Reader, + "EIGamal PRIVATE KEY", + x509PrivateKey, + []byte(password), + cipher, + ) + if err != nil { + return this.AppendError(err) + } + + this.keyData = pem.EncodeToMemory(privateBlock) + + return this +} + +// 生成 pkcs1 公钥 pem 数据 +func (this EIGamal) CreatePKCS1PublicKey() EIGamal { + if this.publicKey == nil { + err := errors.New("elgamal: publicKey error.") + return this.AppendError(err) + } + + publicKeyBytes, err := cryptobin_elgamal.MarshalPKCS1PublicKey(this.publicKey) + if err != nil { + return this.AppendError(err) + } + + publicBlock := &pem.Block{ + Type: "EIGamal PUBLIC KEY", + Bytes: publicKeyBytes, + } + + this.keyData = pem.EncodeToMemory(publicBlock) + + return this +} + +// ========== + +// 生成 pkcs8 私钥 pem 数据 +func (this EIGamal) CreatePKCS8PrivateKey() EIGamal { + if this.privateKey == nil { + err := errors.New("elgamal: privateKey error.") + return this.AppendError(err) + } + + privateKeyBytes, err := cryptobin_elgamal.MarshalPKCS8PrivateKey(this.privateKey) + if err != nil { + return this.AppendError(err) + } + + privateBlock := &pem.Block{ + Type: "PRIVATE KEY", + Bytes: privateKeyBytes, + } + + this.keyData = pem.EncodeToMemory(privateBlock) + + return this +} + +// 生成 PKCS8 私钥带密码 pem 数据 +// CreatePKCS8PrivateKeyWithPassword("123", "AES256CBC", "SHA256") +func (this EIGamal) CreatePKCS8PrivateKeyWithPassword(password string, opts ...any) EIGamal { + if this.privateKey == nil { + err := errors.New("elgamal: privateKey error.") + return this.AppendError(err) + } + + opt, err := cryptobin_pkcs8s.ParseOpts(opts...) + if err != nil { + return this.AppendError(err) + } + + // 生成私钥 + x509PrivateKey, err := cryptobin_elgamal.MarshalPKCS8PrivateKey(this.privateKey) + if err != nil { + return this.AppendError(err) + } + + // 生成加密数据 + privateBlock, err := cryptobin_pkcs8s.EncryptPEMBlock( + rand.Reader, + "ENCRYPTED PRIVATE KEY", + x509PrivateKey, + []byte(password), + opt, + ) + if err != nil { + return this.AppendError(err) + } + + this.keyData = pem.EncodeToMemory(privateBlock) + + return this +} + +// 生成公钥 pem 数据 +func (this EIGamal) CreatePKCS8PublicKey() EIGamal { + if this.publicKey == nil { + err := errors.New("elgamal: publicKey error.") + return this.AppendError(err) + } + + publicKeyBytes, err := cryptobin_elgamal.MarshalPKCS8PublicKey(this.publicKey) + if err != nil { + return this.AppendError(err) + } + + publicBlock := &pem.Block{ + Type: "PUBLIC KEY", + Bytes: publicKeyBytes, + } + + this.keyData = pem.EncodeToMemory(publicBlock) + + return this +} + +// ==================== + +// 生成私钥 xml 数据 +func (this EIGamal) CreateXMLPrivateKey() EIGamal { + if this.privateKey == nil { + err := errors.New("EIGamal: privateKey error.") + return this.AppendError(err) + } + + xmlPrivateKey, err := cryptobin_elgamal.MarshalXMLPrivateKey(this.privateKey) + if err != nil { + return this.AppendError(err) + } + + this.keyData = xmlPrivateKey + + return this +} + +// 生成公钥 xml 数据 +func (this EIGamal) CreateXMLPublicKey() EIGamal { + if this.publicKey == nil { + err := errors.New("EIGamal: publicKey error.") + return this.AppendError(err) + } + + xmlPublicKey, err := cryptobin_elgamal.MarshalXMLPublicKey(this.publicKey) + if err != nil { + return this.AppendError(err) + } + + this.keyData = xmlPublicKey + + return this +} + diff --git a/pkg/lakego-pkg/go-cryptobin/cryptobin/elgamal/elgamal.go b/pkg/lakego-pkg/go-cryptobin/cryptobin/elgamal/elgamal.go new file mode 100644 index 00000000..86b35533 --- /dev/null +++ b/pkg/lakego-pkg/go-cryptobin/cryptobin/elgamal/elgamal.go @@ -0,0 +1,64 @@ +package elgamal + +import ( + "hash" + "crypto/sha256" + + "github.com/deatil/go-cryptobin/elgamal" +) + +type ( + // HashFunc + HashFunc = func() hash.Hash +) + +/** + * EIGamal + * + * @create 2023-6-22 + * @author deatil + */ +type EIGamal struct { + // 私钥 + privateKey *elgamal.PrivateKey + + // 公钥 + publicKey *elgamal.PublicKey + + // 签名验证类型 + signHash HashFunc + + // [私钥/公钥]数据 + keyData []byte + + // 传入数据 + data []byte + + // 解析后的数据 + paredData []byte + + // 验证结果 + verify bool + + // 错误 + Errors []error +} + +// 构造函数 +func NewEIGamal() EIGamal { + return EIGamal{ + signHash: sha256.New, + verify: false, + Errors: make([]error, 0), + } +} + +// 构造函数 +func New() EIGamal { + return NewEIGamal() +} + +var ( + // 默认 + defaultEIGamal = NewEIGamal() +) diff --git a/pkg/lakego-pkg/go-cryptobin/cryptobin/elgamal/elgamal_test.go b/pkg/lakego-pkg/go-cryptobin/cryptobin/elgamal/elgamal_test.go new file mode 100644 index 00000000..a931d70c --- /dev/null +++ b/pkg/lakego-pkg/go-cryptobin/cryptobin/elgamal/elgamal_test.go @@ -0,0 +1,348 @@ +package elgamal + +import ( + "testing" + + cryptobin_test "github.com/deatil/go-cryptobin/tool/test" +) + +var ( + prikeyXML = ` + + vG406oGr5OqG0mMOtq5wWo/aGWWE8EPiPl09/I+ySxs= +

9W35RbKvFgfHndG9wVvFDMDw86BClpDk6kdeGr1ygLc=

+ 120jHKCdPWjLGrqH3HiCZ2GezWyEjfEIPBMhULymfzM= + BjtroR34tS5cvF5YNJaxmOjGDas43wKFunHCYS4P6CQ= +
+ ` + pubkeyXML = ` + + vG406oGr5OqG0mMOtq5wWo/aGWWE8EPiPl09/I+ySxs= +

9W35RbKvFgfHndG9wVvFDMDw86BClpDk6kdeGr1ygLc=

+ 120jHKCdPWjLGrqH3HiCZ2GezWyEjfEIPBMhULymfzM= +
+ ` +) + +func Test_XMLSign(t *testing.T) { + assertEmpty := cryptobin_test.AssertEmptyT(t) + assertBool := cryptobin_test.AssertBoolT(t) + assertError := cryptobin_test.AssertErrorT(t) + + data := "test-pass" + + // 签名 + objSign := New(). + FromString(data). + FromXMLPrivateKey([]byte(prikeyXML)). + Sign() + signed := objSign.ToBase64String() + + assertError(objSign.Error(), "XMLSign2-Sign") + assertEmpty(signed, "XMLSign2-Sign") + + // 验证 + objVerify := New(). + FromBase64String(signed). + FromXMLPublicKey([]byte(pubkeyXML)). + Verify([]byte(data)) + + assertError(objVerify.Error(), "XMLSign2-Verify") + assertBool(objVerify.ToVerify(), "XMLSign2-Verify") +} + +func Test_Encrypt(t *testing.T) { + assertEqual := cryptobin_test.AssertEqualT(t) + assertEmpty := cryptobin_test.AssertEmptyT(t) + assertError := cryptobin_test.AssertErrorT(t) + + data := "123tesfd!df" + + objEn := New(). + FromString(data). + FromXMLPublicKey([]byte(pubkeyXML)). + Encrypt() + enData := objEn.ToBase64String() + + assertError(objEn.Error(), "Encrypt-Encrypt") + assertEmpty(enData, "Encrypt-Encrypt") + + objDe := New(). + FromBase64String(enData). + FromXMLPrivateKey([]byte(prikeyXML)). + Decrypt() + deData := objDe.ToString() + + assertError(objDe.Error(), "Encrypt-Decrypt") + assertEmpty(deData, "Encrypt-Decrypt") + + assertEqual(data, deData, "Encrypt-Dedata") +} + +var testBitsize = 256 +var testProbability = 64 + +func Test_GenerateKeyPKCS1(t *testing.T) { + assertEmpty := cryptobin_test.AssertEmptyT(t) + assertError := cryptobin_test.AssertErrorT(t) + + obj := New().GenerateKey(testBitsize, testProbability) + assertError(obj.Error(), "GenerateKey-Error") + + pri := obj.CreatePKCS1PrivateKey().ToKeyString() + priPass := obj.CreatePKCS1PrivateKeyWithPassword("123").ToKeyString() + pub := obj.CreatePKCS1PublicKey().ToKeyString() + + assertEmpty(pri, "GenerateKey-pri") + assertEmpty(priPass, "GenerateKey-pri") + assertEmpty(pub, "GenerateKey-pub") + + pri2 := obj.CreatePKCS1PrivateKey().ToKeyString() + priPass2 := obj.CreatePKCS1PrivateKeyWithPassword("123", "DESEDE3CBC").ToKeyString() + pub2 := obj.CreatePKCS1PublicKey().ToKeyString() + + assertEmpty(pri2, "GenerateKey-pri") + assertEmpty(priPass2, "GenerateKey-pri") + assertEmpty(pub2, "GenerateKey-pub") +} + +func Test_GenerateKeyPKCS8(t *testing.T) { + assertEmpty := cryptobin_test.AssertEmptyT(t) + assertError := cryptobin_test.AssertErrorT(t) + + obj := New().GenerateKey(testBitsize, testProbability) + assertError(obj.Error(), "GenerateKey-Error") + + pri := obj.CreatePKCS8PrivateKey().ToKeyString() + priPass := obj.CreatePKCS8PrivateKeyWithPassword("123").ToKeyString() + pub := obj.CreatePKCS8PublicKey().ToKeyString() + + assertEmpty(pri, "GenerateKey-pri") + assertEmpty(priPass, "GenerateKey-pri") + assertEmpty(pub, "GenerateKey-pub") + + pri2 := obj.CreatePKCS8PrivateKey().ToKeyString() + priPass2 := obj.CreatePKCS8PrivateKeyWithPassword("123", "AES256CBC", "SHA256").ToKeyString() + pub2 := obj.CreatePKCS8PublicKey().ToKeyString() + + assertEmpty(pri2, "GenerateKey-pri") + assertEmpty(priPass2, "GenerateKey-pri") + assertEmpty(pub2, "GenerateKey-pub") +} + +var ( + pkcs1Prikey = ` +-----BEGIN EIGamal PRIVATE KEY----- +MIGOAgEAAiEAm3984TD8mLcbfO9yBS0JgETilmmzRU6G3a/Z7oGx2CsCIQDLNyir +IBznc0d8nECuJZ8x0x5ToZJ6cqqXobFnhewuIwIhAMXX2PFykwiY9uOBa3/9YpKj +WeePhxAvyHMFdJUTGhUDAiAJKb80n3yq8os1rof3ZrivBxJxMIKgDvBUHA2eSTXu +ZQ== +-----END EIGamal PRIVATE KEY----- + ` + pkcs1EnPrikey = ` +-----BEGIN EIGamal PRIVATE KEY----- +Proc-Type: 4,ENCRYPTED +DEK-Info: AES-256-CBC,cd748069ab721cdaf858332cf48fb445 + +zhkRM8PrHupD8IKRItv2AbDwzkgwqd8HFmFJvUcYKrDjFfkxl0JSEDh3LQYCIOf5 +iofXjTUjxJjzJNtua+8mKIJxKAMxP+zLz8Crirnmm06WL6orYDvAmi23LL0+nbuf +MmKXg7u8ErCOu8fye+5aG/iGNT+cO84PIUBCq6ruC9nBh9Xd+eFPrHyE2902eRRy +t7qNAufyCbFmZJP/WwZlOA== +-----END EIGamal PRIVATE KEY----- + ` + pkcs1Pubkey = ` +-----BEGIN EIGamal PUBLIC KEY----- +MGkCIQCbf3zhMPyYtxt873IFLQmAROKWabNFTobdr9nugbHYKwIhAMs3KKsgHOdz +R3ycQK4lnzHTHlOhknpyqpehsWeF7C4jAiEAxdfY8XKTCJj244Frf/1ikqNZ54+H +EC/IcwV0lRMaFQM= +-----END EIGamal PUBLIC KEY----- + ` +) + +var ( + pkcs8Prikey = ` +-----BEGIN PRIVATE KEY----- +MHsCAQAwCgYGKw4HAgEBBQAEajBoAiEAh26+uiviD9m/QEQE6KJO/BGL62/EDp5Q +4ruIcuWMrOwCIQDByRshe0Br4UkoJPLD0zoP3nYC9eR2u/CxtEJuDyp+TwIgGT4z +e6hUUi8Jx/r2uH0l/AaUN15pJpBcX2Xm0s8GLzo= +-----END PRIVATE KEY----- + ` + pkcs8EnPrikey = ` +-----BEGIN ENCRYPTED PRIVATE KEY----- +MIHWMFEGCSqGSIb3DQEFDTBEMCMGCSqGSIb3DQEFDDAWBBAm0cRa05+WN3aka1a5 +75p4AgInEDAdBglghkgBZQMEASoEEIhMsYKJQMAnawshtW8UdKAEgYCkfWdI/ZEC +BBe+mwSIvlIX7rkiQDoLhbSJaCuDpuhlCHKe/ALzK6lSpPr+5wnF0wttQdrsDdr5 +e6qb7UfGYLsKDfVqgOinMxvRRKsRVBZ28aWYD38u8ly2ZACg/4GjQ9x9dHsdOR/a +BmEHN8CnynXHRZXb+dOnCs22PoNuVEVwsQ== +-----END ENCRYPTED PRIVATE KEY----- + ` + pkcs8Pubkey = ` +-----BEGIN PUBLIC KEY----- +MHkwCgYGKw4HAgEBBQADawAwaAIhAIduvror4g/Zv0BEBOiiTvwRi+tvxA6eUOK7 +iHLljKzsAiEAwckbIXtAa+FJKCTyw9M6D952AvXkdrvwsbRCbg8qfk8CIB68rz21 +IQLlX/fsm/jmML/VbtGOKAGGHfAaosw7FAOw +-----END PUBLIC KEY----- + ` +) + +func Test_EncryptAsn1PKCS1(t *testing.T) { + assertEqual := cryptobin_test.AssertEqualT(t) + assertEmpty := cryptobin_test.AssertEmptyT(t) + assertError := cryptobin_test.AssertErrorT(t) + + data := "123tesfd!df" + + objEn := New(). + FromString(data). + FromPKCS1PublicKey([]byte(pkcs1Pubkey)). + Encrypt() + enData := objEn.ToBase64String() + + assertError(objEn.Error(), "Encrypt-Encrypt") + assertEmpty(enData, "Encrypt-Encrypt") + + objDe := New(). + FromBase64String(enData). + FromPKCS1PrivateKey([]byte(pkcs1Prikey)). + Decrypt() + deData := objDe.ToString() + + assertError(objDe.Error(), "Encrypt-Decrypt") + assertEmpty(deData, "Encrypt-Decrypt") + + assertEqual(data, deData, "Encrypt-Dedata") +} + +func Test_EncryptAsn1PKCS8(t *testing.T) { + assertEqual := cryptobin_test.AssertEqualT(t) + assertEmpty := cryptobin_test.AssertEmptyT(t) + assertError := cryptobin_test.AssertErrorT(t) + + data := "123tesfd!df" + + objEn := New(). + FromString(data). + FromPKCS8PublicKey([]byte(pkcs8Pubkey)). + Encrypt() + enData := objEn.ToBase64String() + + assertError(objEn.Error(), "Encrypt-Encrypt") + assertEmpty(enData, "Encrypt-Encrypt") + + objDe := New(). + FromBase64String(enData). + FromPKCS8PrivateKey([]byte(pkcs8Prikey)). + Decrypt() + deData := objDe.ToString() + + assertError(objDe.Error(), "Encrypt-Decrypt") + assertEmpty(deData, "Encrypt-Decrypt") + + assertEqual(data, deData, "Encrypt-Dedata") +} + +func Test_CheckKeyPair(t *testing.T) { + assertBool := cryptobin_test.AssertBoolT(t) + + objCheck1 := New(). + FromPKCS1PrivateKey([]byte(pkcs1Prikey)). + FromPKCS1PublicKey([]byte(pkcs1Pubkey)). + CheckKeyPair() + assertBool(objCheck1, "CheckKeyPair1") + + objCheck12 := New(). + FromPKCS1PrivateKeyWithPassword([]byte(pkcs1EnPrikey), "123"). + FromPKCS1PublicKey([]byte(pkcs1Pubkey)). + CheckKeyPair() + assertBool(objCheck12, "CheckKeyPair12") + + objCheck2 := New(). + FromPKCS8PrivateKey([]byte(pkcs8Prikey)). + FromPKCS8PublicKey([]byte(pkcs8Pubkey)). + CheckKeyPair() + assertBool(objCheck2, "CheckKeyPair2") + + objCheck22 := New(). + FromPKCS8PrivateKeyWithPassword([]byte(pkcs8EnPrikey), "123"). + FromPKCS8PublicKey([]byte(pkcs8Pubkey)). + CheckKeyPair() + assertBool(objCheck22, "CheckKeyPair22") +} + +func Test_EncryptAsn1_1(t *testing.T) { + assertEqual := cryptobin_test.AssertEqualT(t) + assertEmpty := cryptobin_test.AssertEmptyT(t) + assertError := cryptobin_test.AssertErrorT(t) + + data := "123tesfd!df" + + objEn := New(). + FromString(data). + FromPublicKey([]byte(pkcs1Pubkey)). + Encrypt() + enData := objEn.ToBase64String() + + assertError(objEn.Error(), "Encrypt-Encrypt") + assertEmpty(enData, "Encrypt-Encrypt") + + objDe := New(). + FromBase64String(enData). + FromPrivateKey([]byte(pkcs1Prikey)). + Decrypt() + deData := objDe.ToString() + + assertError(objDe.Error(), "Encrypt-Decrypt") + assertEmpty(deData, "Encrypt-Decrypt") + + assertEqual(data, deData, "Encrypt-Dedata") + + objDe2 := New(). + FromBase64String(enData). + FromPrivateKeyWithPassword([]byte(pkcs1EnPrikey), "123"). + Decrypt() + deData2 := objDe2.ToString() + + assertError(objDe2.Error(), "Encrypt-Decrypt") + assertEmpty(deData2, "Encrypt-Decrypt") + + assertEqual(data, deData2, "Encrypt-Dedata") +} + +func Test_EncryptAsn1_2(t *testing.T) { + assertEqual := cryptobin_test.AssertEqualT(t) + assertEmpty := cryptobin_test.AssertEmptyT(t) + assertError := cryptobin_test.AssertErrorT(t) + + data := "123tesfd!df" + + objEn := New(). + FromString(data). + FromPublicKey([]byte(pkcs8Pubkey)). + Encrypt() + enData := objEn.ToBase64String() + + assertError(objEn.Error(), "Encrypt-Encrypt") + assertEmpty(enData, "Encrypt-Encrypt") + + objDe := New(). + FromBase64String(enData). + FromPrivateKey([]byte(pkcs8Prikey)). + Decrypt() + deData := objDe.ToString() + + assertError(objDe.Error(), "Encrypt-Decrypt") + assertEmpty(deData, "Encrypt-Decrypt") + + assertEqual(data, deData, "Encrypt-Dedata") + + objDe2 := New(). + FromBase64String(enData). + FromPrivateKeyWithPassword([]byte(pkcs8EnPrikey), "123"). + Decrypt() + deData2 := objDe2.ToString() + + assertError(objDe2.Error(), "Encrypt-Decrypt") + assertEmpty(deData2, "Encrypt-Decrypt") + + assertEqual(data, deData2, "Encrypt-Dedata") +} diff --git a/pkg/lakego-pkg/go-cryptobin/cryptobin/elgamal/encryption.go b/pkg/lakego-pkg/go-cryptobin/cryptobin/elgamal/encryption.go new file mode 100644 index 00000000..01ef470a --- /dev/null +++ b/pkg/lakego-pkg/go-cryptobin/cryptobin/elgamal/encryption.go @@ -0,0 +1,40 @@ +package elgamal + +import ( + "errors" + "crypto/rand" +) + +// 公钥加密 +func (this EIGamal) Encrypt() EIGamal { + if this.publicKey == nil { + err := errors.New("EIGamal: publicKey error.") + return this.AppendError(err) + } + + paredData, err := this.publicKey.EncryptAsn1(rand.Reader, this.data) + if err != nil { + return this.AppendError(err) + } + + this.paredData = paredData + + return this +} + +// 私钥解密 +func (this EIGamal) Decrypt() EIGamal { + if this.privateKey == nil { + err := errors.New("EIGamal: privateKey error.") + return this.AppendError(err) + } + + paredData, err := this.privateKey.DecryptAsn1(this.data) + if err != nil { + return this.AppendError(err) + } + + this.paredData = paredData + + return this +} diff --git a/pkg/lakego-pkg/go-cryptobin/cryptobin/elgamal/error.go b/pkg/lakego-pkg/go-cryptobin/cryptobin/elgamal/error.go new file mode 100644 index 00000000..22df89b1 --- /dev/null +++ b/pkg/lakego-pkg/go-cryptobin/cryptobin/elgamal/error.go @@ -0,0 +1,17 @@ +package elgamal + +import ( + "github.com/deatil/go-cryptobin/tool" +) + +// 添加错误 +func (this EIGamal) AppendError(err ...error) EIGamal { + this.Errors = append(this.Errors, err...) + + return this +} + +// 获取错误 +func (this EIGamal) Error() error { + return tool.NewError(this.Errors...) +} diff --git a/pkg/lakego-pkg/go-cryptobin/cryptobin/elgamal/from.go b/pkg/lakego-pkg/go-cryptobin/cryptobin/elgamal/from.go new file mode 100644 index 00000000..9b1a6e88 --- /dev/null +++ b/pkg/lakego-pkg/go-cryptobin/cryptobin/elgamal/from.go @@ -0,0 +1,356 @@ +package elgamal + +import ( + "crypto/rand" + + cryptobin_tool "github.com/deatil/go-cryptobin/tool" + cryptobin_elgamal "github.com/deatil/go-cryptobin/elgamal" +) + +// 私钥 +func (this EIGamal) FromPrivateKey(key []byte) EIGamal { + parsedKey, err := this.ParsePKCS8PrivateKeyFromPEM(key) + if err == nil { + this.privateKey = parsedKey + + return this + } + + parsedKey, err = this.ParsePKCS1PrivateKeyFromPEM(key) + if err != nil { + return this.AppendError(err) + } + + this.privateKey = parsedKey + + return this +} + +// 私钥 +func FromPrivateKey(key []byte) EIGamal { + return defaultEIGamal.FromPrivateKey(key) +} + +// 私钥带密码 +func (this EIGamal) FromPrivateKeyWithPassword(key []byte, password string) EIGamal { + parsedKey, err := this.ParsePKCS8PrivateKeyFromPEMWithPassword(key, password) + if err == nil { + this.privateKey = parsedKey + + return this + } + + parsedKey, err = this.ParsePKCS1PrivateKeyFromPEMWithPassword(key, password) + if err != nil { + return this.AppendError(err) + } + + this.privateKey = parsedKey + + return this +} + +// 私钥带密码 +func FromPrivateKeyWithPassword(key []byte, password string) EIGamal { + return defaultEIGamal.FromPrivateKeyWithPassword(key, password) +} + +// 公钥 +func (this EIGamal) FromPublicKey(key []byte) EIGamal { + parsedKey, err := this.ParsePKCS8PublicKeyFromPEM(key) + if err == nil { + this.publicKey = parsedKey + + return this + } + + parsedKey, err = this.ParsePKCS1PublicKeyFromPEM(key) + if err != nil { + return this.AppendError(err) + } + + this.publicKey = parsedKey + + return this +} + +// 公钥 +func FromPublicKey(key []byte) EIGamal { + return defaultEIGamal.FromPublicKey(key) +} + +// ========== + +// 生成密钥 +func (this EIGamal) GenerateKey(bitsize, probability int) EIGamal { + priv, err := cryptobin_elgamal.GenerateKey(rand.Reader, bitsize, probability) + if err != nil { + return this.AppendError(err) + } + + this.privateKey = priv + this.publicKey = &priv.PublicKey + + return this +} + +// 生成密钥 +func GenerateKey(bitsize, probability int) EIGamal { + return defaultEIGamal.GenerateKey(bitsize, probability) +} + +// ========== + +// PKCS1 私钥 +func (this EIGamal) FromPKCS1PrivateKey(key []byte) EIGamal { + parsedKey, err := this.ParsePKCS1PrivateKeyFromPEM(key) + if err != nil { + return this.AppendError(err) + } + + this.privateKey = parsedKey + + return this +} + +// PKCS1 私钥 +func FromPKCS1PrivateKey(key []byte) EIGamal { + return defaultEIGamal.FromPKCS1PrivateKey(key) +} + +// PKCS1 私钥带密码 +func (this EIGamal) FromPKCS1PrivateKeyWithPassword(key []byte, password string) EIGamal { + parsedKey, err := this.ParsePKCS1PrivateKeyFromPEMWithPassword(key, password) + if err != nil { + return this.AppendError(err) + } + + this.privateKey = parsedKey + + return this +} + +// PKCS1 私钥带密码 +func FromPKCS1PrivateKeyWithPassword(key []byte, password string) EIGamal { + return defaultEIGamal.FromPKCS1PrivateKeyWithPassword(key, password) +} + +// PKCS1 公钥 +func (this EIGamal) FromPKCS1PublicKey(key []byte) EIGamal { + parsedKey, err := this.ParsePKCS1PublicKeyFromPEM(key) + if err != nil { + return this.AppendError(err) + } + + this.publicKey = parsedKey + + return this +} + +// PKCS1 公钥 +func FromPKCS1PublicKey(key []byte) EIGamal { + return defaultEIGamal.FromPKCS1PublicKey(key) +} + +// ========== + +// PKCS8 私钥 +func (this EIGamal) FromPKCS8PrivateKey(key []byte) EIGamal { + parsedKey, err := this.ParsePKCS8PrivateKeyFromPEM(key) + if err != nil { + return this.AppendError(err) + } + + this.privateKey = parsedKey + + return this +} + +// PKCS8 私钥 +func FromPKCS8PrivateKey(key []byte) EIGamal { + return defaultEIGamal.FromPKCS8PrivateKey(key) +} + +// PKCS8 私钥带密码 +func (this EIGamal) FromPKCS8PrivateKeyWithPassword(key []byte, password string) EIGamal { + parsedKey, err := this.ParsePKCS8PrivateKeyFromPEMWithPassword(key, password) + if err != nil { + return this.AppendError(err) + } + + this.privateKey = parsedKey + + return this +} + +// PKCS8 私钥带密码 +func FromPKCS8PrivateKeyWithPassword(key []byte, password string) EIGamal { + return defaultEIGamal.FromPKCS8PrivateKeyWithPassword(key, password) +} + +// PKCS8 公钥 +func (this EIGamal) FromPKCS8PublicKey(key []byte) EIGamal { + parsedKey, err := this.ParsePKCS8PublicKeyFromPEM(key) + if err != nil { + return this.AppendError(err) + } + + this.publicKey = parsedKey + + return this +} + +// PKCS8 公钥 +func FromPKCS8PublicKey(key []byte) EIGamal { + return defaultEIGamal.FromPKCS8PublicKey(key) +} + +// ========== + +// Pkcs1 DER 私钥 +func (this EIGamal) FromPKCS1PrivateKeyDer(der []byte) EIGamal { + key := cryptobin_tool.EncodeDerToPem(der, "EIGamal PRIVATE KEY") + + parsedKey, err := this.ParsePKCS1PrivateKeyFromPEM(key) + if err != nil { + return this.AppendError(err) + } + + this.privateKey = parsedKey + + return this +} + +// PKCS1 DER 公钥 +func (this EIGamal) FromPKCS1PublicKeyDer(der []byte) EIGamal { + key := cryptobin_tool.EncodeDerToPem(der, "EIGamal PUBLIC KEY") + + parsedKey, err := this.ParsePKCS1PublicKeyFromPEM(key) + if err != nil { + return this.AppendError(err) + } + + this.publicKey = parsedKey + + return this +} + +// ========== + +// Pkcs8 DER 私钥 +func (this EIGamal) FromPKCS8PrivateKeyDer(der []byte) EIGamal { + key := cryptobin_tool.EncodeDerToPem(der, "PRIVATE KEY") + + parsedKey, err := this.ParsePKCS8PrivateKeyFromPEM(key) + if err != nil { + return this.AppendError(err) + } + + this.privateKey = parsedKey + + return this +} + +// PKCS8 DER 公钥 +func (this EIGamal) FromPKCS8PublicKeyDer(der []byte) EIGamal { + key := cryptobin_tool.EncodeDerToPem(der, "PUBLIC KEY") + + parsedKey, err := this.ParsePKCS8PublicKeyFromPEM(key) + if err != nil { + return this.AppendError(err) + } + + this.publicKey = parsedKey + + return this +} + +// ========== + +// XML 私钥 +func (this EIGamal) FromXMLPrivateKey(key []byte) EIGamal { + privateKey, err := this.ParsePrivateKeyFromXML(key) + if err != nil { + return this.AppendError(err) + } + + this.privateKey = privateKey + + return this +} + +// XML 私钥 +func FromXMLPrivateKey(key []byte) EIGamal { + return defaultEIGamal.FromXMLPrivateKey(key) +} + +// XML 公钥 +func (this EIGamal) FromXMLPublicKey(key []byte) EIGamal { + publicKey, err := this.ParsePublicKeyFromXML(key) + if err != nil { + return this.AppendError(err) + } + + this.publicKey = publicKey + + return this +} + +// XML 公钥 +func FromXMLPublicKey(key []byte) EIGamal { + return defaultEIGamal.FromXMLPublicKey(key) +} + +// ========== + +// 字节 +func (this EIGamal) FromBytes(data []byte) EIGamal { + this.data = data + + return this +} + +// 字节 +func FromBytes(data []byte) EIGamal { + return defaultEIGamal.FromBytes(data) +} + +// 字符 +func (this EIGamal) FromString(data string) EIGamal { + this.data = []byte(data) + + return this +} + +// 字符 +func FromString(data string) EIGamal { + return defaultEIGamal.FromString(data) +} + +// Base64 +func (this EIGamal) FromBase64String(data string) EIGamal { + newData, err := cryptobin_tool.NewEncoding().Base64Decode(data) + + this.data = newData + + return this.AppendError(err) +} + +// Base64 +func FromBase64String(data string) EIGamal { + return defaultEIGamal.FromBase64String(data) +} + +// Hex +func (this EIGamal) FromHexString(data string) EIGamal { + newData, err := cryptobin_tool.NewEncoding().HexDecode(data) + + this.data = newData + + return this.AppendError(err) +} + +// Hex +func FromHexString(data string) EIGamal { + return defaultEIGamal.FromHexString(data) +} diff --git a/pkg/lakego-pkg/go-cryptobin/cryptobin/elgamal/get.go b/pkg/lakego-pkg/go-cryptobin/cryptobin/elgamal/get.go new file mode 100644 index 00000000..43f2a1d5 --- /dev/null +++ b/pkg/lakego-pkg/go-cryptobin/cryptobin/elgamal/get.go @@ -0,0 +1,45 @@ +package elgamal + +import ( + "github.com/deatil/go-cryptobin/elgamal" +) + +// 获取 PrivateKey +func (this EIGamal) GetPrivateKey() *elgamal.PrivateKey { + return this.privateKey +} + +// 获取 PublicKey +func (this EIGamal) GetPublicKey() *elgamal.PublicKey { + return this.publicKey +} + +// 获取 hash 类型 +func (this EIGamal) GetSignHash() HashFunc { + return this.signHash +} + +// 获取 keyData +func (this EIGamal) GetKeyData() []byte { + return this.keyData +} + +// 获取 data +func (this EIGamal) GetData() []byte { + return this.data +} + +// 获取 paredData +func (this EIGamal) GetParedData() []byte { + return this.paredData +} + +// 获取验证后情况 +func (this EIGamal) GetVerify() bool { + return this.verify +} + +// 获取错误 +func (this EIGamal) GetErrors() []error { + return this.Errors +} diff --git a/pkg/lakego-pkg/go-cryptobin/cryptobin/elgamal/make.go b/pkg/lakego-pkg/go-cryptobin/cryptobin/elgamal/make.go new file mode 100644 index 00000000..cb5effea --- /dev/null +++ b/pkg/lakego-pkg/go-cryptobin/cryptobin/elgamal/make.go @@ -0,0 +1,34 @@ +package elgamal + +import ( + "errors" + "encoding/pem" +) + +// 生成公钥 +func (this EIGamal) MakePublicKey() EIGamal { + this.publicKey = nil + + if this.privateKey == nil { + err := errors.New("EIGamal: privateKey error.") + return this.AppendError(err) + } + + // 导出公钥 + this.publicKey = &this.privateKey.PublicKey + + return this +} + +// 生成密钥 der 数据 +func (this EIGamal) MakeKeyDer() EIGamal { + var block *pem.Block + if block, _ = pem.Decode(this.keyData); block == nil { + err := errors.New("EIGamal: keyData error.") + return this.AppendError(err) + } + + this.keyData = block.Bytes + + return this +} diff --git a/pkg/lakego-pkg/go-cryptobin/cryptobin/elgamal/on.go b/pkg/lakego-pkg/go-cryptobin/cryptobin/elgamal/on.go new file mode 100644 index 00000000..ad9fd7da --- /dev/null +++ b/pkg/lakego-pkg/go-cryptobin/cryptobin/elgamal/on.go @@ -0,0 +1,14 @@ +package elgamal + +type ( + // 错误方法 + DSAErrorFunc = func([]error) +) + +// 引出错误信息 +func (this EIGamal) OnError(fn DSAErrorFunc) EIGamal { + fn(this.Errors) + + return this +} + diff --git a/pkg/lakego-pkg/go-cryptobin/cryptobin/elgamal/parse.go b/pkg/lakego-pkg/go-cryptobin/cryptobin/elgamal/parse.go new file mode 100644 index 00000000..7e1e303a --- /dev/null +++ b/pkg/lakego-pkg/go-cryptobin/cryptobin/elgamal/parse.go @@ -0,0 +1,194 @@ +package elgamal + +import ( + "errors" + "crypto/x509" + "encoding/pem" + + "github.com/deatil/go-cryptobin/elgamal" + cryptobin_pkcs8s "github.com/deatil/go-cryptobin/pkcs8s" +) + +var ( + ErrKeyMustBePEMEncoded = errors.New("invalid key: Key must be a PEM encoded PKCS1 or PKCS8 key") + ErrNotEIGamalPrivateKey = errors.New("key is not a valid EIGamal private key") + ErrNotEIGamalPublicKey = errors.New("key is not a valid EIGamal public key") +) + +// 解析私钥 +func (this EIGamal) ParsePKCS1PrivateKeyFromPEM(key []byte) (*elgamal.PrivateKey, error) { + var err error + + // Parse PEM block + var block *pem.Block + if block, _ = pem.Decode(key); block == nil { + return nil, ErrKeyMustBePEMEncoded + } + + // Parse the key + var parsedKey any + if parsedKey, err = elgamal.ParsePKCS1PrivateKey(block.Bytes); err != nil { + return nil, err + } + + var pkey *elgamal.PrivateKey + var ok bool + if pkey, ok = parsedKey.(*elgamal.PrivateKey); !ok { + return nil, ErrNotEIGamalPrivateKey + } + + return pkey, nil +} + +// 解析私钥带密码 +func (this EIGamal) ParsePKCS1PrivateKeyFromPEMWithPassword(key []byte, password string) (*elgamal.PrivateKey, error) { + var err error + + // Parse PEM block + var block *pem.Block + if block, _ = pem.Decode(key); block == nil { + return nil, ErrKeyMustBePEMEncoded + } + + var blockDecrypted []byte + if blockDecrypted, err = x509.DecryptPEMBlock(block, []byte(password)); err != nil { + return nil, err + } + + // Parse the key + var parsedKey any + if parsedKey, err = elgamal.ParsePKCS1PrivateKey(blockDecrypted); err != nil { + return nil, err + } + + var pkey *elgamal.PrivateKey + var ok bool + if pkey, ok = parsedKey.(*elgamal.PrivateKey); !ok { + return nil, ErrNotEIGamalPrivateKey + } + + return pkey, nil +} + +// 解析公钥 +func (this EIGamal) ParsePKCS1PublicKeyFromPEM(key []byte) (*elgamal.PublicKey, error) { + var err error + + // Parse PEM block + var block *pem.Block + if block, _ = pem.Decode(key); block == nil { + return nil, ErrKeyMustBePEMEncoded + } + + // Parse the key + var parsedKey any + if parsedKey, err = elgamal.ParsePKCS1PublicKey(block.Bytes); err != nil { + return nil, err + } + + var pkey *elgamal.PublicKey + var ok bool + + if pkey, ok = parsedKey.(*elgamal.PublicKey); !ok { + return nil, ErrNotEIGamalPublicKey + } + + return pkey, nil +} + +// ============= + + +// 解析私钥 PKCS8 +func (this EIGamal) ParsePKCS8PrivateKeyFromPEM(key []byte) (*elgamal.PrivateKey, error) { + var err error + + // Parse PEM block + var block *pem.Block + if block, _ = pem.Decode(key); block == nil { + return nil, ErrKeyMustBePEMEncoded + } + + // Parse the key + var parsedKey any + if parsedKey, err = elgamal.ParsePKCS8PrivateKey(block.Bytes); err != nil { + return nil, err + } + + var pkey *elgamal.PrivateKey + var ok bool + + if pkey, ok = parsedKey.(*elgamal.PrivateKey); !ok { + return nil, ErrNotEIGamalPrivateKey + } + + return pkey, nil +} + +// 解析 PKCS8 带密码的私钥 +func (this EIGamal) ParsePKCS8PrivateKeyFromPEMWithPassword(key []byte, password string) (*elgamal.PrivateKey, error) { + var err error + + // Parse PEM block + var block *pem.Block + if block, _ = pem.Decode(key); block == nil { + return nil, ErrKeyMustBePEMEncoded + } + + var blockDecrypted []byte + if blockDecrypted, err = cryptobin_pkcs8s.DecryptPEMBlock(block, []byte(password)); err != nil { + return nil, err + } + + var parsedKey any + if parsedKey, err = elgamal.ParsePKCS8PrivateKey(blockDecrypted); err != nil { + return nil, err + } + + var pkey *elgamal.PrivateKey + var ok bool + + if pkey, ok = parsedKey.(*elgamal.PrivateKey); !ok { + return nil, ErrNotEIGamalPrivateKey + } + + return pkey, nil +} + +// 解析公钥 PKCS8 +func (this EIGamal) ParsePKCS8PublicKeyFromPEM(key []byte) (*elgamal.PublicKey, error) { + var err error + + // Parse PEM block + var block *pem.Block + if block, _ = pem.Decode(key); block == nil { + return nil, ErrKeyMustBePEMEncoded + } + + // Parse the key + var parsedKey any + if parsedKey, err = elgamal.ParsePKCS8PublicKey(block.Bytes); err != nil { + return nil, err + } + + var pkey *elgamal.PublicKey + var ok bool + + if pkey, ok = parsedKey.(*elgamal.PublicKey); !ok { + return nil, ErrNotEIGamalPublicKey + } + + return pkey, nil +} + +// ============ + +// 解析 xml 私钥 +func (this EIGamal) ParsePrivateKeyFromXML(key []byte) (*elgamal.PrivateKey, error) { + return elgamal.ParseXMLPrivateKey(key) +} + +// 解析 xml 公钥 +func (this EIGamal) ParsePublicKeyFromXML(key []byte) (*elgamal.PublicKey, error) { + return elgamal.ParseXMLPublicKey(key) +} diff --git a/pkg/lakego-pkg/go-cryptobin/cryptobin/elgamal/sign.go b/pkg/lakego-pkg/go-cryptobin/cryptobin/elgamal/sign.go new file mode 100644 index 00000000..f06588b6 --- /dev/null +++ b/pkg/lakego-pkg/go-cryptobin/cryptobin/elgamal/sign.go @@ -0,0 +1,59 @@ +package elgamal + +import ( + "errors" + "crypto/rand" + + "github.com/deatil/go-cryptobin/elgamal" +) + +// 私钥签名 +func (this EIGamal) Sign() EIGamal { + if this.privateKey == nil { + err := errors.New("elgamal: privateKey error.") + return this.AppendError(err) + } + + hashed, err := this.DataHash(this.signHash, this.data) + if err != nil { + return this.AppendError(err) + } + + paredData, err := elgamal.SignASN1(rand.Reader, this.privateKey, hashed) + if err != nil { + return this.AppendError(err) + } + + this.paredData = paredData + + return this.AppendError(err) +} + +// 公钥验证 +// 使用原始数据[data]对比签名后数据 +func (this EIGamal) Verify(data []byte) EIGamal { + if this.publicKey == nil { + err := errors.New("elgamal: publicKey error.") + return this.AppendError(err) + } + + hashed, err := this.DataHash(this.signHash, data) + if err != nil { + return this.AppendError(err) + } + + this.verify, err = elgamal.VerifyASN1(this.publicKey, hashed, this.data) + if err != nil { + return this.AppendError(err) + } + + return this +} + +// 签名后数据 +func (this EIGamal) DataHash(fn HashFunc, data []byte) ([]byte, error) { + h := fn() + h.Write(data) + + return h.Sum(nil), nil +} diff --git a/pkg/lakego-pkg/go-cryptobin/cryptobin/elgamal/to.go b/pkg/lakego-pkg/go-cryptobin/cryptobin/elgamal/to.go new file mode 100644 index 00000000..26ab9d6e --- /dev/null +++ b/pkg/lakego-pkg/go-cryptobin/cryptobin/elgamal/to.go @@ -0,0 +1,53 @@ +package elgamal + +import ( + cryptobin_tool "github.com/deatil/go-cryptobin/tool" +) + +// 私钥/公钥 +func (this EIGamal) ToKeyBytes() []byte { + return this.keyData +} + +// 私钥/公钥 +func (this EIGamal) ToKeyString() string { + return string(this.keyData) +} + +// ========== + +// 输出字节 +func (this EIGamal) ToBytes() []byte { + return this.paredData +} + +// 输出字符 +func (this EIGamal) ToString() string { + return string(this.paredData) +} + +// 输出Base64 +func (this EIGamal) ToBase64String() string { + return cryptobin_tool.NewEncoding().Base64Encode(this.paredData) +} + +// 输出Hex +func (this EIGamal) ToHexString() string { + return cryptobin_tool.NewEncoding().HexEncode(this.paredData) +} + +// ========== + +// 验证结果 +func (this EIGamal) ToVerify() bool { + return this.verify +} + +// 验证结果,返回 int 类型 +func (this EIGamal) ToVerifyInt() int { + if this.verify { + return 1 + } + + return 0 +} diff --git a/pkg/lakego-pkg/go-cryptobin/cryptobin/elgamal/with.go b/pkg/lakego-pkg/go-cryptobin/cryptobin/elgamal/with.go new file mode 100644 index 00000000..5968cf79 --- /dev/null +++ b/pkg/lakego-pkg/go-cryptobin/cryptobin/elgamal/with.go @@ -0,0 +1,68 @@ +package elgamal + +import ( + "github.com/deatil/go-cryptobin/tool" + "github.com/deatil/go-cryptobin/elgamal" +) + +// 设置 PrivateKey +func (this EIGamal) WithPrivateKey(data *elgamal.PrivateKey) EIGamal { + this.privateKey = data + + return this +} + +// 设置 PublicKey +func (this EIGamal) WithPublicKey(data *elgamal.PublicKey) EIGamal { + this.publicKey = data + + return this +} + +// 设置 hash 类型 +func (this EIGamal) WithSignHash(data HashFunc) EIGamal { + this.signHash = data + + return this +} + +// 设置 hash 类型 +// 可用参数可查看 Hash 结构体数据 +func (this EIGamal) SetSignHash(data string) EIGamal { + hash, err := tool.GetHash(data) + if err != nil { + return this.AppendError(err) + } + + this.signHash = hash + + return this +} + +// 设置 data +func (this EIGamal) WithData(data []byte) EIGamal { + this.data = data + + return this +} + +// 设置 paredData +func (this EIGamal) WithParedData(data []byte) EIGamal { + this.paredData = data + + return this +} + +// 设置 verify +func (this EIGamal) WithVerify(data bool) EIGamal { + this.verify = data + + return this +} + +// 设置错误 +func (this EIGamal) WithErrors(errs []error) EIGamal { + this.Errors = errs + + return this +} diff --git a/pkg/lakego-pkg/go-cryptobin/dsa/xml.go b/pkg/lakego-pkg/go-cryptobin/dsa/xml.go index a20d66df..24697f49 100644 --- a/pkg/lakego-pkg/go-cryptobin/dsa/xml.go +++ b/pkg/lakego-pkg/go-cryptobin/dsa/xml.go @@ -10,21 +10,27 @@ import ( // 私钥 type xmlPrivateKey struct { - XMLName xml.Name `xml:"DSAKeyValue"` - P string `xml:"P"` - Q string `xml:"Q"` - G string `xml:"G"` - Y string `xml:"Y"` - X string `xml:"X"` + XMLName xml.Name `xml:"DSAKeyValue"` + P string `xml:"P"` + Q string `xml:"Q"` + G string `xml:"G"` + Y string `xml:"Y"` + J string `xml:"J,omitempty"` + Seed string `xml:"Seed,omitempty"` + PgenCounter string `xml:"PgenCounter,omitempty"` + X string `xml:"X"` } // 公钥 type xmlPublicKey struct { - XMLName xml.Name `xml:"DSAKeyValue"` - P string `xml:"P"` - Q string `xml:"Q"` - G string `xml:"G"` - Y string `xml:"Y"` + XMLName xml.Name `xml:"DSAKeyValue"` + P string `xml:"P"` + Q string `xml:"Q"` + G string `xml:"G"` + Y string `xml:"Y"` + J string `xml:"J,omitempty"` + Seed string `xml:"Seed,omitempty"` + PgenCounter string `xml:"PgenCounter,omitempty"` } var ( @@ -69,9 +75,9 @@ func MarshalXMLPublicKey(key *dsa.PublicKey) ([]byte, error) { } // 解析公钥 -func (this XMLKey) ParsePublicKey(der []byte) (*dsa.PublicKey, error) { +func (this XMLKey) ParsePublicKey(data []byte) (*dsa.PublicKey, error) { var pub xmlPublicKey - err := xml.Unmarshal(der, &pub) + err := xml.Unmarshal(data, &pub) if err != nil { return nil, err } @@ -137,9 +143,9 @@ func MarshalXMLPrivateKey(key *dsa.PrivateKey) ([]byte, error) { } // 解析私钥 -func (this XMLKey) ParsePrivateKey(der []byte) (*dsa.PrivateKey, error) { +func (this XMLKey) ParsePrivateKey(data []byte) (*dsa.PrivateKey, error) { var priv xmlPrivateKey - err := xml.Unmarshal(der, &priv) + err := xml.Unmarshal(data, &priv) if err != nil { return nil, err } diff --git a/pkg/lakego-pkg/go-cryptobin/dsa/xml_test.go b/pkg/lakego-pkg/go-cryptobin/dsa/xml_test.go new file mode 100644 index 00000000..ca8c67b8 --- /dev/null +++ b/pkg/lakego-pkg/go-cryptobin/dsa/xml_test.go @@ -0,0 +1,84 @@ +package dsa + +import ( + "testing" + + cryptobin_test "github.com/deatil/go-cryptobin/tool/test" +) + +var testPrivateKey = "

v9B+VOY0xg7z8tlT+7ALGLZDGCcKbheTYd7eIUfBmU8BloVn97DnU49HGWx/KIx5BRkf87JAtUBENQ+NEVoNRIvsowOuGY5biP5xdCOw+OuKEoVobgXM6cQ+FHhm7dKCOfKw4onlBZSbykyE/g3z9EJjlmSmMgB/yCGHSJSyOTU=

rpQWFUx8Y3w2CDXc3MVuP8ugjXk=JQZg0wH3U0zy1DvlVrL7BwveZncLNcVaxG8HvRKMHiON+w2BAzGM7xBnXe96nJ/BStvJFzJq6y29azD/kvC2U9/t2ALh8PanBhWlbmTIS6BopyJ0RxpuM5sRtTamXnLgI7y4B6rTfnFfFX7U8eldV6CRUmDifh0ZZxbD78/BFYo=R3busGxRGdW6lTbcGZAQlvkoLlxhIkQqPeLepBx4mHXcd6epC4IK/FuMOM0stLuwX5WFz15oxILoxZDfyO0WwiP0bjyJ9t7DrerYdH7JXhg1aeRzhKJFaQPojYkvla3cNDaoZPw25jQ8kEigvL69HDxFm1nPM076b9dBEV+yymc=AAAAARlGTpkJFSjk5pWsdf0YhNUiTmDoBG4wVsb1dU0vn/67Z+du7y8R4HfRwoAUvKBzNuclISy1Zx6y9XuyauAtgL315gpQwlbXkZfeTNVlNOajUDd0Y3g+MYuNZ2iYqbxIv4DDfvhcHm6sSX5Z1A==fpbN84HbBsVGshjSsj3Rl6iHPcY=Alo=q5FlYAqyvS2hR8UM3FUtrhF6ZqE=
"; +var testPublicKey = "

v9B+VOY0xg7z8tlT+7ALGLZDGCcKbheTYd7eIUfBmU8BloVn97DnU49HGWx/KIx5BRkf87JAtUBENQ+NEVoNRIvsowOuGY5biP5xdCOw+OuKEoVobgXM6cQ+FHhm7dKCOfKw4onlBZSbykyE/g3z9EJjlmSmMgB/yCGHSJSyOTU=

rpQWFUx8Y3w2CDXc3MVuP8ugjXk=JQZg0wH3U0zy1DvlVrL7BwveZncLNcVaxG8HvRKMHiON+w2BAzGM7xBnXe96nJ/BStvJFzJq6y29azD/kvC2U9/t2ALh8PanBhWlbmTIS6BopyJ0RxpuM5sRtTamXnLgI7y4B6rTfnFfFX7U8eldV6CRUmDifh0ZZxbD78/BFYo=R3busGxRGdW6lTbcGZAQlvkoLlxhIkQqPeLepBx4mHXcd6epC4IK/FuMOM0stLuwX5WFz15oxILoxZDfyO0WwiP0bjyJ9t7DrerYdH7JXhg1aeRzhKJFaQPojYkvla3cNDaoZPw25jQ8kEigvL69HDxFm1nPM076b9dBEV+yymc=AAAAARlGTpkJFSjk5pWsdf0YhNUiTmDoBG4wVsb1dU0vn/67Z+du7y8R4HfRwoAUvKBzNuclISy1Zx6y9XuyauAtgL315gpQwlbXkZfeTNVlNOajUDd0Y3g+MYuNZ2iYqbxIv4DDfvhcHm6sSX5Z1A==fpbN84HbBsVGshjSsj3Rl6iHPcY=Alo=
"; + +var testPrivateKeyCheck = ` +

v9B+VOY0xg7z8tlT+7ALGLZDGCcKbheTYd7eIUfBmU8BloVn97DnU49HGWx/KIx5BRkf87JAtUBENQ+NEVoNRIvsowOuGY5biP5xdCOw+OuKEoVobgXM6cQ+FHhm7dKCOfKw4onlBZSbykyE/g3z9EJjlmSmMgB/yCGHSJSyOTU=

+ rpQWFUx8Y3w2CDXc3MVuP8ugjXk= + JQZg0wH3U0zy1DvlVrL7BwveZncLNcVaxG8HvRKMHiON+w2BAzGM7xBnXe96nJ/BStvJFzJq6y29azD/kvC2U9/t2ALh8PanBhWlbmTIS6BopyJ0RxpuM5sRtTamXnLgI7y4B6rTfnFfFX7U8eldV6CRUmDifh0ZZxbD78/BFYo= + R3busGxRGdW6lTbcGZAQlvkoLlxhIkQqPeLepBx4mHXcd6epC4IK/FuMOM0stLuwX5WFz15oxILoxZDfyO0WwiP0bjyJ9t7DrerYdH7JXhg1aeRzhKJFaQPojYkvla3cNDaoZPw25jQ8kEigvL69HDxFm1nPM076b9dBEV+yymc= + q5FlYAqyvS2hR8UM3FUtrhF6ZqE= +
`; +var testPublicKeyCheck = ` +

v9B+VOY0xg7z8tlT+7ALGLZDGCcKbheTYd7eIUfBmU8BloVn97DnU49HGWx/KIx5BRkf87JAtUBENQ+NEVoNRIvsowOuGY5biP5xdCOw+OuKEoVobgXM6cQ+FHhm7dKCOfKw4onlBZSbykyE/g3z9EJjlmSmMgB/yCGHSJSyOTU=

+ rpQWFUx8Y3w2CDXc3MVuP8ugjXk= + JQZg0wH3U0zy1DvlVrL7BwveZncLNcVaxG8HvRKMHiON+w2BAzGM7xBnXe96nJ/BStvJFzJq6y29azD/kvC2U9/t2ALh8PanBhWlbmTIS6BopyJ0RxpuM5sRtTamXnLgI7y4B6rTfnFfFX7U8eldV6CRUmDifh0ZZxbD78/BFYo= + R3busGxRGdW6lTbcGZAQlvkoLlxhIkQqPeLepBx4mHXcd6epC4IK/FuMOM0stLuwX5WFz15oxILoxZDfyO0WwiP0bjyJ9t7DrerYdH7JXhg1aeRzhKJFaQPojYkvla3cNDaoZPw25jQ8kEigvL69HDxFm1nPM076b9dBEV+yymc= +
`; + +func Test_ParseAndMarshalPublicKey(t *testing.T) { + assertEqual := cryptobin_test.AssertEqualT(t) + assertError := cryptobin_test.AssertErrorT(t) + + testPub := []byte(testPublicKey) + pub, err1 := ParseXMLPublicKey(testPub) + + assertError(err1, "ParseAndMarshalPublicKey-Error") + + xmlPub, err2 := MarshalXMLPublicKey(pub) + assertError(err2, "ParseAndMarshalPublicKey-Error2") + + assertEqual(testPublicKeyCheck, string(xmlPub), "ParseAndMarshalPublicKey") +} + +func Test_ParseAndMarshalPublicKey2(t *testing.T) { + assertEqual := cryptobin_test.AssertEqualT(t) + assertError := cryptobin_test.AssertErrorT(t) + + testPub := []byte(testPublicKeyCheck) + pub, err1 := ParseXMLPublicKey(testPub) + + assertError(err1, "ParseAndMarshalPublicKey2-Error") + + xmlPub, err2 := MarshalXMLPublicKey(pub) + assertError(err2, "ParseAndMarshalPublicKey2-Error2") + + assertEqual(testPublicKeyCheck, string(xmlPub), "ParseAndMarshalPublicKey2") +} + +func Test_ParseAndMarshalPrivateKey(t *testing.T) { + assertEqual := cryptobin_test.AssertEqualT(t) + assertError := cryptobin_test.AssertErrorT(t) + + testPri := []byte(testPrivateKey) + pri, err1 := ParseXMLPrivateKey(testPri) + + assertError(err1, "ParseAndMarshalPrivateKey-Error") + + xmlPri, err2 := MarshalXMLPrivateKey(pri) + assertError(err2, "ParseAndMarshalPrivateKey-Error2") + + assertEqual(testPrivateKeyCheck, string(xmlPri), "ParseAndMarshalPrivateKey") +} + +func Test_ParseAndMarshalPrivateKey2(t *testing.T) { + assertEqual := cryptobin_test.AssertEqualT(t) + assertError := cryptobin_test.AssertErrorT(t) + + testPri := []byte(testPrivateKeyCheck) + pri, err1 := ParseXMLPrivateKey(testPri) + + assertError(err1, "ParseAndMarshalPrivateKey2-Error") + + xmlPri, err2 := MarshalXMLPrivateKey(pri) + assertError(err2, "ParseAndMarshalPrivateKey2-Error2") + + assertEqual(testPrivateKeyCheck, string(xmlPri), "ParseAndMarshalPrivateKey2") +} diff --git a/pkg/lakego-pkg/go-cryptobin/ecc/ecc.go b/pkg/lakego-pkg/go-cryptobin/ecc/ecc.go index 1c23ab19..a81c3556 100644 --- a/pkg/lakego-pkg/go-cryptobin/ecc/ecc.go +++ b/pkg/lakego-pkg/go-cryptobin/ecc/ecc.go @@ -78,6 +78,10 @@ func ParamsFromCurve(curve elliptic.Curve) *ECIESParams { return paramsFromCurve[curve] } +func AddParamsFromCurve(curve elliptic.Curve, ecie *ECIESParams) { + paramsFromCurve[curve] = ecie +} + // PublicKey is a representation of an elliptic curve public key. type PublicKey struct { X *big.Int diff --git a/pkg/lakego-pkg/go-cryptobin/ecdh/ecdh.go b/pkg/lakego-pkg/go-cryptobin/ecdh/ecdh.go index 7cb7a622..39685269 100644 --- a/pkg/lakego-pkg/go-cryptobin/ecdh/ecdh.go +++ b/pkg/lakego-pkg/go-cryptobin/ecdh/ecdh.go @@ -5,6 +5,8 @@ import ( "crypto/ecdh" "crypto/x509/pkix" "encoding/asn1" + + "golang.org/x/crypto/cryptobyte" ) var ( @@ -14,10 +16,10 @@ var ( // ECMQV oidPublicKeyECMQV = asn1.ObjectIdentifier{1, 3, 132, 1, 13} - namedCurveP256 = "P-256" - namedCurveP384 = "P-384" - namedCurveP521 = "P-521" - namedCurveX25519 = "X25519" + oidNamedCurveP256 = asn1.ObjectIdentifier{1, 2, 840, 10045, 3, 1, 7} + oidNamedCurveP384 = asn1.ObjectIdentifier{1, 3, 132, 0, 34} + oidNamedCurveP521 = asn1.ObjectIdentifier{1, 3, 132, 0, 35} + oidNamedCurveX25519 = asn1.ObjectIdentifier{1, 3, 101, 110} ) // 私钥 - 包装 @@ -46,13 +48,13 @@ func MarshalPublicKey(key *ecdh.PublicKey) ([]byte, error) { var publicKeyAlgorithm pkix.AlgorithmIdentifier var err error - name, ok := nameFromNamedCurve(key.Curve()) + oid, ok := oidFromNamedCurve(key.Curve()) if !ok { return nil, errors.New("x509: unsupported ecdh curve") } var paramBytes []byte - paramBytes, err = asn1.Marshal([]byte(name)) + paramBytes, err = asn1.Marshal(oid) if err != nil { return nil, err } @@ -95,18 +97,13 @@ func ParsePublicKey(derBytes []byte) (pub *ecdh.PublicKey, err error) { // 解析 keyData := &pki - var curveName []byte - rest, err = asn1.Unmarshal(keyData.Algorithm.Parameters.FullBytes, &curveName) - if err != nil { - return - } - - if len(rest) > 0 { - err = asn1.SyntaxError{Msg: "trailing data"} - return + paramsDer := cryptobyte.String(keyData.Algorithm.Parameters.FullBytes) + namedCurveOID := new(asn1.ObjectIdentifier) + if !paramsDer.ReadASN1ObjectIdentifier(namedCurveOID) { + return nil, errors.New("ecdh: invalid ECDH parameters") } - namedCurve := namedCurveFromName(string(curveName)) + namedCurve := namedCurveFromOid(*namedCurveOID) if namedCurve == nil { err = errors.New("ecdh: unsupported ecdh curve") return @@ -128,13 +125,13 @@ func ParsePublicKey(derBytes []byte) (pub *ecdh.PublicKey, err error) { func MarshalPrivateKey(key *ecdh.PrivateKey) ([]byte, error) { var privKey pkcs8 - name, ok := nameFromNamedCurve(key.Curve()) + oid, ok := oidFromNamedCurve(key.Curve()) if !ok { return nil, errors.New("x509: unsupported ecdh curve") } // 创建数据 - paramBytes, err := asn1.Marshal([]byte(name)) + paramBytes, err := asn1.Marshal(oid) if err != nil { return nil, errors.New("ecdh: failed to marshal algo param: " + err.Error()) } @@ -167,18 +164,13 @@ func ParsePrivateKey(derBytes []byte) (*ecdh.PrivateKey, error) { return nil, err } - var curveName []byte - rest, err := asn1.Unmarshal(privKey.Algo.Parameters.FullBytes, &curveName) - if err != nil { - return nil, err - } - - if len(rest) > 0 { - err = asn1.SyntaxError{Msg: "trailing data"} - return nil, err + paramsDer := cryptobyte.String(privKey.Algo.Parameters.FullBytes) + namedCurveOID := new(asn1.ObjectIdentifier) + if !paramsDer.ReadASN1ObjectIdentifier(namedCurveOID) { + return nil, errors.New("ecdh: invalid ECDH parameters") } - namedCurve := namedCurveFromName(string(curveName)) + namedCurve := namedCurveFromOid(*namedCurveOID) if namedCurve == nil { err = errors.New("ecdh: unsupported ecdh curve") return nil, err @@ -194,33 +186,33 @@ func ParsePrivateKey(derBytes []byte) (*ecdh.PrivateKey, error) { // ==================== -func namedCurveFromName(name string) ecdh.Curve { - switch name { - case namedCurveP256: +func namedCurveFromOid(oid asn1.ObjectIdentifier) ecdh.Curve { + switch { + case oid.Equal(oidNamedCurveP256): return ecdh.P256() - case namedCurveP384: + case oid.Equal(oidNamedCurveP384): return ecdh.P384() - case namedCurveP521: + case oid.Equal(oidNamedCurveP521): return ecdh.P521() - case namedCurveX25519: + case oid.Equal(oidNamedCurveX25519): return ecdh.X25519() } return nil } -func nameFromNamedCurve(curve ecdh.Curve) (string, bool) { +func oidFromNamedCurve(curve ecdh.Curve) (asn1.ObjectIdentifier, bool) { switch curve { case ecdh.P256(): - return namedCurveP256, true + return oidNamedCurveP256, true case ecdh.P384(): - return namedCurveP384, true + return oidNamedCurveP384, true case ecdh.P521(): - return namedCurveP521, true + return oidNamedCurveP521, true case ecdh.X25519(): - return namedCurveX25519, true + return oidNamedCurveX25519, true } - return "", false + return asn1.ObjectIdentifier{}, false } diff --git a/pkg/lakego-pkg/go-cryptobin/elgamal/elgamal.go b/pkg/lakego-pkg/go-cryptobin/elgamal/elgamal.go new file mode 100644 index 00000000..48fc82d0 --- /dev/null +++ b/pkg/lakego-pkg/go-cryptobin/elgamal/elgamal.go @@ -0,0 +1,493 @@ +package elgamal + +import ( + "io" + "time" + "errors" + "math/big" + "crypto" + "crypto/rand" + "crypto/subtle" + + mathrand "math/rand" + go_asn1 "encoding/asn1" + + "golang.org/x/crypto/cryptobyte" + "golang.org/x/crypto/cryptobyte/asn1" +) + +/* +from docs: +1. https://en.wikipedia.org/wiki/ElGamal_encryption +2. https://en.wikipedia.org/wiki/ElGamal_signature_scheme +3. https://dl.acm.org/doi/pdf/10.1145/3214303 +4. https://pkg.go.dev/golang.org/x/crypto/openpgp/elgamal +*/ + +var zero = big.NewInt(0) +var one = big.NewInt(1) +var two = big.NewInt(2) + +var ErrMessageLarge = errors.New("elgamal: message is larger than public key size") +var ErrCipherLarge = errors.New("elgamal: cipher is larger than public key size") + +// PublicKey represents a Elgamal public key. +type PublicKey struct { + G, P, Y *big.Int +} + +// PrivateKey represents Elgamal private key. +type PrivateKey struct { + PublicKey + X *big.Int +} + +// GenerateKey generates elgamal private key according +// to given bit size and probability. Moreover, the given probability +// value is used in choosing prime number P for performing n Miller-Rabin +// tests with 1 - 1/(4^n) probability false rate. +func GenerateKey(random io.Reader, bitsize, probability int) (*PrivateKey, error) { + // p is prime number + // q is prime group order + // g is cyclic group generator Zp + p, q, g, err := GeneratePQZp(random, bitsize, probability) + if err != nil { + return nil, err + } + + randSource := mathrand.New(mathrand.NewSource(time.Now().UnixNano())) + // choose random integer x from {1...(q-1)} + priv := new(big.Int).Rand(randSource, new(big.Int).Sub(q, one)) + // y = g^p mod p + y := new(big.Int).Exp(g, priv, p) + + return &PrivateKey{ + PublicKey: PublicKey{ + G: g, // cyclic group generator Zp + P: p, // prime number + Y: y, // y = g^p mod p + }, + X: priv, // secret key x + }, nil +} + +// Encrypt encrypts a plain text represented as a byte array. It returns +// an error if plain text value is larger than modulus P of Public key. +func (pub *PublicKey) Encrypt(random io.Reader, message []byte) ([]byte, []byte, error) { + // choose random integer k from {1...p} + k, err := rand.Int(random, pub.P) + if err != nil { + return nil, nil, err + } + + m := new(big.Int).SetBytes(message) + if m.Cmp(pub.P) == 1 { // m < P + return nil, nil, ErrMessageLarge + } + + // c1 = g^k mod p + c1 := new(big.Int).Exp(pub.G, k, pub.P) + // s = y^k mod p + s := new(big.Int).Exp(pub.Y, k, pub.P) + + // c2 = m*s mod p + c2 := new(big.Int).Mod( + new(big.Int).Mul(m, s), + pub.P, + ) + + return c1.Bytes(), c2.Bytes(), nil +} + +// c1 and c2 data +type elgamalEncryptData struct { + C1, C2 []byte +} + +// Encrypt encrypts a plain text represented as a byte array. +func (pub *PublicKey) EncryptAsn1(random io.Reader, message []byte) ([]byte, error) { + c1, c2, err := pub.Encrypt(random, message) + if err != nil { + return nil, err + } + + encryptData, err := go_asn1.Marshal(elgamalEncryptData{c1, c2}) + if err != nil { + return nil, err + } + + return encryptData, nil +} + +// Equal reports whether pub and x have the same value. +func (pub *PublicKey) Equal(x crypto.PublicKey) bool { + xx, ok := x.(*PublicKey) + if !ok { + return false + } + + return bigIntEqual(pub.G, xx.G) && + bigIntEqual(pub.P, xx.P) && + bigIntEqual(pub.Y, xx.Y) +} + +// Decrypt decrypts the passed cipher text. It returns an +// error if cipher text value is larger than modulus P of Public key. +func (priv *PrivateKey) Decrypt(cipher1, cipher2 []byte) ([]byte, error) { + c1 := new(big.Int).SetBytes(cipher1) + c2 := new(big.Int).SetBytes(cipher2) + if c1.Cmp(priv.P) == 1 && c2.Cmp(priv.P) == 1 { // (c1, c2) < P + return nil, ErrCipherLarge + } + + // s = c^x mod p + s := new(big.Int).Exp(c1, priv.X, priv.P) + // s = s(inv) = s^(-1) mod p + if s.ModInverse(s, priv.P) == nil { + return nil, errors.New("elgamal: invalid private key") + } + + // m = s(inv) * c2 mod p + m := new(big.Int).Mod( + new(big.Int).Mul(s, c2), + priv.P, + ) + + return m.Bytes(), nil +} + +// Decrypt decrypts the passed cipher text. +func (priv *PrivateKey) DecryptAsn1(cipherData []byte) ([]byte, error) { + var encryptData elgamalEncryptData + _, err := go_asn1.Unmarshal(cipherData, &encryptData) + if err != nil { + return nil, err + } + + deData, err := priv.Decrypt(encryptData.C1, encryptData.C2) + if err != nil { + return nil, err + } + + return deData, nil +} + +// Public returns the public key corresponding to priv. +func (priv *PrivateKey) Public() crypto.PublicKey { + return &priv.PublicKey +} + +// Equal reports whether priv and x have the same value. +func (priv *PrivateKey) Equal(x crypto.PrivateKey) bool { + xx, ok := x.(*PrivateKey) + if !ok { + return false + } + + return priv.PublicKey.Equal(&xx.PublicKey) && + bigIntEqual(priv.X, xx.X) +} + +// bigIntEqual reports whether a and b are equal leaking only their bit length +// through timing side-channels. +func bigIntEqual(a, b *big.Int) bool { + return subtle.ConstantTimeCompare(a.Bytes(), b.Bytes()) == 1 +} + +// HomomorphicEncTwo performs homomorphic operation over two passed chiphers. +// Elgamal has multiplicative homomorphic property, so resultant cipher +// contains the product of two numbers. +func (pub *PublicKey) HomomorphicEncTwo(c1, c2, c1dash, c2dash []byte) ([]byte, []byte, error) { + cipher1 := new(big.Int).SetBytes(c1) + cipher2 := new(big.Int).SetBytes(c2) + if cipher1.Cmp(pub.P) == 1 && cipher2.Cmp(pub.P) == 1 { // (c1, c2) < P + return nil, nil, ErrCipherLarge + } + + // In the context of elgamal encryption, (cipher1,cipher2) and + // (cipher1dash, cipher2dash) both are valid ciphers and represented + // by different variable names. + cipher1dash := new(big.Int).SetBytes(c1dash) + cipher2dash := new(big.Int).SetBytes(c2dash) + if cipher1dash.Cmp(pub.P) == 1 && cipher2dash.Cmp(pub.P) == 1 { // (c1dash, c2dash) < P + return nil, nil, ErrCipherLarge + } + + // C1 = c1 * c1dash mod p + C1 := new(big.Int).Mod( + new(big.Int).Mul(cipher1, cipher1dash), + pub.P, + ) + + // C2 = c2 * c2dash mod p + C2 := new(big.Int).Mod( + new(big.Int).Mul(cipher2, cipher2dash), + pub.P, + ) + + return C1.Bytes(), C2.Bytes(), nil +} + +// HommorphicEncMultiple performs homomorphic operation over multiple passed chiphers. +// Elgamal has multiplicative homomorphic property, so resultant cipher +// contains the product of multiple numbers. +func (pub *PublicKey) HommorphicEncMultiple(ciphertext [][2][]byte) ([]byte, []byte, error) { + // C1, C2, _ := pub.Encrypt(one.Bytes()) + C1 := one // since, c = 1^e mod n is equal to 1 + C2 := one + + for i := 0; i < len(ciphertext); i++ { + c1 := new(big.Int).SetBytes(ciphertext[i][0]) + c2 := new(big.Int).SetBytes(ciphertext[i][1]) + + if c1.Cmp(pub.P) == 1 && c2.Cmp(pub.P) == 1 { // (c1, c2) < P + return nil, nil, ErrCipherLarge + } + + // C1 = (c1)_1 * (c1)_2 * (c1)_3 ...(c1)_n mod p + C1 = new(big.Int).Mod( + new(big.Int).Mul( + C1, + c1), + pub.P, + ) + + // C2 = (c2)_1 * (c2)_2 * (c2)_3 ...(c2)_n mod p + C2 = new(big.Int).Mod( + new(big.Int).Mul( + C2, + c2), + pub.P, + ) + } + + return C1.Bytes(), C2.Bytes(), nil +} + +// Signature generates signature over the given hash. It returns signature +// value consisting of two parts "r" and "s" as byte arrays. +func (priv *PrivateKey) Sign(random io.Reader, hash []byte) ([]byte, []byte, error) { + k := new(big.Int) + gcd := new(big.Int) + + var err error + + // choosing random integer k from {1...(p-2)}, such that + // gcd(k,(p-1)) should be equal to 1. + for { + k, err = rand.Int(random, new(big.Int).Sub(priv.P, two)) + if err != nil { + return nil, nil, err + } + + if k.Cmp(one) == 0 { + continue + } else { + gcd = gcd.GCD(nil, nil, k, new(big.Int).Sub(priv.P, one)) + if gcd.Cmp(one) == 0 { + break + } + } + } + + // m as H(m) + m := new(big.Int).SetBytes(hash) + + // r = g^k mod p + r := new(big.Int).Exp(priv.G, k, priv.P) + // xr = x * r + xr := new(big.Int).Mod( + new(big.Int).Mul(r, priv.X), + new(big.Int).Sub(priv.P, one), + ) + + // hmxr = [H(m) -xr] + hmxr := new(big.Int).Sub(m, xr) + // k = k^(-1) + k = k.ModInverse(k, new(big.Int).Sub(priv.P, one)) + + // s = [H(m) -xr]k^(-1) mod (p-1) + s := new(big.Int).Mod( + new(big.Int).Mul(hmxr, k), + new(big.Int).Sub(priv.P, one), + ) + + return r.Bytes(), s.Bytes(), nil +} + +// Verify verifies signature over the given hash and signature values (r & s). +// It returns true as a boolean value if signature is verify correctly. Otherwise +// it returns false along with error message. +func (pub *PublicKey) Verify(r, s, hash []byte) (bool, error) { + // verify that 0 < r < p + signr := new(big.Int).SetBytes(r) + if signr.Cmp(zero) == -1 { + return false, errors.New("r is smaller than zero") + } else if signr.Cmp(pub.P) == +1 { + return false, errors.New("r is larger than public key p") + } + + signs := new(big.Int).SetBytes(s) + if signs.Cmp(zero) == -1 { + return false, errors.New("s is smaller than zero") + } else if signs.Cmp(new(big.Int).Sub(pub.P, one)) == +1 { + return false, errors.New("s is larger than public key p") + } + + // m as H(m) + m := new(big.Int).SetBytes(hash) + // ghashm = g^[H(m)] mod p + ghashm := new(big.Int).Exp(pub.G, m, pub.P) + + // y^r * r*s mod p + YrRs := new(big.Int).Mod( + new(big.Int).Mul( + new(big.Int).Exp(pub.Y, signr, pub.P), + new(big.Int).Exp(signr, signs, pub.P), + ), + pub.P, + ) + + // g^H(m) y^r * r*s mod p + if ghashm.Cmp(YrRs) == 0 { + return true, nil // signature is verified + } + + return false, errors.New("signature is not verified") +} + +// Sign hash +func Sign(rand io.Reader, priv *PrivateKey, hash []byte) ([]byte, []byte, error) { + if priv == nil { + return nil, nil, errors.New("Private Key is error") + } + + return priv.Sign(rand, hash) +} + +// Verify hash +func Verify(pub *PublicKey, hash, r, s []byte) (bool, error) { + if pub == nil { + return false, errors.New("Public Key is error") + } + + ok, err := pub.Verify(r, s, hash) + if err != nil { + return false, err + } + + return ok, nil +} + +// SignASN1 signs a hash (which should be the result of hashing a larger message) +// using the private key, priv. If the hash is longer than the bit-length of the +// private key's curve order, the hash will be truncated to that length. It +// returns the ASN.1 encoded signature. +func SignASN1(rand io.Reader, priv *PrivateKey, hash []byte) ([]byte, error) { + r, s, err := priv.Sign(rand, hash) + if err != nil { + return nil, err + } + + return encodeSignature(r, s) +} + +// VerifyASN1 verifies the ASN.1 encoded signature, sig, of hash using the +// public key, pub. Its return value records whether the signature is valid. +func VerifyASN1(pub *PublicKey, hash, sig []byte) (bool, error) { + rBytes, sBytes, err := parseSignature(sig) + if err != nil { + return false, err + } + + ok, err := pub.Verify(rBytes, sBytes, hash) + if err != nil { + return false, err + } + + return ok, nil +} + +func encodeSignature(r, s []byte) ([]byte, error) { + var b cryptobyte.Builder + b.AddASN1(asn1.SEQUENCE, func(b *cryptobyte.Builder) { + addASN1IntBytes(b, r) + addASN1IntBytes(b, s) + }) + + return b.Bytes() +} + +// addASN1IntBytes encodes in ASN.1 a positive integer represented as +// a big-endian byte slice with zero or more leading zeroes. +func addASN1IntBytes(b *cryptobyte.Builder, bytes []byte) { + for len(bytes) > 0 && bytes[0] == 0 { + bytes = bytes[1:] + } + + if len(bytes) == 0 { + b.SetError(errors.New("invalid integer")) + return + } + + b.AddASN1(asn1.INTEGER, func(c *cryptobyte.Builder) { + if bytes[0]&0x80 != 0 { + c.AddUint8(0) + } + + c.AddBytes(bytes) + }) +} + +func parseSignature(sig []byte) (r, s []byte, err error) { + var inner cryptobyte.String + + input := cryptobyte.String(sig) + if !input.ReadASN1(&inner, asn1.SEQUENCE) || + !input.Empty() || + !inner.ReadASN1Integer(&r) || + !inner.ReadASN1Integer(&s) || + !inner.Empty() { + return nil, nil, errors.New("invalid ASN.1") + } + + return r, s, nil +} + +// Gen emit . +// p = 2q + 1, p,q - safe primes +// g - cyclic group generator Zp +// performs n Miller-Rabin tests with 1 - 1/(4^n) probability false rate. +// Gain n - bit width for integer & probability rang for MR. +// It returns p, q, g and write error message. +func GeneratePQZp(random io.Reader, n, probability int) (*big.Int, *big.Int, *big.Int, error) { + for { + q, err := rand.Prime(random, n-1) + if err != nil { + return nil, nil, nil, err + } + + t := new(big.Int).Mul(q, two) + p := new(big.Int).Add(t, one) + if p.ProbablyPrime(probability) { + for { + g, err := rand.Int(random, p) + if err != nil { + return nil, nil, nil, err + } + + b := new(big.Int).Exp(g, two, p) + if b.Cmp(one) == 0 { + continue + } + + b = new(big.Int).Exp(g, q, p) + if b.Cmp(one) == 0 { + return p, q, g, nil + } + } + } + } + + return nil, nil, nil, errors.New("can't emit ") +} diff --git a/pkg/lakego-pkg/go-cryptobin/elgamal/elgamal_test.go b/pkg/lakego-pkg/go-cryptobin/elgamal/elgamal_test.go new file mode 100644 index 00000000..6b3d6172 --- /dev/null +++ b/pkg/lakego-pkg/go-cryptobin/elgamal/elgamal_test.go @@ -0,0 +1,207 @@ +package elgamal + +import ( + "testing" + "crypto/rand" + "crypto/sha256" + + cryptobin_test "github.com/deatil/go-cryptobin/tool/test" +) + +var testBitsize = 256 +var testProbability = 64 + +func Test_GenerateKey(t *testing.T) { + assertEmpty := cryptobin_test.AssertEmptyT(t) + assertError := cryptobin_test.AssertErrorT(t) + + pri, err := GenerateKey(rand.Reader, testBitsize, testProbability) + + assertError(err, "GenerateKey-Error") + assertEmpty(pri, "GenerateKey") +} + +func Test_Encrypt(t *testing.T) { + assertEqual := cryptobin_test.AssertEqualT(t) + assertEmpty := cryptobin_test.AssertEmptyT(t) + assertError := cryptobin_test.AssertErrorT(t) + + pri, err := GenerateKey(rand.Reader, testBitsize, testProbability) + pub := &pri.PublicKey + + assertError(err, "Encrypt-Error") + assertEmpty(pri, "Encrypt") + + data := "123tesfd!df" + + c1, c2, err := pub.Encrypt(rand.Reader, []byte(data)) + assertError(err, "Encrypt-Encrypt-Error") + + de, err := pri.Decrypt(c1, c2) + assertError(err, "Encrypt-Decrypt-Error") + + assertEqual(string(de), data, "Encrypt-Dedata") +} + +func Test_EncryptAsn1(t *testing.T) { + assertEqual := cryptobin_test.AssertEqualT(t) + assertEmpty := cryptobin_test.AssertEmptyT(t) + assertError := cryptobin_test.AssertErrorT(t) + + pri, err := GenerateKey(rand.Reader, testBitsize, testProbability) + pub := &pri.PublicKey + + assertError(err, "Encrypt-Error") + assertEmpty(pri, "Encrypt") + + data := "123tesfd!df" + + c, err := pub.EncryptAsn1(rand.Reader, []byte(data)) + assertError(err, "Encrypt-Encrypt-Error") + + de, err := pri.DecryptAsn1(c) + assertError(err, "Encrypt-Decrypt-Error") + + assertEqual(string(de), data, "Encrypt-Dedata") +} + +func Test_Sign(t *testing.T) { + assertBool := cryptobin_test.AssertBoolT(t) + assertEmpty := cryptobin_test.AssertEmptyT(t) + assertError := cryptobin_test.AssertErrorT(t) + + pri, err := GenerateKey(rand.Reader, testBitsize, testProbability) + pub := &pri.PublicKey + + assertError(err, "Sign-Error") + assertEmpty(pri, "Sign") + + data := "123tesfd!dfsign" + hash := sha256.Sum256([]byte(data)) + + sig, err := SignASN1(rand.Reader, pri, hash[:]) + assertError(err, "Sign-sig-Error") + + veri, _ := VerifyASN1(pub, hash[:], sig) + assertBool(veri, "Sign-veri") +} + +func Test_MarshalPKCS1(t *testing.T) { + assertEmpty := cryptobin_test.AssertEmptyT(t) + assertError := cryptobin_test.AssertErrorT(t) + assertEqual := cryptobin_test.AssertEqualT(t) + + pri, err := GenerateKey(rand.Reader, testBitsize, testProbability) + pub := &pri.PublicKey + + assertError(err, "MarshalPKCS1-Error") + assertEmpty(pri, "MarshalPKCS1") + + //=============== + + pubDer, err := MarshalPKCS1PublicKey(pub) + assertError(err, "MarshalPKCS1-pub-Error") + assertEmpty(pubDer, "MarshalPKCS1") + + parsedPub, err := ParsePKCS1PublicKey(pubDer) + assertError(err, "MarshalPKCS1-pub-Error") + assertEqual(pub, parsedPub, "MarshalPKCS1") + + //=============== + + priDer, err := MarshalPKCS1PrivateKey(pri) + assertError(err, "MarshalPKCS1-pri-Error") + assertEmpty(priDer, "MarshalPKCS1") + + parsedPri, err := ParsePKCS1PrivateKey(priDer) + assertError(err, "MarshalPKCS1-pri-Error") + assertEqual(pri, parsedPri, "MarshalPKCS1") +} + +func Test_MarshalPKCS8(t *testing.T) { + assertEmpty := cryptobin_test.AssertEmptyT(t) + assertError := cryptobin_test.AssertErrorT(t) + assertEqual := cryptobin_test.AssertEqualT(t) + + pri, err := GenerateKey(rand.Reader, testBitsize, testProbability) + pub := &pri.PublicKey + + assertError(err, "MarshalPKCS8-Error") + assertEmpty(pri, "MarshalPKCS8") + + //=============== + + pubDer, err := MarshalPKCS8PublicKey(pub) + assertError(err, "MarshalPKCS8-pub-Error") + assertEmpty(pubDer, "MarshalPKCS8") + + parsedPub, err := ParsePKCS8PublicKey(pubDer) + assertError(err, "MarshalPKCS8-pub-Error") + assertEqual(pub, parsedPub, "MarshalPKCS8") + + //=============== + + priDer, err := MarshalPKCS8PrivateKey(pri) + assertError(err, "MarshalPKCS8-pri-Error") + assertEmpty(priDer, "MarshalPKCS8") + + parsedPri, err := ParsePKCS8PrivateKey(priDer) + assertError(err, "MarshalPKCS8-pri-Error") + assertEqual(pri, parsedPri, "MarshalPKCS8") +} + +var testXMLPrivateKey = ` + + vG406oGr5OqG0mMOtq5wWo/aGWWE8EPiPl09/I+ySxs= +

9W35RbKvFgfHndG9wVvFDMDw86BClpDk6kdeGr1ygLc=

+ 120jHKCdPWjLGrqH3HiCZ2GezWyEjfEIPBMhULymfzM= + BjtroR34tS5cvF5YNJaxmOjGDas43wKFunHCYS4P6CQ= +
+`; +var testXMLPublicKey = ` + + vG406oGr5OqG0mMOtq5wWo/aGWWE8EPiPl09/I+ySxs= +

9W35RbKvFgfHndG9wVvFDMDw86BClpDk6kdeGr1ygLc=

+ 120jHKCdPWjLGrqH3HiCZ2GezWyEjfEIPBMhULymfzM= +
+`; + +func Test_MarshalXML(t *testing.T) { + assertEmpty := cryptobin_test.AssertEmptyT(t) + assertError := cryptobin_test.AssertErrorT(t) + assertEqual := cryptobin_test.AssertEqualT(t) + + pri, err := GenerateKey(rand.Reader, testBitsize, testProbability) + pub := &pri.PublicKey + + assertError(err, "MarshalXML-Error") + assertEmpty(pri, "MarshalXML") + + //=============== + + pubDer, err := MarshalXMLPublicKey(pub) + assertError(err, "MarshalXML-pub-Error") + assertEmpty(pubDer, "MarshalXML") + + parsedPub, err := ParseXMLPublicKey(pubDer) + assertError(err, "MarshalXML-pub-Error") + assertEqual(pub, parsedPub, "MarshalXML") + + //=============== + + priDer, err := MarshalXMLPrivateKey(pri) + assertError(err, "MarshalXML-pri-Error") + assertEmpty(priDer, "MarshalXML") + + parsedPri, err := ParseXMLPrivateKey(priDer) + assertError(err, "MarshalXML-pri-Error") + assertEqual(pri, parsedPri, "MarshalXML") + + //=============== + + _, err = ParseXMLPublicKey([]byte(testXMLPublicKey)) + assertError(err, "MarshalXML-pub-Error") + + _, err = ParseXMLPrivateKey([]byte(testXMLPrivateKey)) + assertError(err, "MarshalXML-pri-Error") +} diff --git a/pkg/lakego-pkg/go-cryptobin/elgamal/pkcs1.go b/pkg/lakego-pkg/go-cryptobin/elgamal/pkcs1.go new file mode 100644 index 00000000..13617f09 --- /dev/null +++ b/pkg/lakego-pkg/go-cryptobin/elgamal/pkcs1.go @@ -0,0 +1,150 @@ +package elgamal + +import ( + "fmt" + "math/big" + "encoding/asn1" +) + +// 序列号 +var elgamalPrivKeyVersion = 0 + +// 私钥 +type elgamalPrivateKey struct { + Version int + G *big.Int + P *big.Int + Y *big.Int + X *big.Int +} + +// 公钥 +type elgamalPublicKey struct { + G *big.Int + P *big.Int + Y *big.Int +} + +var ( + // 默认 + defaultPKCS1Key = NewPKCS1Key() + + // 默认为 pkcs1 模式 + MarshalPublicKey = MarshalPKCS1PublicKey + ParsePublicKey = ParsePKCS1PublicKey + + MarshalPrivateKey = MarshalPKCS1PrivateKey + ParsePrivateKey = ParsePKCS1PrivateKey +) + +/** + * elgamal pkcs1 密钥 + * + * @create 2023-6-16 + * @author deatil + */ +type PKCS1Key struct {} + +// 构造函数 +func NewPKCS1Key() PKCS1Key { + return PKCS1Key{} +} + +// 包装公钥 +func (this PKCS1Key) MarshalPublicKey(key *PublicKey) ([]byte, error) { + publicKey := elgamalPublicKey{ + G: key.G, + P: key.P, + Y: key.Y, + } + + return asn1.Marshal(publicKey) +} + +// 包装公钥 +func MarshalPKCS1PublicKey(key *PublicKey) ([]byte, error) { + return defaultPKCS1Key.MarshalPublicKey(key) +} + +// 解析公钥 +func (this PKCS1Key) ParsePublicKey(der []byte) (*PublicKey, error) { + var key elgamalPublicKey + rest, err := asn1.Unmarshal(der, &key) + if err != nil { + return nil, err + } + + if len(rest) > 0 { + return nil, asn1.SyntaxError{Msg: "trailing data"} + } + + publicKey := &PublicKey{ + G: key.G, + P: key.P, + Y: key.Y, + } + + return publicKey, nil +} + +// 解析公钥 +func ParsePKCS1PublicKey(derBytes []byte) (*PublicKey, error) { + return defaultPKCS1Key.ParsePublicKey(derBytes) +} + +// ==================== + +// 包装私钥 +func (this PKCS1Key) MarshalPrivateKey(key *PrivateKey) ([]byte, error) { + // 版本号 + version := elgamalPrivKeyVersion + + // 构造私钥信息 + privateKey := elgamalPrivateKey{ + Version: version, + G: key.G, + P: key.P, + Y: key.Y, + X: key.X, + } + + return asn1.Marshal(privateKey) +} + +// 包装私钥 +func MarshalPKCS1PrivateKey(key *PrivateKey) ([]byte, error) { + return defaultPKCS1Key.MarshalPrivateKey(key) +} + +// 解析私钥 +func (this PKCS1Key) ParsePrivateKey(der []byte) (*PrivateKey, error) { + var key elgamalPrivateKey + rest, err := asn1.Unmarshal(der, &key) + if err != nil { + return nil, err + } + + if len(rest) > 0 { + return nil, asn1.SyntaxError{Msg: "trailing data"} + } + + if key.Version != elgamalPrivKeyVersion { + return nil, fmt.Errorf("EIGamal: unknown EIGamal private key version %d", key.Version) + } + + privateKey := &PrivateKey{ + PublicKey: PublicKey{ + G: key.G, + P: key.P, + Y: key.Y, + }, + X: key.X, + } + + return privateKey, nil +} + +// 解析私钥 +func ParsePKCS1PrivateKey(derBytes []byte) (*PrivateKey, error) { + return defaultPKCS1Key.ParsePrivateKey(derBytes) +} diff --git a/pkg/lakego-pkg/go-cryptobin/elgamal/pkcs8.go b/pkg/lakego-pkg/go-cryptobin/elgamal/pkcs8.go new file mode 100644 index 00000000..2b49a05d --- /dev/null +++ b/pkg/lakego-pkg/go-cryptobin/elgamal/pkcs8.go @@ -0,0 +1,224 @@ +package elgamal + +import ( + "fmt" + "errors" + "math/big" + "encoding/asn1" + "crypto/x509/pkix" + + "golang.org/x/crypto/cryptobyte" + cryptobyte_asn1 "golang.org/x/crypto/cryptobyte/asn1" +) + +var ( + // elgamal 公钥 oid + oidPublicKeyEIGamal = asn1.ObjectIdentifier{1, 3, 14, 7, 2, 1, 1} +) + +// 私钥 - 包装 +type pkcs8 struct { + Version int + Algo pkix.AlgorithmIdentifier + PrivateKey []byte +} + +// 公钥 - 包装 +type pkixPublicKey struct { + Algo pkix.AlgorithmIdentifier + BitString asn1.BitString +} + +// 公钥信息 - 解析 +type publicKeyInfo struct { + Raw asn1.RawContent + Algorithm pkix.AlgorithmIdentifier + PublicKey asn1.BitString +} + +var ( + defaultPKCS8Key = NewPKCS8Key() +) + +/** + * elgamal pkcs8 密钥 + * + * @create 2023-6-16 + * @author deatil + */ +type PKCS8Key struct {} + +// 构造函数 +func NewPKCS8Key() PKCS8Key { + return PKCS8Key{} +} + +// PKCS8 包装公钥 +func (this PKCS8Key) MarshalPublicKey(key *PublicKey) ([]byte, error) { + var publicKeyBytes []byte + var publicKeyAlgorithm pkix.AlgorithmIdentifier + var err error + + publicKeyAlgorithm.Algorithm = oidPublicKeyEIGamal + publicKeyAlgorithm.Parameters = asn1.NullRawValue + + var p cryptobyte.Builder + p.AddASN1(cryptobyte_asn1.SEQUENCE, func(b *cryptobyte.Builder) { + addASN1IntBytes(b, key.G.Bytes()) + addASN1IntBytes(b, key.P.Bytes()) + addASN1IntBytes(b, key.Y.Bytes()) + }) + + publicKeyBytes, err = p.Bytes() + if err != nil { + return nil, errors.New("elgamal: failed to builder PrivateKey: " + err.Error()) + } + + pkix := pkixPublicKey{ + Algo: publicKeyAlgorithm, + BitString: asn1.BitString{ + Bytes: publicKeyBytes, + BitLength: 8 * len(publicKeyBytes), + }, + } + + return asn1.Marshal(pkix) +} + +// PKCS8 包装公钥 +func MarshalPKCS8PublicKey(pub *PublicKey) ([]byte, error) { + return defaultPKCS8Key.MarshalPublicKey(pub) +} + +// PKCS8 解析公钥 +func (this PKCS8Key) ParsePublicKey(der []byte) (*PublicKey, error) { + var pki publicKeyInfo + rest, err := asn1.Unmarshal(der, &pki) + if err != nil { + return nil, err + } + + if len(rest) > 0 { + return nil, asn1.SyntaxError{Msg: "trailing data"} + } + + algoEq := pki.Algorithm.Algorithm.Equal(oidPublicKeyEIGamal) + if !algoEq { + return nil, errors.New("elgamal: unknown public key algorithm") + } + + // 解析 + keyData := &pki + + pub := &PublicKey{ + G: new(big.Int), + P: new(big.Int), + Y: new(big.Int), + } + + pubDer := cryptobyte.String(keyData.PublicKey.RightAlign()) + if !pubDer.ReadASN1(&pubDer, cryptobyte_asn1.SEQUENCE) || + !pubDer.ReadASN1Integer(pub.G) || + !pubDer.ReadASN1Integer(pub.P) || + !pubDer.ReadASN1Integer(pub.Y) { + return nil, errors.New("x509: invalid EIGamal public key") + } + + if pub.Y.Sign() <= 0 || + pub.G.Sign() <= 0 || + pub.P.Sign() <= 0 { + return nil, errors.New("x509: zero or negative EIGamal parameter") + } + + return pub, nil +} + +// PKCS8 解析公钥 +func ParsePKCS8PublicKey(derBytes []byte) (*PublicKey, error) { + return defaultPKCS8Key.ParsePublicKey(derBytes) +} + +// ==================== + +// PKCS8 包装私钥 +func (this PKCS8Key) MarshalPrivateKey(key *PrivateKey) ([]byte, error) { + var privKey pkcs8 + + privKey.Algo = pkix.AlgorithmIdentifier{ + Algorithm: oidPublicKeyEIGamal, + Parameters: asn1.NullRawValue, + } + + var p cryptobyte.Builder + p.AddASN1(cryptobyte_asn1.SEQUENCE, func(b *cryptobyte.Builder) { + addASN1IntBytes(b, key.G.Bytes()) + addASN1IntBytes(b, key.P.Bytes()) + addASN1IntBytes(b, key.X.Bytes()) + }) + + privateKeyBytes, err := p.Bytes() + if err != nil { + return nil, errors.New("elgamal: failed to builder PrivateKey: " + err.Error()) + } + + privKey.PrivateKey = privateKeyBytes + + return asn1.Marshal(privKey) +} + +// PKCS8 包装私钥 +func MarshalPKCS8PrivateKey(key *PrivateKey) ([]byte, error) { + return defaultPKCS8Key.MarshalPrivateKey(key) +} + +// PKCS8 解析私钥 +func (this PKCS8Key) ParsePrivateKey(der []byte) (key *PrivateKey, err error) { + var privKey pkcs8 + _, err = asn1.Unmarshal(der, &privKey) + if err != nil { + return nil, err + } + + if !privKey.Algo.Algorithm.Equal(oidPublicKeyEIGamal) { + return nil, fmt.Errorf("elgamal: PKCS#8 wrapping contained private key with unknown algorithm: %v", privKey.Algo.Algorithm) + } + + priv := &PrivateKey{ + PublicKey: PublicKey{ + G: new(big.Int), + P: new(big.Int), + Y: new(big.Int), + }, + X: new(big.Int), + } + + // 找出 g,p,x 数据 + priDer := cryptobyte.String(string(privKey.PrivateKey)) + if !priDer.ReadASN1(&priDer, cryptobyte_asn1.SEQUENCE) || + !priDer.ReadASN1Integer(priv.G) || + !priDer.ReadASN1Integer(priv.P) || + !priDer.ReadASN1Integer(priv.X) { + return nil, errors.New("x509: invalid EIGamal private key") + } + + if priv.X.Sign() <= 0 || + priv.G.Sign() <= 0 || + priv.P.Sign() <= 0 { + return nil, errors.New("x509: zero or negative EIGamal parameter") + } + + // 算出 Y 值 + priv.Y.Exp(priv.G, priv.X, priv.P) + + if priv.Y.Sign() <= 0 || priv.G.Sign() <= 0 || + priv.P.Sign() <= 0 || priv.X.Sign() <= 0 { + return nil, errors.New("x509: zero or negative EIGamal parameter") + } + + return priv, nil +} + +// PKCS8 解析私钥 +func ParsePKCS8PrivateKey(derBytes []byte) (key *PrivateKey, err error) { + return defaultPKCS8Key.ParsePrivateKey(derBytes) +} diff --git a/pkg/lakego-pkg/go-cryptobin/elgamal/xml.go b/pkg/lakego-pkg/go-cryptobin/elgamal/xml.go new file mode 100644 index 00000000..030412c6 --- /dev/null +++ b/pkg/lakego-pkg/go-cryptobin/elgamal/xml.go @@ -0,0 +1,202 @@ +package elgamal + +import ( + "errors" + "math/big" + "encoding/xml" + "encoding/base64" +) + +// 私钥 +type xmlPrivateKey struct { + XMLName xml.Name `xml:"EIGamalKeyValue"` + G string `xml:"G"` + P string `xml:"P"` + Q string `xml:"Q,omitempty"` + Y string `xml:"Y"` + X string `xml:"X"` +} + +// 公钥 +type xmlPublicKey struct { + XMLName xml.Name `xml:"EIGamalKeyValue"` + G string `xml:"G"` + P string `xml:"P"` + Q string `xml:"Q,omitempty"` + Y string `xml:"Y"` +} + +var ( + errPublicKeyXMLValue = func(name string) error { + return errors.New("elgamal xml: public key [" + name + "] value is error") + } + + errPrivateKeyXMLValue = func(name string) error { + return errors.New("elgamal xml: private key [" + name + "] value is error") + } +) + +var defaultXMLKey = NewXMLKey() + +/** + * elgamal xml密钥 + * + * @create 2023-6-16 + * @author deatil + */ +type XMLKey struct {} + +// 构造函数 +func NewXMLKey() XMLKey { + return XMLKey{} +} + +// 包装公钥 +func (this XMLKey) MarshalPublicKey(key *PublicKey) ([]byte, error) { + publicKey := xmlPublicKey{ + G: this.bigintToB64(key.G), + P: this.bigintToB64(key.P), + Y: this.bigintToB64(key.Y), + } + + return xml.MarshalIndent(publicKey, "", " ") +} + +func MarshalXMLPublicKey(key *PublicKey) ([]byte, error) { + return defaultXMLKey.MarshalPublicKey(key) +} + +// 解析公钥 +func (this XMLKey) ParsePublicKey(data []byte) (*PublicKey, error) { + var pub xmlPublicKey + err := xml.Unmarshal(data, &pub) + if err != nil { + return nil, err + } + + g, err := this.b64ToBigint(pub.G) + if err != nil { + return nil, errPublicKeyXMLValue("G") + } + + p, err := this.b64ToBigint(pub.P) + if err != nil { + return nil, errPublicKeyXMLValue("P") + } + + y, err := this.b64ToBigint(pub.Y) + if err != nil { + return nil, errPublicKeyXMLValue("Y") + } + + if g.Sign() <= 0 || p.Sign() <= 0 || y.Sign() <= 0 { + return nil, errors.New("elgamal xml: public key contains zero or negative value") + } + + publicKey := &PublicKey{ + G: g, + P: p, + Y: y, + } + + return publicKey, nil +} + +func ParseXMLPublicKey(der []byte) (*PublicKey, error) { + return defaultXMLKey.ParsePublicKey(der) +} + +// ==================== + +// 包装私钥 +func (this XMLKey) MarshalPrivateKey(key *PrivateKey) ([]byte, error) { + // 构造私钥信息 + priv := xmlPrivateKey{ + G: this.bigintToB64(key.G), + P: this.bigintToB64(key.P), + Y: this.bigintToB64(key.Y), + X: this.bigintToB64(key.X), + } + + return xml.MarshalIndent(priv, "", " ") +} + +func MarshalXMLPrivateKey(key *PrivateKey) ([]byte, error) { + return defaultXMLKey.MarshalPrivateKey(key) +} + +// 解析私钥 +func (this XMLKey) ParsePrivateKey(data []byte) (*PrivateKey, error) { + var priv xmlPrivateKey + err := xml.Unmarshal(data, &priv) + if err != nil { + return nil, err + } + + g, err := this.b64ToBigint(priv.G) + if err != nil { + return nil, errPrivateKeyXMLValue("G") + } + + p, err := this.b64ToBigint(priv.P) + if err != nil { + return nil, errPrivateKeyXMLValue("P") + } + + y, err := this.b64ToBigint(priv.Y) + if err != nil { + return nil, errPrivateKeyXMLValue("Y") + } + + x, err := this.b64ToBigint(priv.X) + if err != nil { + return nil, errPrivateKeyXMLValue("X") + } + + if g.Sign() <= 0 || p.Sign() <= 0 || y.Sign() <= 0 || x.Sign() <= 0 { + return nil, errors.New("elgamal xml: private key contains zero or negative value") + } + + privateKey := &PrivateKey{ + PublicKey: PublicKey{ + G: g, + P: p, + Y: y, + }, + X: x, + } + + return privateKey, nil +} + +func ParseXMLPrivateKey(der []byte) (*PrivateKey, error) { + return defaultXMLKey.ParsePrivateKey(der) +} + +// ==================== + +func (this XMLKey) b64d(str string) ([]byte, error) { + decoded, err := base64.StdEncoding.DecodeString(str) + + return []byte(decoded), err +} + +func (this XMLKey) b64e(src []byte) string { + return base64.StdEncoding.EncodeToString(src) +} + +func (this XMLKey) b64ToBigint(str string) (*big.Int, error) { + decoded, err := this.b64d(str) + if err != nil { + return nil, err + } + + bInt := &big.Int{} + bInt.SetBytes(decoded) + + return bInt, nil +} + +func (this XMLKey) bigintToB64(encoded *big.Int) string { + return this.b64e(encoded.Bytes()) +} diff --git a/pkg/lakego-pkg/lakego-filesystem/filesystem/filesystem.go b/pkg/lakego-pkg/lakego-filesystem/filesystem/filesystem.go index 7f72162c..25ffa934 100644 --- a/pkg/lakego-pkg/lakego-filesystem/filesystem/filesystem.go +++ b/pkg/lakego-pkg/lakego-filesystem/filesystem/filesystem.go @@ -245,16 +245,16 @@ func Hash(path string) (string, error) { // 添加数据 func (this *Filesystem) Put(path string, contents string, lock ...bool) error { - out, createErr := os.Create(path) - if createErr != nil { - return errors.New("执行函数 os.Create() 失败, 错误为:" + createErr.Error()) + out, err := os.Create(path) + if err != nil { + return err } defer out.Close() - _, writeErr := out.WriteString(contents) - if writeErr != nil { - return errors.New("执行函数 os.WriteString() 失败, 错误为:" + writeErr.Error()) + _, err = out.WriteString(contents) + if err != nil { + return err } return nil @@ -356,7 +356,7 @@ func Append(path string, data string) error { func (this *Filesystem) Chmod(path string, mode uint32) error { err := os.Chmod(path, os.FileMode(mode)) if err != nil { - return errors.New("设置文件权限失败") + return err } return nil @@ -988,18 +988,18 @@ func (this *Filesystem) EnsureDirectoryExists( fd, err := os.Create(checkFile) if err != nil { if os.IsPermission(err) { - return fmt.Errorf("%s 没有读写权限", directory) + return fmt.Errorf("%s no permission", directory) } return err } if err := fd.Close(); err != nil { - return fmt.Errorf("关闭失败: %s", err) + return fmt.Errorf("close err: %s", err) } if err := os.Remove(checkFile); err != nil { - return fmt.Errorf("删除失败: %s", err) + return fmt.Errorf("delete err: %s", err) } return nil @@ -1045,7 +1045,7 @@ func (this *Filesystem) MoveDirectory( if len(overwrite) > 0 && overwrite[0] && this.IsDirectory(to) { err := this.DeleteDirectory(to, false) if err != nil { - return errors.New("覆盖旧文件操作失败") + return errors.New("overwrite fail") } } @@ -1065,10 +1065,10 @@ func MoveDirectory( func (this *Filesystem) CopyDirectory(directory string, destination string) error { // 检测目录正确性 if srcInfo, err := os.Stat(directory); err != nil { - return errors.New("原始目录不是一个正确的目录!原因为:" + err.Error()) + return err } else { if !srcInfo.IsDir() { - e := errors.New("原始目录不是一个正确的目录!") + e := errors.New("src is not dir path") return e } } @@ -1078,15 +1078,15 @@ func (this *Filesystem) CopyDirectory(directory string, destination string) erro // 创建目录 err := os.MkdirAll(destination, os.ModePerm) if err != nil { - return errors.New("创建目录失败!原因为:" + err.Error()) + return err } } if destInfo, err := os.Stat(destination); err != nil { - return errors.New("目标目录不是一个正确的目录!原因为:" + err.Error()) + return err } else { if !destInfo.IsDir() { - e := errors.New("目标目录不是一个正确的目录!") + e := errors.New("dest is not dir path") return e } } @@ -1121,11 +1121,11 @@ func CopyDirectory(directory string, destination string) error { // 删除文件夹 func (this *Filesystem) DeleteDirectory(directory string, preserve ...bool) error { if !this.IsDirectory(directory) { - return errors.New("文件夹删除失败, 当前文件不是文件夹类型") + return errors.New("not dir path") } if err := os.RemoveAll(directory); err != nil { - return errors.New("文件夹删除失败, 错误为:" + err.Error()) + return err } newPreserve := false