diff --git a/cert/cmd.go b/cert/cmd.go index 22b9375..482d423 100644 --- a/cert/cmd.go +++ b/cert/cmd.go @@ -1,6 +1,7 @@ package cert import ( + "b612.me/apps/b612/utils" "b612.me/stario" "b612.me/starlog" "crypto" @@ -205,7 +206,83 @@ var CmdFastGen = &cobra.Command{ Short: "快速生成证书", Long: "快速生成证书", Run: func(cmd *cobra.Command, args []string) { - return + if promptMode { + if fastgen.Country == "" { + fastgen.Country = stario.MessageBox("请输入国家:", "").MustString() + } + if fastgen.Province == "" { + fastgen.Province = stario.MessageBox("请输入省份:", "").MustString() + } + if fastgen.City == "" { + fastgen.City = stario.MessageBox("请输入城市:", "").MustString() + } + if fastgen.Organization == "" { + fastgen.Organization = stario.MessageBox("请输入组织:", "").MustString() + } + if fastgen.OrganizationUnit == "" { + fastgen.OrganizationUnit = stario.MessageBox("请输入组织单位:", "").MustString() + } + if fastgen.CommonName == "" { + fastgen.CommonName = stario.MessageBox("请输入通用名称:", "").MustString() + } + if fastgen.Dns == nil { + fastgen.Dns = stario.MessageBox("请输入dns名称,用逗号分割:", "").MustSliceString(",") + } + if fastgen.Type == "" { + fastgen.Type = stario.MessageBox("请输入证书类型(RSA/ECDSA):", "RSA").MustString() + } + if fastgen.Bits <= 0 { + fastgen.Bits = stario.MessageBox("请输入证书位数:", "2048").MustInt() + } + if startStr == "" { + startStr = stario.MessageBox("请输入证书开始时间,格式:2006-01-02T15:04:05Z07:00:", time.Now().Format(time.RFC3339)).MustString() + } + if endStr == "" { + endStr = stario.MessageBox("请输入证书结束时间,格式:2006-01-02T15:04:05Z07:00:", time.Now().AddDate(1, 0, 0).Format(time.RFC3339)).MustString() + } + } + var err error + fastgen.StartDate, err = time.Parse(time.RFC3339, startStr) + if err != nil { + starlog.Errorln("开始时间格式错误,格式:2006-01-02T15:04:05Z07:00", err) + os.Exit(1) + } + fastgen.EndDate, err = time.Parse(time.RFC3339, endStr) + if err != nil { + starlog.Errorln("结束时间格式错误,格式:2006-01-02T15:04:05Z07:00", err) + os.Exit(1) + } + if caCert != "" && caKey != "" { + fastgen.CAPriv, fastgen.CA, err = LoadCA(caKey, caCert, caKeyPwd) + if err != nil { + starlog.Errorln("加载CA错误", err) + os.Exit(1) + } + } + if fastgen.CAPriv == nil { + fastgen.CA, fastgen.CAPriv = utils.ToolCert("") + } + byteCrt, byteKey, err := utils.GenerateCert(fastgen) + if err != nil { + starlog.Errorln("生成证书错误", err) + os.Exit(1) + } + name := fastgen.CommonName + if name == "" { + name = "cert" + } + err = os.WriteFile(filepath.Join(savefolder, name+".crt"), byteCrt, 0644) + if err != nil { + starlog.Errorln("保存证书错误", err) + os.Exit(1) + } + starlog.Infoln("保存证书成功", filepath.Join(savefolder, name+".crt")) + err = os.WriteFile(filepath.Join(savefolder, name+".key"), byteKey, 0644) + if err != nil { + starlog.Errorln("保存私钥错误", err) + os.Exit(1) + } + starlog.Infoln("保存私钥成功", filepath.Join(savefolder, name+".key")) }, } @@ -230,15 +307,17 @@ var CmdParse = &cobra.Command{ }, } +var fastgen utils.GenerateCertParams + func init() { Cmd.AddCommand(CmdCsr) CmdCsr.Flags().BoolVarP(&promptMode, "prompt", "P", false, "是否交互模式") - CmdCsr.Flags().StringVarP(&country, "country", "c", "CN", "国家") - CmdCsr.Flags().StringVarP(&province, "province", "p", "B612", "省份") - CmdCsr.Flags().StringVarP(&city, "city", "t", "B612", "城市") + CmdCsr.Flags().StringVarP(&country, "country", "c", "", "国家") + CmdCsr.Flags().StringVarP(&province, "province", "p", "", "省份") + CmdCsr.Flags().StringVarP(&city, "city", "t", "", "城市") CmdCsr.Flags().StringVarP(&org, "org", "o", "", "组织") CmdCsr.Flags().StringVarP(&orgUnit, "orgUnit", "u", "", "组织单位") - CmdCsr.Flags().StringVarP(&name, "name", "n", "Starainrt", "通用名称") + CmdCsr.Flags().StringVarP(&name, "name", "n", "", "通用名称") CmdCsr.Flags().StringSliceVarP(&dnsName, "dnsName", "d", nil, "dns名称") CmdCsr.Flags().StringVarP(&savefolder, "savefolder", "s", "./", "保存文件夹") CmdCsr.Flags().StringVarP(&caKey, "secret-key", "k", "", "加密私钥") @@ -249,7 +328,7 @@ func init() { //CmdCsr.Flags().BoolVarP(&maxPathLenZero, "maxPathLenZero", "z", false, "允许最大路径长度为0") //CmdCsr.Flags().IntVarP(&maxPathLen, "maxPathLen", "m", 0, "最大路径长度") CmdGen.Flags().IntVarP(&keyUsage, "keyUsage", "u", 0, "证书使用类型,默认数字0,0表示数字签名和密钥加密,1表示证书签名,2表示CRL签名,4表示密钥协商,8表示数据加密") - CmdGen.Flags().IntSliceVarP(&extKeyUsage, "extKeyUsage", "e", nil, "扩展证书使用类型,默认数字0,0表示服务器认证,1表示客户端认证,2表示代码签名,3表示电子邮件保护,4表示IPSEC终端系统,5表示IPSEC隧道,6表示IPSEC用户,7表示时间戳,8表示OCSP签名,9表示Microsoft服务器网关加密,10表示Netscape服务器网关加密,11表示Microsoft商业代码签名,12表示Microsoft内核代码签名") + CmdGen.Flags().IntSliceVarP(&extKeyUsage, "extKeyUsage", "e", []int{0, 1}, "扩展证书使用类型,默认数字0和1,0表示服务器认证,1表示客户端认证,2表示代码签名,3表示电子邮件保护,4表示IPSEC终端系统,5表示IPSEC隧道,6表示IPSEC用户,7表示时间戳,8表示OCSP签名,9表示Microsoft服务器网关加密,10表示Netscape服务器网关加密,11表示Microsoft商业代码签名,12表示Microsoft内核代码签名") CmdGen.Flags().StringVarP(&caKey, "caKey", "k", "", "CA私钥") CmdGen.Flags().StringVarP(&caCert, "caCert", "C", "", "CA证书") CmdGen.Flags().StringVarP(&csr, "csr", "r", "", "证书请求") @@ -289,21 +368,27 @@ func init() { Cmd.AddCommand(CmdOpenssh) CmdFastGen.Flags().BoolVarP(&promptMode, "prompt", "P", false, "是否交互模式") - CmdFastGen.Flags().StringVarP(&country, "country", "c", "CN", "国家") - CmdFastGen.Flags().StringVarP(&province, "province", "p", "B612", "省份") - CmdFastGen.Flags().StringVarP(&city, "city", "t", "B612", "城市") - CmdFastGen.Flags().StringVarP(&org, "org", "o", "", "组织") - CmdFastGen.Flags().StringVarP(&orgUnit, "orgUnit", "u", "", "组织单位") - CmdFastGen.Flags().StringVarP(&name, "name", "n", "Starainrt", "通用名称") - CmdFastGen.Flags().StringSliceVarP(&dnsName, "dnsName", "d", nil, "dns名称") + CmdFastGen.Flags().StringVarP(&fastgen.Country, "country", "c", "", "国家") + CmdFastGen.Flags().StringVarP(&fastgen.Province, "province", "p", "", "省份") + CmdFastGen.Flags().StringVar(&fastgen.City, "city", "", "城市") + CmdFastGen.Flags().StringVarP(&fastgen.Organization, "org", "o", "", "组织") + CmdFastGen.Flags().StringVarP(&fastgen.OrganizationUnit, "orgUnit", "u", "", "组织单位") + CmdFastGen.Flags().StringVarP(&fastgen.CommonName, "name", "n", "", "通用名称") + CmdFastGen.Flags().StringSliceVarP(&fastgen.Dns, "dnsName", "d", nil, "dns名称") CmdFastGen.Flags().StringVarP(&savefolder, "savefolder", "s", "./", "保存文件夹") - CmdFastGen.Flags().IntVarP(&keyUsage, "keyUsage", "U", 0, "证书使用类型,默认数字0,0表示数字签名和密钥加密,1表示证书签名,2表示CRL签名,4表示密钥协商,8表示数据加密") - CmdFastGen.Flags().IntSliceVarP(&extKeyUsage, "extKeyUsage", "e", nil, "扩展证书使用类型,默认数字0,0表示服务器认证,1表示客户端认证,2表示代码签名,3表示电子邮件保护,4表示IPSEC终端系统,5表示IPSEC隧道,6表示IPSEC用户,7表示时间戳,8表示OCSP签名,9表示Microsoft服务器网关加密,10表示Netscape服务器网关加密,11表示Microsoft商业代码签名,12表示Microsoft内核代码签名") - CmdFastGen.Flags().BoolVarP(&isCa, "isCa", "A", false, "是否是CA") + CmdFastGen.Flags().IntVarP(&fastgen.KeyUsage, "keyUsage", "U", 0, "证书使用类型,默认数字0,0表示数字签名和密钥加密,1表示证书签名,2表示CRL签名,4表示密钥协商,8表示数据加密") + CmdFastGen.Flags().IntSliceVarP(&fastgen.ExtendedKeyUsage, "extKeyUsage", "e", []int{0, 1}, "扩展证书使用类型,默认数字0和1,0表示服务器认证,1表示客户端认证,2表示代码签名,3表示电子邮件保护,4表示IPSEC终端系统,5表示IPSEC隧道,6表示IPSEC用户,7表示时间戳,8表示OCSP签名,9表示Microsoft服务器网关加密,10表示Netscape服务器网关加密,11表示Microsoft商业代码签名,12表示Microsoft内核代码签名") + CmdFastGen.Flags().BoolVarP(&fastgen.IsCA, "isCa", "A", false, "是否是CA") CmdFastGen.Flags().StringVarP(&startStr, "start", "S", time.Now().Format(time.RFC3339), "开始时间,格式:2006-01-02T15:04:05Z07:00") CmdFastGen.Flags().StringVarP(&endStr, "end", "E", time.Now().AddDate(1, 0, 0).Format(time.RFC3339), "结束时间,格式:2006-01-02T15:04:05Z07:00") - CmdFastGen.Flags().BoolVarP(&maxPathLenZero, "maxPathLenZero", "z", false, "允许最大路径长度为0") - CmdFastGen.Flags().IntVarP(&maxPathLen, "maxPathLen", "m", 0, "最大路径长度") + CmdFastGen.Flags().BoolVarP(&fastgen.MaxPathLengthZero, "maxPathLenZero", "z", false, "允许最大路径长度为0") + CmdFastGen.Flags().IntVarP(&fastgen.MaxPathLength, "maxPathLen", "m", 0, "最大路径长度") + CmdFastGen.Flags().StringVarP(&caKey, "caKey", "K", "", "CA私钥,可以留空") + CmdFastGen.Flags().StringVarP(&caCert, "caCert", "C", "", "CA证书,可以留空") + CmdFastGen.Flags().StringVar(&caKeyPwd, "caKeyPwd", "", "CA私钥密码") + CmdFastGen.Flags().StringVarP(&fastgen.Type, "type", "t", "RSA", "证书类型,支持RSA和ECDSA") + CmdFastGen.Flags().IntVarP(&fastgen.Bits, "bits", "b", 2048, "证书位数,默认2048") + Cmd.AddCommand(CmdFastGen) } var CmdPkcs8 = &cobra.Command{ diff --git a/go.mod b/go.mod index f569c08..a446d3d 100644 --- a/go.mod +++ b/go.mod @@ -14,7 +14,7 @@ require ( b612.me/stario v0.0.10 b612.me/starlog v1.3.4 b612.me/starmap v0.0.0-20240818092703-ae61140c5062 - b612.me/starnet v0.0.0-20250612085047-7a1767214960 + b612.me/starnet v0.0.0-20250617043657-c1eaf4305803 b612.me/staros v1.1.8 b612.me/starssh v0.0.2 b612.me/startext v0.0.0-20220314043758-22c6d5e5b1cd diff --git a/go.sum b/go.sum index e21a36d..ebf2f24 100644 --- a/go.sum +++ b/go.sum @@ -17,8 +17,8 @@ b612.me/starlog v1.3.4 h1:XuVYo6NCij8F4TGSgtEuMhs1WkZ7HZNnYUgQ3nLTt84= b612.me/starlog v1.3.4/go.mod h1:37GMgkWQMOAjzKs49Hf2i8bLwdXbd9QF4zKhUxFDoSk= b612.me/starmap v0.0.0-20240818092703-ae61140c5062 h1:ImKEWAxzBYsS/YbqdVOPdUdv6b+i/lSGpipUGueXk7w= b612.me/starmap v0.0.0-20240818092703-ae61140c5062/go.mod h1:PhtO9wFrwPIHpry2CEdnVNZkrNOgfv77xrE0ZKQDkLM= -b612.me/starnet v0.0.0-20250612085047-7a1767214960 h1:LRB59HvHW6D4sAKd/P2GDgmnGyx5DM8aSGnJoEifeI0= -b612.me/starnet v0.0.0-20250612085047-7a1767214960/go.mod h1:6q+AXhYeXsIiKV+hZZmqAMn8S48QcdonURJyH66rbzI= +b612.me/starnet v0.0.0-20250617043657-c1eaf4305803 h1:ppTZxCYigi2wBElUVtvdDIlFjR4/tJT2O3X1ILjDHZA= +b612.me/starnet v0.0.0-20250617043657-c1eaf4305803/go.mod h1:6q+AXhYeXsIiKV+hZZmqAMn8S48QcdonURJyH66rbzI= b612.me/staros v1.1.8 h1:5Bpuf9q2nH75S2ekmieJuH3Y8LTqg/voxXCOiMAC3kk= b612.me/staros v1.1.8/go.mod h1:4KmokjKXFW5h1hbA4aIv5O+2FptVzBubCo7IPirfqm8= b612.me/starssh v0.0.2 h1:cYlrXjd7ZTesdZG+7XcoLsEEMROaeWMTYonScBLnvyY= diff --git a/httpreverse/service.go b/httpreverse/service.go index d65a3ce..652202d 100644 --- a/httpreverse/service.go +++ b/httpreverse/service.go @@ -102,7 +102,7 @@ func autoGenCert(hostname string) *tls.Config { return &tls.Config{Certificates: []tls.Certificate{cert}} } if toolCa == nil { - toolCa, toolCaKey = utils.ToolCert() + toolCa, toolCaKey = utils.ToolCert("") } cert, err := utils.GenerateTlsCert(utils.GenerateCertParams{ Country: "CN", diff --git a/httpserver/cmd.go b/httpserver/cmd.go index 3a88028..5e3a9d5 100644 --- a/httpserver/cmd.go +++ b/httpserver/cmd.go @@ -22,7 +22,7 @@ var speedlimit string func init() { Cmd.Flags().StringVarP(&hooks, "hook", "H", "", "fileget hook for modify") - Cmd.Flags().StringVarP(&s.port, "port", "p", "80", "监听端口") + Cmd.Flags().StringVarP(&s.port, "port", "p", "", "监听端口,http时默认80,https时默认443") Cmd.Flags().StringVarP(&s.addr, "ip", "i", "0.0.0.0", "监听ip") Cmd.Flags().StringVarP(&s.envPath, "folder", "f", "./", "本地文件地址") Cmd.Flags().StringVarP(&s.uploadFolder, "upload", "u", "", "文件上传文件夹路径") @@ -98,6 +98,13 @@ var Cmd = &cobra.Command{ if s.logpath != "" && starlog.GetWriter() == nil { starlog.SetLogFile(s.logpath, starlog.Std, true) } + if s.port == "" { + if s.cert != "" && s.key != "" || s.autoGenCert { + s.port = "443" + } else { + s.port = "80" + } + } if speedlimit != "" { speed, err := parseSpeedString(speedlimit) if err != nil { diff --git a/httpserver/server.go b/httpserver/server.go index 352d963..1b1acae 100644 --- a/httpserver/server.go +++ b/httpserver/server.go @@ -129,6 +129,15 @@ func (h *HttpServer) Run(ctx context.Context) error { server := http.Server{ Addr: h.addr + ":" + h.port, Handler: h, + ConnContext: func(ctx context.Context, c net.Conn) context.Context { + switch conn := c.(type) { + case *tls.Conn: + return context.WithValue(ctx, "istls", true) + case *starnet.Conn: + return context.WithValue(ctx, "istls", conn) + } + return context.WithValue(ctx, "istls", false) + }, } go func() { select { @@ -209,7 +218,7 @@ func autoGenCert(hostname string) *tls.Config { return &tls.Config{Certificates: []tls.Certificate{cert}} } if toolCa == nil { - toolCa, toolCaKey = utils.ToolCert() + toolCa, toolCaKey = utils.ToolCert("") } cert, err := utils.GenerateTlsCert(utils.GenerateCertParams{ Country: "CN", @@ -413,6 +422,7 @@ func (h *HttpServer) Listen(w http.ResponseWriter, r *http.Request) { path = filepath.Join(path, h.indexFile) } } + isTls := h.isTlsStr(r) now := time.Now() if h.SetUpload(w, r, path) { return @@ -421,23 +431,23 @@ func (h *HttpServer) Listen(w http.ResponseWriter, r *http.Request) { case "OPTIONS", "HEAD": err := h.BuildHeader(w, r, fullpath) if err != nil { - log.Warningf("%s %s From %s %s %.2fs %v\n", r.Method, path, r.RemoteAddr, ua, time.Since(now).Seconds(), err) + log.Warningf(isTls+"%s %s From %s %s %.2fs %v\n", r.Method, path, r.RemoteAddr, ua, time.Since(now).Seconds(), err) } else { - log.Infof("%s %s From %s %s %.2fs \n", r.Method, path, r.RemoteAddr, ua, time.Since(now).Seconds()) + log.Infof(isTls+"%s %s From %s %s %.2fs \n", r.Method, path, r.RemoteAddr, ua, time.Since(now).Seconds()) } case "GET": err := h.BuildHeader(w, r, fullpath) if err != nil { - log.Warningf("GET Header Build Failed Path:%s IP:%s Err:%v\n", path, r.RemoteAddr, err) + log.Warningf(isTls+"GET Header Build Failed Path:%s IP:%s Err:%v\n", path, r.RemoteAddr, err) } err = h.ResponseGet(log, w, r, fullpath) if err != nil { - log.Warningf("%s %s From %s %s %.2fs %v\n", r.Method, path, r.RemoteAddr, ua, time.Since(now).Seconds(), err) + log.Warningf(isTls+"%s %s From %s %s %.2fs %v\n", r.Method, path, r.RemoteAddr, ua, time.Since(now).Seconds(), err) return } - log.Infof("%s %s From %s %s %.2fs\n", r.Method, path, r.RemoteAddr, ua, time.Since(now).Seconds()) + log.Infof(isTls+"%s %s From %s %s %.2fs\n", r.Method, path, r.RemoteAddr, ua, time.Since(now).Seconds()) default: - log.Errorf("Invalid %s %s From %s %s %.2fs\n", r.Method, path, r.RemoteAddr, ua, time.Since(now).Seconds()) + log.Errorf(isTls+"Invalid %s %s From %s %s %.2fs\n", r.Method, path, r.RemoteAddr, ua, time.Since(now).Seconds()) return } } @@ -703,11 +713,33 @@ func (h *HttpServer) getSleepTime() time.Duration { } +func (h *HttpServer) isTls(r *http.Request) bool { + if r.Context().Value("istls") != nil { + if v, ok := r.Context().Value("istls").(bool); ok { + if v { + return true + } + } + if v, ok := r.Context().Value("istls").(*starnet.Conn); ok { + return v.IsTLS() + } + } + return false +} + +func (h *HttpServer) isTlsStr(r *http.Request) string { + if h.isTls(r) { + return "TLS " + } + return "PLN " +} + func (h *HttpServer) getFile(log *starlog.StarLogger, w http.ResponseWriter, r *http.Request, fullpath string) error { if !staros.Exists(fullpath) { h.Page404(w) return errors.New("File Not Found! 404 ERROR") } + isTls := h.isTlsStr(r) var lastCount int64 var lastDate time.Time = time.Now() var currentCount int64 @@ -754,7 +786,7 @@ func (h *HttpServer) getFile(log *starlog.StarLogger, w http.ResponseWriter, r * } } } - log.Infof("Tranfered File %s %d bytes (%s) to remote %v\n", r.URL.Path, + log.Infof(isTls+"Tranfered File %s %d bytes (%s) to remote %v\n", r.URL.Path, transferData, tani, r.RemoteAddr) } }() @@ -770,7 +802,7 @@ func (h *HttpServer) getFile(log *starlog.StarLogger, w http.ResponseWriter, r * ns, err := w.Write(buf[:n]) transferData += ns if err != nil { - log.Errorf("Transfer File %s to Remote Failed:%v\n", fullpath, err) + log.Errorf(isTls+"Transfer File %s to Remote Failed:%v\n", fullpath, err) return err } } @@ -805,7 +837,7 @@ func (h *HttpServer) getFile(log *starlog.StarLogger, w http.ResponseWriter, r * ns, err := w.Write(data) transferData += ns if err != nil { - log.Errorf("Transfer File %s to Remote Failed:%v\n", fullpath, err) + log.Errorf(isTls+"Transfer File %s to Remote Failed:%v\n", fullpath, err) return err } return nil @@ -816,12 +848,12 @@ func (h *HttpServer) getFile(log *starlog.StarLogger, w http.ResponseWriter, r * ns, err := w.Write(recvData) transferData += ns if err != nil { - log.Errorf("Transfer File %s to Remote Failed:%v\n", fullpath, err) + log.Errorf(isTls+"Transfer File %s to Remote Failed:%v\n", fullpath, err) return err } return nil } - log.Debugf("206 transfer mode for %v %v start %v end %v\n", r.URL.Path, r.RemoteAddr, startRange, endRange) + log.Debugf(isTls+"206 transfer mode for %v %v start %v end %v\n", r.URL.Path, r.RemoteAddr, startRange, endRange) w.WriteHeader(206) fp.Seek(int64(startRange), 0) count := startRange @@ -832,7 +864,7 @@ func (h *HttpServer) getFile(log *starlog.StarLogger, w http.ResponseWriter, r * if err == io.EOF { break } - log.Errorf("Read File %s Failed:%v\n", r.URL.Path, err) + log.Errorf(isTls+"Read File %s Failed:%v\n", r.URL.Path, err) return err } speedControl(n) @@ -840,7 +872,7 @@ func (h *HttpServer) getFile(log *starlog.StarLogger, w http.ResponseWriter, r * ns, err := w.Write(buf[:n]) transferData += ns if err != nil { - log.Errorf("Transfer File %s to Remote Failed:%v\n", r.URL.Path, err) + log.Errorf(isTls+"Transfer File %s to Remote Failed:%v\n", r.URL.Path, err) return err } } else { diff --git a/netforward/cmd.go b/netforward/cmd.go index 47b9714..a928578 100644 --- a/netforward/cmd.go +++ b/netforward/cmd.go @@ -3,6 +3,7 @@ package netforward import ( "b612.me/stario" "b612.me/starlog" + "crypto/tls" "github.com/spf13/cobra" "os" "os/signal" @@ -30,6 +31,18 @@ func init() { CmdNetforward.Flags().IntVarP(&f.UserTimeout, "user-timeout", "U", 0, "user timeout (milliseconds)") CmdNetforward.Flags().BoolVarP(&f.IgnoreEof, "ignore-eof", "E", false, "ignore eof") CmdNetforward.Flags().BoolVarP(&f.Verbose, "verbose", "v", false, "verbose mode") + + CmdNetforward.Flags().BoolVarP(&f.inTls, "in-tls", "i", false, "enable input tls") + CmdNetforward.Flags().BoolVarP(&f.outTls, "out-tls", "o", false, "enable output tls") + CmdNetforward.Flags().StringVar(&f.inTlsCert, "in-cert", "", "tls cert file") + CmdNetforward.Flags().StringVar(&f.inTlsKey, "in-key", "", "tls key file") + CmdNetforward.Flags().StringVar(&f.outTlsCert, "out-cert", "", "tls cert file") + CmdNetforward.Flags().StringVar(&f.outTlsKey, "out-key", "", "tls key file") + CmdNetforward.Flags().BoolVar(&f.inTlsSkipVerify, "in-skip", false, "skip verify input tls cert") + CmdNetforward.Flags().BoolVar(&f.outTlsSkipVerify, "out-skip", false, "skip verify output tls cert") + CmdNetforward.Flags().BoolVarP(&f.inTlsAutoGen, "in-autogen", "G", false, "auto generate input tls cert") + CmdNetforward.Flags().StringSliceVar(&f.CaCerts, "ca", []string{}, "tls ca certs") + CmdNetforward.Flags().BoolVarP(&f.allowNoTls, "allow-no-tls", "A", false, "allow no tls connection") } var CmdNetforward = &cobra.Command{ @@ -41,6 +54,7 @@ var CmdNetforward = &cobra.Command{ starlog.Errorln("please enter a target uri") os.Exit(1) } + f.certCache = make(map[string]tls.Certificate) f.RemoteURI = strings.TrimSpace(args[0]) if dialTimeout == 0 { dialTimeout = 10000 diff --git a/netforward/forward.go b/netforward/forward.go index 13ef3d4..a73e7e7 100644 --- a/netforward/forward.go +++ b/netforward/forward.go @@ -1,14 +1,19 @@ package netforward import ( + "b612.me/apps/b612/utils" "b612.me/stario" "b612.me/starlog" "b612.me/starmap" + "b612.me/starnet" "context" + "crypto/tls" + "crypto/x509" "errors" "fmt" "io" "net" + "os" "strconv" "strings" "sync" @@ -41,6 +46,23 @@ type NetForward struct { UsingKeepAlive bool Verbose bool udpListener *net.UDPConn + + inTls bool // 是否启用TLS + outTls bool // 是否启用TLS + inTlsCert string // TLS证书路径 + inTlsKey string // TLS密钥路径 + inTlsAutoGen bool // 是否自动生成TLS证书 + CaCerts []string // TLS CA证书路径 + outTlsKey string // TLS密钥路径 + outTlsCert string // TLS证书路径 + inTlsSkipVerify bool // 是否跳过TLS验证 + outTlsSkipVerify bool // 是否跳过TLS验证 + allowNoTls bool // 是否允许不使用TLS + certCache map[string]tls.Certificate + toolCa *x509.Certificate + toolCaKey any + caPool *x509.CertPool + outTlsCertCache tls.Certificate } func (n *NetForward) UdpListener() *net.UDPConn { @@ -133,15 +155,110 @@ func (n *NetForward) Run() error { return nil } -func (n *NetForward) runTCP() error { - atomic.AddInt32(&n.running, 1) - defer atomic.AddInt32(&n.running, -1) +func (n *NetForward) TcpListener() (net.Listener, error) { + if n.outTls && n.outTlsCert != "" && n.outTlsKey != "" { + cert, err := tls.LoadX509KeyPair(n.outTlsCert, n.outTlsKey) + if err != nil { + starlog.Errorln("Load X509 Key Pair Failed:", err) + return nil, err + } + n.outTlsCertCache = cert + } cfg := net.ListenConfig{ Control: func(network, address string, c syscall.RawConn) error { return c.Control(SetReUseAddr) }, } - listen, err := cfg.Listen(context.Background(), "tcp", fmt.Sprintf("%s:%d", n.LocalAddr, n.LocalPort)) + listener, err := cfg.Listen(context.Background(), "tcp", fmt.Sprintf("%s:%d", n.LocalAddr, n.LocalPort)) + if !n.inTls { + return listener, err + } + var caPool *x509.CertPool + if n.inTlsAutoGen { + if n.toolCa == nil { + n.toolCa, n.toolCaKey = utils.ToolCert("") + if n.toolCa != nil { + caPool = x509.NewCertPool() + caPool.AddCert(n.toolCa) + } + } + } + if len(n.CaCerts) > 0 { + if caPool == nil { + caPool = x509.NewCertPool() + } + for _, ca := range n.CaCerts { + data, err := os.ReadFile(ca) + if err != nil { + starlog.Errorln("Read CA Cert Failed:", err) + listener.Close() + return nil, err + } + if !caPool.AppendCertsFromPEM(data) { + starlog.Errorln("Append CA Cert Failed:", ca) + listener.Close() + return nil, fmt.Errorf("append ca cert %s failed", ca) + } + } + n.caPool = caPool + } + var tlsConfig = &tls.Config{ + Certificates: nil, + RootCAs: caPool, + InsecureSkipVerify: n.inTlsSkipVerify, + } + if !n.inTlsAutoGen && (n.inTlsCert != "" || n.inTlsKey != "") { + cert, err := tls.LoadX509KeyPair(n.inTlsCert, n.inTlsKey) + if err != nil { + starlog.Errorln("Load X509 Key Pair Failed:", err) + listener.Close() + return nil, err + } + tlsConfig.Certificates = []tls.Certificate{cert} + } + if n.inTlsAutoGen { + return starnet.ListenWithListener(listener, tlsConfig, n.autoGenCert, n.allowNoTls) + } + return starnet.ListenWithListener(listener, tlsConfig, nil, n.allowNoTls) +} + +func (n *NetForward) autoGenCert(hostname string) *tls.Config { + if cert, ok := n.certCache[hostname]; ok { + return &tls.Config{Certificates: []tls.Certificate{cert}} + } + if n.toolCa == nil { + n.toolCa, n.toolCaKey = utils.ToolCert("") + } + cert, err := utils.GenerateTlsCert(utils.GenerateCertParams{ + Country: "CN", + Organization: "B612 HTTP SERVER", + OrganizationUnit: "cert@b612.me", + CommonName: hostname, + Dns: []string{hostname}, + KeyUsage: int(x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment | x509.KeyUsageCertSign), + ExtendedKeyUsage: []int{ + int(x509.ExtKeyUsageServerAuth), + int(x509.ExtKeyUsageClientAuth), + }, + IsCA: false, + StartDate: time.Now().Add(-24 * time.Hour), + EndDate: time.Now().AddDate(1, 0, 0), + Type: "RSA", + Bits: 2048, + CA: n.toolCa, + CAPriv: n.toolCaKey, + }) + if err != nil { + return nil + } + n.certCache[hostname] = cert + return &tls.Config{Certificates: []tls.Certificate{cert}} +} + +func (n *NetForward) runTCP() error { + atomic.AddInt32(&n.running, 1) + defer atomic.AddInt32(&n.running, -1) + listen, err := n.TcpListener() if err != nil { starlog.Errorln("Listening On Tcp Failed:", err) return err @@ -167,7 +284,13 @@ func (n *NetForward) runTCP() error { log.Infof("Delay %d ms\n", n.DelayMilSec) time.Sleep(time.Millisecond * time.Duration(n.DelayMilSec)) } - err = SetTcpInfo(conn.(*net.TCPConn), n.UsingKeepAlive, n.KeepAliveIdel, n.KeepAlivePeriod, n.KeepAliveCount, n.UserTimeout) + switch c := conn.(type) { + case *net.TCPConn: + err = SetTcpInfo(c, n.UsingKeepAlive, n.KeepAliveIdel, n.KeepAlivePeriod, n.KeepAliveCount, n.UserTimeout) + case *starnet.Conn: + err = SetTcpInfo(c.Conn.(*net.TCPConn), n.UsingKeepAlive, n.KeepAliveIdel, n.KeepAlivePeriod, n.KeepAliveCount, n.UserTimeout) + } + if err != nil { log.Errorf("SetTcpInfo error for %s: %v\n", conn.RemoteAddr().String(), err) conn.Close() @@ -187,6 +310,24 @@ func (n *NetForward) runTCP() error { return } log.Infof("TCP Connect %s <==> %s\n", conn.RemoteAddr().String(), rmt.RemoteAddr().String()) + if n.outTls { + serverName, _, _ := net.SplitHostPort(n.RemoteURI) + tlsConfig := &tls.Config{ + InsecureSkipVerify: n.outTlsSkipVerify, + RootCAs: n.caPool, + ServerName: serverName, + } + if n.outTlsCert != "" && n.outTlsKey != "" { + tlsConfig.Certificates = []tls.Certificate{n.outTlsCertCache} + } + rmt = tls.Client(rmt, tlsConfig) + if err := rmt.(*tls.Conn).Handshake(); err != nil { + log.Errorf("TLS Handshake Failed: %v\n", err) + conn.Close() + rmt.Close() + return + } + } n.copy(rmt, conn) log.Noticef("TCP Connection Closed %s <==> %s\n", conn.RemoteAddr().String(), n.RemoteURI) conn.Close() diff --git a/smtpserver/smtp.go b/smtpserver/smtp.go index ed84577..07b576e 100644 --- a/smtpserver/smtp.go +++ b/smtpserver/smtp.go @@ -209,7 +209,7 @@ func run() { return } } else { - ca, cakey := utils.ToolCert() + ca, cakey := utils.ToolCert("") genCert, err := utils.GenerateTlsCert(utils.GenerateCertParams{ Country: "CN", Organization: "B612 SMTP SERVER", diff --git a/utils/cert.go b/utils/cert.go index 89aba9a..834b35f 100644 --- a/utils/cert.go +++ b/utils/cert.go @@ -6,15 +6,20 @@ import ( "crypto/ecdh" "crypto/ed25519" "crypto/elliptic" + "crypto/pbkdf2" "crypto/rand" + "crypto/sha512" "crypto/tls" "crypto/x509" "crypto/x509/pkix" + "encoding/hex" "encoding/pem" "errors" "fmt" + "io" "math/big" "net" + "os" "strings" "time" ) @@ -198,93 +203,25 @@ func GenerateTlsCert(params GenerateCertParams) (tls.Certificate, error) { return tls.X509KeyPair(certPem, privPem) } -func ToolCert() (*x509.Certificate, any) { - certPem := `-----BEGIN CERTIFICATE----- -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 -EdAtgNNfLagVhHl7yF8TGiLUPgszinPBDd0= ------END CERTIFICATE-----` - privPem := `-----BEGIN RSA PRIVATE KEY----- -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 ------END RSA PRIVATE KEY-----` +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 @@ -300,3 +237,47 @@ HWarSznekvG0leX8t4XefN9WV+Gj9G/isKgF7bYaI3/S3wK5IgQ+AYPx8TSb } 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, err := pbkdf2.Key(sha512.New, key, []byte("b612.me"), 923876, 32) + if err != nil { + return nil, fmt.Errorf("failed to generate AES key: %w", err) + } + 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, err := pbkdf2.Key(sha512.New, key, []byte("b612.me"), 923876, 32) + if err != nil { + return nil, fmt.Errorf("failed to generate AES key: %w", err) + } + 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 +} diff --git a/utils/cert_test.go b/utils/cert_test.go index 20bf625..1ccfb6f 100644 --- a/utils/cert_test.go +++ b/utils/cert_test.go @@ -77,10 +77,10 @@ func TestGenerateMiddleCA(t *testing.T) { Locality: []string{"Asteroid B612"}, Organization: []string{"B612.ME"}, OrganizationalUnit: []string{"CA.B612.ME"}, - CommonName: "B612 Inter Tool CA", + CommonName: "B612 Inter Tool CA 2025", }, - NotBefore: time.Date(2000, 01, 01, 8, 00, 00, 00, time.UTC), - NotAfter: time.Date(2077, 12, 31, 23, 59, 59, 00, time.UTC), + NotBefore: time.Date(2024, 01, 01, 8, 00, 00, 00, time.UTC), + NotAfter: time.Date(2026, 06, 12, 23, 59, 59, 00, time.UTC), BasicConstraintsValid: true, IsCA: true, MaxPathLen: 0, @@ -140,3 +140,25 @@ func LoadB612CA() (crypto.PrivateKey, *x509.Certificate, error) { } return caKey, cert, nil } + +func TestEncode(t *testing.T) { + crt, err := os.ReadFile("../bin/toolinter.crt") + if err != nil { + t.Fatal(err) + } + key, err := os.ReadFile("../bin/toolinter.key") + if err != nil { + t.Fatal(err) + } + aesKey := `` + encCrt, err := Encode(crt, aesKey) + if err != nil { + t.Fatal(err) + } + encKey, err := Encode(key, aesKey) + if err != nil { + t.Fatal(err) + } + fmt.Println("Encrypted Certificate:", hex.EncodeToString(encCrt)) + fmt.Println("Encrypted Key:", hex.EncodeToString(encKey)) +}