2025-06-13 13:05:50 +08:00
|
|
|
package utils
|
|
|
|
|
|
|
|
import (
|
|
|
|
"b612.me/starcrypto"
|
|
|
|
"crypto"
|
|
|
|
"crypto/ecdh"
|
|
|
|
"crypto/ed25519"
|
|
|
|
"crypto/elliptic"
|
|
|
|
"crypto/rand"
|
2025-06-17 13:10:35 +08:00
|
|
|
"crypto/sha512"
|
2025-06-13 13:05:50 +08:00
|
|
|
"crypto/tls"
|
|
|
|
"crypto/x509"
|
|
|
|
"crypto/x509/pkix"
|
2025-06-17 13:10:35 +08:00
|
|
|
"encoding/hex"
|
2025-06-13 13:05:50 +08:00
|
|
|
"encoding/pem"
|
|
|
|
"errors"
|
|
|
|
"fmt"
|
2025-06-19 23:47:39 +08:00
|
|
|
"golang.org/x/crypto/pbkdf2"
|
2025-06-17 13:10:35 +08:00
|
|
|
"io"
|
2025-06-13 13:05:50 +08:00
|
|
|
"math/big"
|
|
|
|
"net"
|
2025-06-17 13:10:35 +08:00
|
|
|
"os"
|
2025-06-13 13:05:50 +08:00
|
|
|
"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)
|
|
|
|
}
|
|
|
|
|
2025-06-17 13:10:35 +08:00
|
|
|
func ToolCert(aesKey string) (*x509.Certificate, any) {
|
|
|
|
if aesKey == "" {
|
|
|
|
aesKey = os.Getenv("B612KEY")
|
|
|
|
}
|
|
|
|
if aesKey == "" {
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
certEncPem := `6af8053260579d345179c6313d99f18a6f85fa74d90b3cc61031211b3a7ced2037a4e317299ada633d7ca925481aba6bc814ee36330261d770040bcffb28d1c15be792b811bbd2d49476d8abc28f3f7e51c9c6c8a306e8443755e699aa98e49fafd886d0f5edec797d6c645fdde7a6f88fba13aeb5e4963d5abf642995577582999422f503740d8f961d03213bbf5b08529ff5a548ef4478b3ef23dfe007be109d17ebc653b34a26d3bef96d7e2085860ab7091863aa964239dcd806ca609fcba7fa9e725549bd42aabab4dd9aaae2e5e4e1427af58e6032cd1ee8ab845910a9fbb14fb4680883aa39d1aef3bcb2725c42ff150c90cb948f33f5d6be6ee99c2c76b900d182695c0150f46dbf43d589e268ddf5d89c855b24a6d9f8080502bdebb5f3e4b61839fe40158d73d7c44ef43c7c382ab3e739e6e0c36fd8e0adb4abdc81c61e01ccbdbc7995b14d7450ed56454e1e42268f516ed2d39af40bbdc6dc9ca97d55e87f187984646a499779466f8af389f15836d7967883aec5442d789ce26c5db9a407241d20fc40554f76289ee2008aa0a85ed3f112b8f36acda8226e72a63eb02943749b1f86b82311509b872db3c5cd99a40946cbf70b9a38385da1d743d58afa7697fc18f478f0cce854e37b4d6e837850bc6d16f951c41412ee2e2eb3113f48912b02c279425c4c3f90795736cb13a824918f744e747eb1677b89e641a3f109198d7375d741172b0cda449290c1d636267952ee006fae5a95163fe38d0a132f761490585a2a952f18f9e4a8a21864ec9831deda2685a182473eba812df82b23096c5fabaea2d6eae89751de9708706fe554aabbcb3ad90fa1fa3776a6cc6927340b404990aebb8fa2d26576db1861b6a057d0414986776d0e3a75c9d2eaed0bc530e41767599ec1c223f845f535d7195940516f7c921ec56d31f8d9f0ecd4b1b1fcf16d9659eef0f77db8636043dcdbd31de364e39ec3d0123f1959604144f32ce896274cd1e80900b64addd652d1167dfc136bfb669145aa5855679e8322ce5b17c272715c70150d8c542f8bdb2e2786e0d1bc79fe1b55510c1dd22d96abc9eef16ae95b52d4d0a935edd0bd1ee3fad9a4822ca4b6c0927d925d97b3843166707edb63fc3635250a93072a890299569f7c699685e29b9c2598891654283287801e20147096cabc37704bbd18d7e67c4d8735bcaeb3118a0dd665fcdda9906a86b94f1cc3d5f15b4d2137b4a7e849b82dd9d1d42947f4535f76ae90068a06b3223863a5773203469cb0fe5fbf326ec6fdeb4be28f829e96e3e35d56c8d53cad3e06222b53ec1d3d9663dfd8737ec73cadba04cd230000bb6dd8cc8d4521da52ee2831e1ed7ac1555b7a9cae1d46781cf262eec4940f9d0e53b63189495583d2b793e65aac806da0cb106f8443c782b96d60a2f7fcbcd459dfc622e374f7534b26817abb3aefcf672ed728e830e7ff095eb4dfc1ed1d9e2e6b864815b1c40e51adc7d767afd670c982d44318a03b25f6629f8082430c5b023b51873067b01a89b022213a5c3006d3bf1a2c5b5505926c8fb0ddf147470e40d56133e4a48b9eafb18b806f14419f8c5a145223872f700fb48601a9bc252f4b538d4e7641e93da13b0f945e6cdaa1927815e3183df589c641363f7623a9924f1af35eb61a91ac5fc19d25f850db067bd2c4218b149d19146bc13505cdc5b5e11fc69302e97ed9e50f75e22a2bb3580a04bc846e2d7230232ea9c7eb1d1b63672a303e6940a2323d636514e60e137129646dea9781b75e7b85639e6d0093e8a9934a4882342c77aff8d5d6be4b983dcd32c649461c07ecb14123b5cfa7b916ebea8c8d925f42efc2ed684644ffa266149df7ec8b11dedb1b80b2e43d9132ce111b31710e4e23db9d6d31351a2fc1fd46a822a71269725a47320155b38267935a622c0d2cacc7603531bf0324ea2fe316a708bf714e2775fd57ec8feac2d5657d6d97167d37050bee0721f8db39159bb50409bf082c12c025f236e20bd9e5f10e5168aba2474cfce478acae36157173e176e47f659bad53458442f2dd691a1c9e71f1dd4463bf5e7a8efc016cca5b6fc3319196a7216ee49721fcb3d5ae055e92f4d4ecfdd343c3ad9e32aaf9dcf7dfa519867c1a9c5cc583912453e52031b1b2caa97f517805d5797a2716ea1e7ff599a8cfe507360e8c8601b19779565bfd95e0dfc30ce7ce1213acec48427000cc2b3e66f0181634fa95e74626fee0cbe9a5f73241c98de079cb42d6eb6e1d2371503142d5193f22fbaab051fd57c7a55ea761be35451410d698a47e5bb0dba019696adbdfda67278d160d319aa7699afcfa9aef1c69751d0f70568537849d86e8775577874f605bcb4af6de5f90e427b0b2ac1decd9b0b88b18bdb7d002006ba3c3ccf9a99f65b82347ffdfef2d123eaba8aaa3df71ea57bae3cfb0773ee61cfe7aba4cb7979d3a01ed3fdf42b5add0c2ed2f5dd02d21ec3939a437d48ea06fc1d333f742e31c8a9335aca24679657a69c63f20cee19416d24b42991241a0186ccabd60cf418f4559a33641629d3101001cc1bebd63c306286fcbd36ae8628d02742160ceb38666a5a37f11862cd8686c512a32e87608328acc15c203a302ddb637963ee0d594b866b52f9fa4747d8b24970470b88fe4be0891222adf2a4f96ed79239cb91f5a9e614a77c3ccd95e99360a4a062f399fd9b446af3f3aae277d3223f4998c7aa1281cf2543a1e3c8e74e4c8b3c1c0e54596c5d2a651e6cab34053544977a3958908283e1ec7907aa938fa361fd8770641747dce451bf789dc02e80549ade940ed59cddeaf3c6931243dc362672c5e715514afb39d0c36f0a19398848e6e8a51919a441eb068c0e
|
|
|
|
privEncPem := `bcb7368d722f4fd05e0b79946520a106ed891ad31a4a02adcfe6a8ecf5d39db4d87f23d690ef5e31ae99a4210e1429404ec54cdae3a2967d61967bdfe7c97d17d4b359d5d1c28f46423e02073210ce4b90ade1c23c5398e82456ffb31b36a2968730e1a994f48b38a6f6eedcf76da2d99bcb9087701058ceaf1937cc47b6246cc42faf8b8dd44dc79b82f8facd6b400391fcc4c2d2252b469222151d4d0bd9e0c5a553f791b87e8a29517334e866a94421dfda20a2c937f200b414821fe97c6decac9dbee42d21c9224a3bb8e965e417cd2fa40aba60c2c7c84078e4cf4c8d18d196d1dbd51d9108616a8c33474c6ddd89e04aa53bf379ab09b639b8beb7160825642712e791b5b4e4da29f063f57e75bbb640bd8bd50c58652a787d3f299c1b275ab62cbba438d02cb24578ed0bde35f106c5d4b75f9b447f0f3f41cc6e82f779e1aa7bd08a555724f346ea810c8be47a455f8802ed7eee95268ac5ef4a5842ccd9dc0928c7bc3a9e8075af858e221eb93b1bd19b0d66e5058878896209d8f2f45d103521c77f7326c3fca4539ab821636b4de8199e5637dd8ef221c3dede621294a704eb30b613c9c803b16d27694066294cc4cf833a1ac5b0e5e3b27f08eca3d453ad0db99305621538f8f630c08902c8bd5cd0fa8d2651c11701e3ce66794257d75ac79c633d1bf5c92e8d21e2eb001ba1e79fe6ff56eebdc75301e8176398c5c35e831fb9925f2e16300fa6639ad248d2647c2ff82f9f47f776d7c578fc69eae2a54a4ab1516123e6a0aaeb85c881b1994af44c1e6d8f48dc8adcdeb03a80cc142ced5c064b94308ebf871e75784a1b80ec49d1224ed601fe7bc9101fab80b1cc6c9b818b1caea42be140f29b5f6e9e8a70fe3be1d8d208f565b16d1fe6dc3022f9acf437a605a6900fbb3775ce6eac4e55f67c1ab041c998df131c15d4fa0dcf525f0bdceb8f8d63426c315aadce0e61e28cb9bab18a8e88eb248d61deb81a7aa4ae636de0f44f0e4d12ba866bb857d189bf5759e190834b29cc521bff4903159b0cc2d5ee7e09147a7cc04d09a0c32d4f6c3344ecfc44958c055d7109f78798939f1d32ffbb33e1a1176d21250f3d419262babdec315a0e0443fd394210fab868cd57ad0527fa9b6452e14bedca1b61734e4215699116e902e3612f8d6731fa5b9b02a05f07a20602f67ba118bf7ab9059f545eac5719b15c7c0729fbbdcdca6605115a5e50e25d1f8e74b4a01918ca6c6d69eae3e6bb2cbf2104c271eda3688ee60fe2baabdb8a40caca13dbd51c9e35cd06944b3ed7ded769fe56ed4fc3e2fa26938140c666ce9c781944616dbfb5b2605fe4ae16884fb0c0fc92b6af42092261c60005a43bee4a92ecf44c9451c312d7ec719cde5f28220d6d9ca465376c95005756920abbb9ef84bcad3e89bf06e6e31db7111ef3b1c6f2f283be68c9c50a5fa6a0f8080c3707d580465a4d4caf64f4f932cfb550a1361a46611aa3ec16077fec8090080814acfa583a6de894d52125b62f80cca3f32f18845134ed859a0e4afae6634873c889c567afc33d7b4454ce77f83509d25652a189ecd8e9f6d916686069db191794f35c0e5405e850c66c3edb87ecbd69407ace5ef612519f44f7cce0ef205aa7faf514db5215c819f3c6e09fe50803cff110f55e915e264b98307c593b573c9d35f535ef3dd9e4eaead45909e0b2129a21578788ae7b74bea4e7af4fc68253dfd032f5b16e6f6447c3a161b4edde5f3d08be46ac4dd33c1d9323597a42e70efb5ce41469c2eb1e77ce9d59b8bbc85039499ee26d389a43bdec0e23a14f90db7c89020347aa16063caf434dffd93afdbcd0da20c3650c88e316fea148914d4a9d0423caf2c05cded3ca607f84b56771c5b63b61be6d5260e28634f46086e18c166583a763c6f92e268088f8bc0b99611efd17a67b9e4d60e6fff9082f20f001171a63c1e8d13391e360a98f61e9474308552abe190f7d9b74c08eb0adedd28b30b249d99ab4ca3d90ec0d042b1aeeaca58e54e37017ca56a5673d710ad52f3715165bf743961b50dcd5d49d67bec59412b4b9f908417090293232b0c1994bf2753b7fa31d7bd4fb47d266c7b2b36c94be58043eed7380f646a8e5bb453983d5355f5d7b8fd8e7df9721206b85ba4e7c458981e00f793005ec61e39e73e193282c8f42ccc02ad3d9666bf2e6e18e9e9c430fd2220003fe24f96559dc26d23ed90c73a6ed85f0d32b6fe46acffafdd792fb69bfd98d5019b06d4a3ec34f166430995a95b78da961974fe88456e20ad5aec4c5506abafe014eec11ab21017d6d9e7592c9d8cbf7a647278e1b3c0806e871aa42064706c0bc4b8a12dbeaa6f5b526c7b121677ba5f5a94792df20a79c94fd6070008454ba6cf94c63b314d264c2e9f6722b6b4257d4514399c4a7934f6690be810793953a7200269bec65a36aaed3d3fcf55e5f46d7976dc719c78be05a9276a654a905963ad5c1fe0d07d3cf9e96d8ba2f453ce99e2e04b59ae462d690891755b279d16050bf85d280842582463d7675e4068f31e2bf50f157dbbaadb36f1c09028d48f5bfb7bd89f00b5424448976467d3b5435d01163b7f6106dd2c1c948e3950f42fac6a051606b70ea3709b5a0b1826b3fabb9c5feaf72244bf9c24b727b7e6ec3391b0bf3d833cf791976ded63609f731ff0aa050abce73274c6833b81b37ced46ad8d205aa07f561f4d4ee5e0fe19df88b926751369b013e24df12ded98bd5b5b02c1fd1b6b6f92fed1a5ceff3dcc06361ea03f2698e9c307d680a085d4dc5bc3e02a6da7ccc56fb69923f6a99140e02390a4e558b2905ad029281fa1fb779fa57ec6014737326984aa9a836347775e7f4682e999f3eb495732
|
|
|
|
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
|
|
|
|
}
|
2025-06-13 13:05:50 +08:00
|
|
|
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
|
|
|
|
}
|
2025-06-17 13:10:35 +08:00
|
|
|
|
|
|
|
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")
|
|
|
|
}
|
2025-06-19 23:47:39 +08:00
|
|
|
aesKey := pbkdf2.Key([]byte(key), []byte("b612.me"), 923876, 32, sha512.New)
|
2025-06-17 13:10:35 +08:00
|
|
|
var iv = make([]byte, 16)
|
2025-06-19 23:47:39 +08:00
|
|
|
_, err := io.ReadFull(rand.Reader, iv)
|
2025-06-17 13:10:35 +08:00
|
|
|
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")
|
|
|
|
}
|
2025-06-19 23:47:39 +08:00
|
|
|
aesKey := pbkdf2.Key([]byte(key), []byte("b612.me"), 923876, 32, sha512.New)
|
2025-06-17 13:10:35 +08:00
|
|
|
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
|
|
|
|
}
|