package utils import ( "b612.me/starcrypto" "crypto" "crypto/ecdh" "crypto/ed25519" "crypto/elliptic" "crypto/rand" "crypto/sha512" "crypto/tls" "crypto/x509" "crypto/x509/pkix" "encoding/hex" "encoding/pem" "errors" "fmt" "golang.org/x/crypto/pbkdf2" "io" "math/big" "net" "os" "strings" "time" ) type GenerateCertParams struct { Country string `json:"country"` Province string `json:"province"` City string `json:"city"` Organization string `json:"organization"` OrganizationUnit string `json:"organization_unit"` CommonName string `json:"common_name"` Dns []string `json:"dns"` KeyUsage int `json:"key_usage"` ExtendedKeyUsage []int `json:"extended_key_usage"` IsCA bool `json:"is_ca"` StartDate time.Time `json:"start_date"` EndDate time.Time `json:"end_date"` MaxPathLength int `json:"max_path_length"` MaxPathLengthZero bool `json:"max_path_length_zero"` Type string `json:"type"` // e.g., "x509", "ssh" Bits int `json:"bits"` // e.g., 2048, 4096 CA *x509.Certificate CAPriv any // private key for CA, if nil, use the generated key } func GenerateKeys(types string, bits int) (crypto.PublicKey, any, error) { var priv any var pub crypto.PublicKey var err error switch strings.ToLower(types) { case "rsa": priv, pub, err = starcrypto.GenerateRsaKey(bits) if err != nil { return nil, nil, err } case "ecdsa": var cr elliptic.Curve switch bits { case 224: cr = elliptic.P224() case 256: cr = elliptic.P256() case 384: cr = elliptic.P384() case 521: cr = elliptic.P521() default: return nil, nil, errors.New("invalid bits,should be 224,256,384,521") } priv, pub, err = starcrypto.GenerateEcdsaKey(cr) if err != nil { return nil, nil, err } case "ed25519": pub, priv, err = ed25519.GenerateKey(rand.Reader) if err != nil { return nil, nil, err } case "x25519": priv, err = ecdh.X25519().GenerateKey(rand.Reader) if err != nil { return nil, nil, err } pub = priv.(*ecdh.PrivateKey).Public() case "ecdh": switch bits { case 256: priv, err = ecdh.P256().GenerateKey(rand.Reader) if err != nil { return nil, nil, err } pub = priv.(*ecdh.PrivateKey).Public() case 384: priv, err = ecdh.P384().GenerateKey(rand.Reader) if err != nil { return nil, nil, err } pub = priv.(*ecdh.PrivateKey).Public() case 521: priv, err = ecdh.P521().GenerateKey(rand.Reader) if err != nil { return nil, nil, err } pub = priv.(*ecdh.PrivateKey).Public() } default: return nil, nil, errors.New("invalid key type,only support rsa,ecdsa") } return pub, priv, nil } func getSlice(val string) []string { if val == "" { return nil } return []string{val} } func GenerateCert(params GenerateCertParams) ([]byte, []byte, error) { var needAppendCa bool = true pub, priv, err := GenerateKeys(params.Type, params.Bits) if err != nil { return nil, nil, err } var extKeyUsage []x509.ExtKeyUsage = nil for _, usage := range params.ExtendedKeyUsage { if extKeyUsage == nil { extKeyUsage = make([]x509.ExtKeyUsage, 0, len(params.ExtendedKeyUsage)) } extKeyUsage = append(extKeyUsage, x509.ExtKeyUsage(usage)) } var trueDNS []string var trueIp []net.IP for _, v := range params.Dns { ip := net.ParseIP(v) if ip == nil { trueDNS = append(trueDNS, v) continue } trueIp = append(trueIp, ip) } cert := &x509.Certificate{ Version: 3, SerialNumber: big.NewInt(time.Now().Unix()), Subject: pkix.Name{ Country: getSlice(params.Country), Province: getSlice(params.Province), Locality: getSlice(params.City), Organization: getSlice(params.Organization), OrganizationalUnit: getSlice(params.OrganizationUnit), CommonName: params.CommonName, }, NotBefore: params.StartDate, NotAfter: params.EndDate, BasicConstraintsValid: true, IsCA: params.IsCA, MaxPathLenZero: params.MaxPathLengthZero, ExtKeyUsage: extKeyUsage, KeyUsage: x509.KeyUsage(params.KeyUsage), MaxPathLen: params.MaxPathLength, DNSNames: trueDNS, IPAddresses: trueIp, } if params.CA == nil { params.CA = cert needAppendCa = false } if params.CAPriv == nil { params.CAPriv = priv } certs, err := x509.CreateCertificate(rand.Reader, cert, params.CA, pub, params.CAPriv) if err != nil { return nil, nil, err } certBlock := &pem.Block{ Type: "CERTIFICATE", Bytes: certs, } pemData := pem.EncodeToMemory(certBlock) privData, err := starcrypto.EncodePrivateKey(priv, "") if err != nil { return nil, nil, err } if needAppendCa { pemData = append(pemData, []byte("\n")...) certBlock = &pem.Block{ Type: "CERTIFICATE", Bytes: params.CA.Raw, } pemData = append(pemData, pem.EncodeToMemory(certBlock)...) } return pemData, privData, nil } func GenerateTlsCert(params GenerateCertParams) (tls.Certificate, error) { certPem, privPem, err := GenerateCert(params) if err != nil { return tls.Certificate{}, err } return tls.X509KeyPair(certPem, privPem) } func ToolCert(aesKey string) (*x509.Certificate, any) { if aesKey == "" { aesKey = os.Getenv("B612KEY") } if aesKey == "" { return nil, nil } certEncPem := `6af8053260579d345179c6313d99f18a6f85fa74d90b3cc61031211b3a7ced2037a4e317299ada633d7ca925481aba6bc814ee36330261d770040bcffb28d1c15be792b811bbd2d49476d8abc28f3f7e51c9c6c8a306e8443755e699aa98e49fafd886d0f5edec797d6c645fdde7a6f88fba13aeb5e4963d5abf642995577582999422f503740d8f961d03213bbf5b08529ff5a548ef4478b3ef23dfe007be109d17ebc653b34a26d3bef96d7e2085860ab7091863aa964239dcd806ca609fcba7fa9e725549bd42aabab4dd9aaae2e5e4e1427af58e6032cd1ee8ab845910a9fbb14fb4680883aa39d1aef3bcb2725c42ff150c90cb948f33f5d6be6ee99c2c76b900d182695c0150f46dbf43d589e268ddf5d89c855b24a6d9f8080502bdebb5f3e4b61839fe40158d73d7c44ef43c7c382ab3e739e6e0c36fd8e0adb4abdc81c61e01ccbdbc7995b14d7450ed56454e1e42268f516ed2d39af40bbdc6dc9ca97d55e87f187984646a499779466f8af389f15836d7967883aec5442d789ce26c5db9a407241d20fc40554f76289ee2008aa0a85ed3f112b8f36acda8226e72a63eb02943749b1f86b82311509b872db3c5cd99a40946cbf70b9a38385da1d743d58afa7697fc18f478f0cce854e37b4d6e837850bc6d16f951c41412ee2e2eb3113f48912b02c279425c4c3f90795736cb13a824918f744e747eb1677b89e641a3f109198d7375d741172b0cda449290c1d636267952ee006fae5a95163fe38d0a132f761490585a2a952f18f9e4a8a21864ec9831deda2685a182473eba812df82b23096c5fabaea2d6eae89751de9708706fe554aabbcb3ad90fa1fa3776a6cc6927340b404990aebb8fa2d26576db1861b6a057d0414986776d0e3a75c9d2eaed0bc530e41767599ec1c223f845f535d7195940516f7c921ec56d31f8d9f0ecd4b1b1fcf16d9659eef0f77db8636043dcdbd31de364e39ec3d0123f1959604144f32ce896274cd1e80900b64addd652d1167dfc136bfb669145aa5855679e8322ce5b17c272715c70150d8c542f8bdb2e2786e0d1bc79fe1b55510c1dd22d96abc9eef16ae95b52d4d0a935edd0bd1ee3fad9a4822ca4b6c0927d925d97b3843166707edb63fc3635250a93072a890299569f7c699685e29b9c2598891654283287801e20147096cabc37704bbd18d7e67c4d8735bcaeb3118a0dd665fcdda9906a86b94f1cc3d5f15b4d2137b4a7e849b82dd9d1d42947f4535f76ae90068a06b3223863a5773203469cb0fe5fbf326ec6fdeb4be28f829e96e3e35d56c8d53cad3e06222b53ec1d3d9663dfd8737ec73cadba04cd230000bb6dd8cc8d4521da52ee2831e1ed7ac1555b7a9cae1d46781cf262eec4940f9d0e53b63189495583d2b793e65aac806da0cb106f8443c782b96d60a2f7fcbcd459dfc622e374f7534b26817abb3aefcf672ed728e830e7ff095eb4dfc1ed1d9e2e6b864815b1c40e51adc7d767afd670c982d44318a03b25f6629f8082430c5b023b51873067b01a89b022213a5c3006d3bf1a2c5b5505926c8fb0ddf147470e40d56133e4a48b9eafb18b806f14419f8c5a145223872f700fb48601a9bc252f4b538d4e7641e93da13b0f945e6cdaa1927815e3183df589c641363f7623a9924f1af35eb61a91ac5fc19d25f850db067bd2c4218b149d19146bc13505cdc5b5e11fc69302e97ed9e50f75e22a2bb3580a04bc846e2d7230232ea9c7eb1d1b63672a303e6940a2323d636514e60e137129646dea9781b75e7b85639e6d0093e8a9934a4882342c77aff8d5d6be4b983dcd32c649461c07ecb14123b5cfa7b916ebea8c8d925f42efc2ed684644ffa266149df7ec8b11dedb1b80b2e43d9132ce111b31710e4e23db9d6d31351a2fc1fd46a822a71269725a47320155b38267935a622c0d2cacc7603531bf0324ea2fe316a708bf714e2775fd57ec8feac2d5657d6d97167d37050bee0721f8db39159bb50409bf082c12c025f236e20bd9e5f10e5168aba2474cfce478acae36157173e176e47f659bad53458442f2dd691a1c9e71f1dd4463bf5e7a8efc016cca5b6fc3319196a7216ee49721fcb3d5ae055e92f4d4ecfdd343c3ad9e32aaf9dcf7dfa519867c1a9c5cc583912453e52031b1b2caa97f517805d5797a2716ea1e7ff599a8cfe507360e8c8601b19779565bfd95e0dfc30ce7ce1213acec48427000cc2b3e66f0181634fa95e74626fee0cbe9a5f73241c98de079cb42d6eb6e1d2371503142d5193f22fbaab051fd57c7a55ea761be35451410d698a47e5bb0dba019696adbdfda67278d160d319aa7699afcfa9aef1c69751d0f70568537849d86e8775577874f605bcb4af6de5f90e427b0b2ac1decd9b0b88b18bdb7d002006ba3c3ccf9a99f65b82347ffdfef2d123eaba8aaa3df71ea57bae3cfb0773ee61cfe7aba4cb7979d3a01ed3fdf42b5add0c2ed2f5dd02d21ec3939a437d48ea06fc1d333f742e31c8a9335aca24679657a69c63f20cee19416d24b42991241a0186ccabd60cf418f4559a33641629d3101001cc1bebd63c306286fcbd36ae8628d02742160ceb38666a5a37f11862cd8686c512a32e87608328acc15c203a302ddb637963ee0d594b866b52f9fa4747d8b24970470b88fe4be0891222adf2a4f96ed79239cb91f5a9e614a77c3ccd95e99360a4a062f399fd9b446af3f3aae277d3223f4998c7aa1281cf2543a1e3c8e74e4c8b3c1c0e54596c5d2a651e6cab34053544977a3958908283e1ec7907aa938fa361fd8770641747dce451bf789dc02e80549ade940ed59cddeaf3c6931243dc362672c5e715514afb39d0c36f0a19398848e6e8a51919a441eb068c0e24ea12df7552456e91bf7c9402d2f85bf316a19704e29d848598c3cd9b7ce0652a29dcf8fd7a2c040bc17d59213af399e3b42f8b1c1ccd20467396b73b2046822c139617cf5a473807156975663747df2fcdc3fed568ca8967fe961a2c7a8c1883e18bbd00383078894ccfce540003fb9ba76f8abad8326a87b29bff3ff5eb0013688f0208369181a2c56c6754d9b4e13083555957a479e` privEncPem := `bcb7368d722f4fd05e0b79946520a106ed891ad31a4a02adcfe6a8ecf5d39db4d87f23d690ef5e31ae99a4210e1429404ec54cdae3a2967d61967bdfe7c97d17d4b359d5d1c28f46423e02073210ce4b90ade1c23c5398e82456ffb31b36a2968730e1a994f48b38a6f6eedcf76da2d99bcb9087701058ceaf1937cc47b6246cc42faf8b8dd44dc79b82f8facd6b400391fcc4c2d2252b469222151d4d0bd9e0c5a553f791b87e8a29517334e866a94421dfda20a2c937f200b414821fe97c6decac9dbee42d21c9224a3bb8e965e417cd2fa40aba60c2c7c84078e4cf4c8d18d196d1dbd51d9108616a8c33474c6ddd89e04aa53bf379ab09b639b8beb7160825642712e791b5b4e4da29f063f57e75bbb640bd8bd50c58652a787d3f299c1b275ab62cbba438d02cb24578ed0bde35f106c5d4b75f9b447f0f3f41cc6e82f779e1aa7bd08a555724f346ea810c8be47a455f8802ed7eee95268ac5ef4a5842ccd9dc0928c7bc3a9e8075af858e221eb93b1bd19b0d66e5058878896209d8f2f45d103521c77f7326c3fca4539ab821636b4de8199e5637dd8ef221c3dede621294a704eb30b613c9c803b16d27694066294cc4cf833a1ac5b0e5e3b27f08eca3d453ad0db99305621538f8f630c08902c8bd5cd0fa8d2651c11701e3ce66794257d75ac79c633d1bf5c92e8d21e2eb001ba1e79fe6ff56eebdc75301e8176398c5c35e831fb9925f2e16300fa6639ad248d2647c2ff82f9f47f776d7c578fc69eae2a54a4ab1516123e6a0aaeb85c881b1994af44c1e6d8f48dc8adcdeb03a80cc142ced5c064b94308ebf871e75784a1b80ec49d1224ed601fe7bc9101fab80b1cc6c9b818b1caea42be140f29b5f6e9e8a70fe3be1d8d208f565b16d1fe6dc3022f9acf437a605a6900fbb3775ce6eac4e55f67c1ab041c998df131c15d4fa0dcf525f0bdceb8f8d63426c315aadce0e61e28cb9bab18a8e88eb248d61deb81a7aa4ae636de0f44f0e4d12ba866bb857d189bf5759e190834b29cc521bff4903159b0cc2d5ee7e09147a7cc04d09a0c32d4f6c3344ecfc44958c055d7109f78798939f1d32ffbb33e1a1176d21250f3d419262babdec315a0e0443fd394210fab868cd57ad0527fa9b6452e14bedca1b61734e4215699116e902e3612f8d6731fa5b9b02a05f07a20602f67ba118bf7ab9059f545eac5719b15c7c0729fbbdcdca6605115a5e50e25d1f8e74b4a01918ca6c6d69eae3e6bb2cbf2104c271eda3688ee60fe2baabdb8a40caca13dbd51c9e35cd06944b3ed7ded769fe56ed4fc3e2fa26938140c666ce9c781944616dbfb5b2605fe4ae16884fb0c0fc92b6af42092261c60005a43bee4a92ecf44c9451c312d7ec719cde5f28220d6d9ca465376c95005756920abbb9ef84bcad3e89bf06e6e31db7111ef3b1c6f2f283be68c9c50a5fa6a0f8080c3707d580465a4d4caf64f4f932cfb550a1361a46611aa3ec16077fec8090080814acfa583a6de894d52125b62f80cca3f32f18845134ed859a0e4afae6634873c889c567afc33d7b4454ce77f83509d25652a189ecd8e9f6d916686069db191794f35c0e5405e850c66c3edb87ecbd69407ace5ef612519f44f7cce0ef205aa7faf514db5215c819f3c6e09fe50803cff110f55e915e264b98307c593b573c9d35f535ef3dd9e4eaead45909e0b2129a21578788ae7b74bea4e7af4fc68253dfd032f5b16e6f6447c3a161b4edde5f3d08be46ac4dd33c1d9323597a42e70efb5ce41469c2eb1e77ce9d59b8bbc85039499ee26d389a43bdec0e23a14f90db7c89020347aa16063caf434dffd93afdbcd0da20c3650c88e316fea148914d4a9d0423caf2c05cded3ca607f84b56771c5b63b61be6d5260e28634f46086e18c166583a763c6f92e268088f8bc0b99611efd17a67b9e4d60e6fff9082f20f001171a63c1e8d13391e360a98f61e9474308552abe190f7d9b74c08eb0adedd28b30b249d99ab4ca3d90ec0d042b1aeeaca58e54e37017ca56a5673d710ad52f3715165bf743961b50dcd5d49d67bec59412b4b9f908417090293232b0c1994bf2753b7fa31d7bd4fb47d266c7b2b36c94be58043eed7380f646a8e5bb453983d5355f5d7b8fd8e7df9721206b85ba4e7c458981e00f793005ec61e39e73e193282c8f42ccc02ad3d9666bf2e6e18e9e9c430fd2220003fe24f96559dc26d23ed90c73a6ed85f0d32b6fe46acffafdd792fb69bfd98d5019b06d4a3ec34f166430995a95b78da961974fe88456e20ad5aec4c5506abafe014eec11ab21017d6d9e7592c9d8cbf7a647278e1b3c0806e871aa42064706c0bc4b8a12dbeaa6f5b526c7b121677ba5f5a94792df20a79c94fd6070008454ba6cf94c63b314d264c2e9f6722b6b4257d4514399c4a7934f6690be810793953a7200269bec65a36aaed3d3fcf55e5f46d7976dc719c78be05a9276a654a905963ad5c1fe0d07d3cf9e96d8ba2f453ce99e2e04b59ae462d690891755b279d16050bf85d280842582463d7675e4068f31e2bf50f157dbbaadb36f1c09028d48f5bfb7bd89f00b5424448976467d3b5435d01163b7f6106dd2c1c948e3950f42fac6a051606b70ea3709b5a0b1826b3fabb9c5feaf72244bf9c24b727b7e6ec3391b0bf3d833cf791976ded63609f731ff0aa050abce73274c6833b81b37ced46ad8d205aa07f561f4d4ee5e0fe19df88b926751369b013e24df12ded98bd5b5b02c1fd1b6b6f92fed1a5ceff3dcc06361ea03f2698e9c307d680a085d4dc5bc3e02a6da7ccc56fb69923f6a99140e02390a4e558b2905ad029281fa1fb779fa57ec6014737326984aa9a836347775e7f4682e999f3eb49573210cf463c4944a9dc51804eed4dbdc9f46fdbf2d435fc8b84cb42610d2781af25387dd81cec879a618ed253a8859a181260a0287beca963afded2817f138fcf307b438674e1c1ef2799d7bbcf990f4d84e4dcf01605a34e8dbf15275ebf92eeaa38b74e5c8858c1e0f343ea89c2b7d990b8ef0803e1c50964e1dd6e7296c5ee48a7de01b9444ae5ea28750fa502d8f52278fba028514b3da899d6b3439a882502fd3820b5272baba014b83c051b84797146e7a10d893f3bdae4abfdd4aa64f51444d234c9885b9affa66ba08214d29857fb4b37777ff2990362a44571dc68d90236414d8a863bcda2fe22e9261ebc60ca81b2d3580d3c47c3bca914232bed730da7bd8444b92c230ba7cd8cf777e60658faf0fcd34b407faa98aecd95a95d9d456538ec935945a6cf01f029c7fe097fcaac0012c59655031156621a6a0b4bbce2bfb0fb6bc6e913077e8aefb0694a4f977a3b9f240df5dbd6153400eef5c13e7708a92389175f6082321f20106d0743d884d56375a270d50cab662d5b56a5d23c34fccca94e5f25d0c099a080a7ee82ff7e1f78f44176e2c7970f00031b20ff3eec15a22991310a41fc3a0af247a826288e9a91f9ae38337a1d72791123626368b9693756cd75505242d840a61b3290b756b9a36231b1f453aad9b472cc7a5feb2bfd9fb714f9454c8aace0488177f14bf756f2a2d1b6493ee72efdbd9f3d8f3c81023f9b7d1d3ef2c27713e1af1caf3a33f0b1b3985c775b15346337aa46d42b6ba1c1d51cf3127b6c3d5a41e8ad1fa2f14f1ef94cb888cb373116c17245135ad1117027486165bfdd1b577c6329cd06997a43eaf1b680701284b391d36c94cbc709ee5ca05aad5db663a7a9b4311a994bddc15839467073815abfd89251130f72a2d24ec5a1e5f82242181ad20b8d5cca611996ca2d44c1d3edfc5a594b0a6e41fcf1a3e2bf7f1ae3b5a95f30fd7e6d10b58c486e4a8aa79a26b2ff07919ea92767105a8117d820159a472d578243bc061f30a9eef07e3c2dd9715b241ad020ae271f1041909b62be6b4aa53f6217c1352e1b3c4ac65b7845a6e6e644f690e968e5e0a6a7fc3999fc75c8f1afa5625d0d1f1b73645d4769b065551b07904af5c03226d756546d98f0557c999d30840252072912c1c4fe4d224d08fd0f460c403eb2f789c8daf51aca64ebf9040f9159cd96e60f4443015e3cbd85347a7baa9c0526c9c6e2d6e895c6341a055d4855ef14c0cdcda7bdcb1b06423d82d9517ab8ee3d23f8dc73d0bc344ffe0206ac60d9706d9d1071d34cf10909db66dabe5ebd1cff0b598d7f5eec912698098e6c6cbe2cc0040579d3bd9e556a6f04c78984d75059e4708afb178d2cf11aebed6e0dc7920e3ede3b3502c476f0478aac3e23dbd86628380591ed49f93e1c55d5aae165236517421647dbe2de18ba53693eaddda9580f1094c3d0c12283a8cc40ae6db5e226bfa87f5c6d34b521922149b2fd2f0706e4c8a14d97651e8a466bc89f1690d4a812463da3fa9703f12d7e4fd0a6ce3c3f1aa60b0b866e33f1e8917cdedc628d244ffe9067eedbbfbe037088da5fe7199160be6cf89caa3c4684ca479ae752f1c41eeee037afd97175d3477407d77a94c3c02e95b8cc166c4b3be751be01019d309bb50102cd6301c7c2bdfc60c5ea613e39650104af4c2172630498771baeceb19f41d81924bf89a53b7f502d5386fc41594` tmp, _ := hex.DecodeString(certEncPem) certPem, err := Decode(tmp, aesKey) if err != nil { return nil, nil } tmp, _ = hex.DecodeString(privEncPem) privPem, err := Decode(tmp, aesKey) if err != nil { return nil, nil } block, _ := pem.Decode([]byte(certPem)) if block == nil || block.Type != "CERTIFICATE" { return nil, nil } cert, err := x509.ParseCertificate(block.Bytes) if err != nil { return nil, nil } priv, err := starcrypto.DecodePrivateKey([]byte(privPem), "") if err != nil { fmt.Println(err) return nil, nil } return cert, priv } func Encode(data []byte, key string) ([]byte, error) { if len(data) == 0 { return nil, errors.New("data is empty") } if key == "" { return nil, errors.New("key is empty") } aesKey := pbkdf2.Key([]byte(key), []byte("b612.me"), 923876, 32, sha512.New) var iv = make([]byte, 16) _, err := io.ReadFull(rand.Reader, iv) if err != nil { return nil, fmt.Errorf("failed to generate IV: %w", err) } ciphertext, err := starcrypto.CustomEncryptAesCFBNoBlock(data, aesKey, iv) if err != nil { return nil, fmt.Errorf("failed to encrypt data: %w", err) } ciphertext = append(iv, ciphertext...) return ciphertext, nil } func Decode(data []byte, key string) ([]byte, error) { if len(data) < 16 { return nil, errors.New("data is too short") } if key == "" { return nil, errors.New("key is empty") } aesKey := pbkdf2.Key([]byte(key), []byte("b612.me"), 923876, 32, sha512.New) iv := data[:16] ciphertext := data[16:] plaintext, err := starcrypto.CustomDecryptAesCFBNoBlock(ciphertext, aesKey, iv) if err != nil { return nil, fmt.Errorf("failed to decrypt data: %w", err) } return plaintext, nil }