diff --git a/dns/cmd.go b/dns/cmd.go index de8306f..5df34a8 100644 --- a/dns/cmd.go +++ b/dns/cmd.go @@ -18,9 +18,10 @@ func init() { } var Cmd = &cobra.Command{ - Use: "dns", - Short: "dns查询服务", - Long: "DNS查询服务,支持UDP,TCP,DoT,DoH", + Use: "dns", + Aliases: []string{"dig"}, + Short: "dns查询服务", + Long: "DNS查询服务,支持UDP,TCP,DoT,DoH", Run: func(cmd *cobra.Command, args []string) { itype := 0 switch strings.ToLower(serverType) { diff --git a/main.go b/main.go index 393f18a..860e116 100644 --- a/main.go +++ b/main.go @@ -29,6 +29,7 @@ import ( "b612.me/apps/b612/socks5" "b612.me/apps/b612/split" "b612.me/apps/b612/tcping" + "b612.me/apps/b612/tls" "b612.me/apps/b612/uac" "b612.me/apps/b612/vic" "b612.me/apps/b612/whois" @@ -40,7 +41,7 @@ import ( var cmdRoot = &cobra.Command{ Use: "b612", - Version: "2.1.0.beta", + Version: "2.1.0.beta.4", } func init() { @@ -49,7 +50,7 @@ func init() { base64.Cmd, base85.Cmd, base91.Cmd, attach.Cmd, detach.Cmd, df.Cmd, dfinder.Cmd, ftp.Cmd, generate.Cmd, hash.Cmd, image.Cmd, merge.Cmd, search.Cmd, split.Cmd, vic.Cmd, calc.Cmd, net.Cmd, rmt.Cmds, rmt.Cmdc, keygen.Cmd, dns.Cmd, whois.Cmd, socks5.Cmd, httproxy.Cmd, smtpserver.Cmd, smtpclient.Cmd, - cert.Cmd, aes.Cmd) + cert.Cmd, aes.Cmd, tls.Cmd) } func main() { diff --git a/tls/cert.go b/tls/cert.go new file mode 100644 index 0000000..efe82a0 --- /dev/null +++ b/tls/cert.go @@ -0,0 +1,196 @@ +package tls + +import ( + "b612.me/starlog" + "crypto/ecdsa" + "crypto/rsa" + "crypto/tls" + "crypto/x509" + "encoding/pem" + "fmt" + "github.com/spf13/cobra" + "os" + "path/filepath" + "strings" + "time" +) + +var hideDetail bool +var dump string + +func init() { + Cmd.Flags().BoolVarP(&hideDetail, "hide-detail", "H", false, "隐藏证书详细信息") + Cmd.Flags().StringVarP(&dump, "dump", "d", "", "将证书保存到文件") +} + +var Cmd = &cobra.Command{ + Use: "tls", + Short: "查看TLS证书信息", + Long: "查看TLS证书信息", + Run: func(cmd *cobra.Command, args []string) { + for _, target := range args { + showTls(target, !hideDetail, dump) + } + }, +} + +func showTls(target string, showDetail bool, dumpPath string) { + if !strings.Contains(target, ":") { + target += ":443" + } + starlog.Infof("正在连接服务器: %s\n", target) + conn, err := tls.Dial("tcp", target, &tls.Config{ + InsecureSkipVerify: true, + }) + if err != nil { + starlog.Errorln("failed to connect: " + err.Error()) + return + } + defer conn.Close() + starlog.Infoln("连接成功,正在获取证书信息") + certs := conn.ConnectionState().PeerCertificates + if len(certs) == 0 { + starlog.Errorln("no certificate found") + return + } + starlog.Infof("证书获取成功,证书链上共有%d个证书\n", len(certs)) + if showDetail { + for _, c := range certs { + if c.IsCA { + continue + } + fmt.Printf("----------\n证书基础信息: %+v\n", c.Subject) + fmt.Printf("证书颁发者: %+v\n", c.Issuer) + fmt.Printf("证书生效时间: %+v 距今:%.1f天\n", c.NotBefore.In(time.Local), time.Since(c.NotBefore).Hours()/24) + fmt.Printf("证书过期时间: %+v 剩余:%.1f天\n", c.NotAfter.In(time.Local), c.NotAfter.Sub(time.Now()).Hours()/24) + fmt.Printf("证书序列号: %s\n", c.SerialNumber.Text(16)) + fmt.Printf("证书签名算法: %s\n", c.SignatureAlgorithm) + fmt.Printf("证书公钥算法: %s\n", c.PublicKeyAlgorithm) + switch pub := c.PublicKey.(type) { + case *rsa.PublicKey: + fmt.Printf("RSA公钥位数: %d\n", pub.Size()*8) // RSA公钥的位数 + case *ecdsa.PublicKey: + fmt.Printf("ECDSA Curve位数: %d\n", pub.Curve.Params().BitSize) // ECDSA公钥的位数 + } + if len(c.DNSNames) != 0 { + fmt.Printf("可选使用的DNS: %s\n", strings.Join(c.DNSNames, ", ")) + } + if len(c.IPAddresses) != 0 { + ipAddr := "" + for _, ip := range c.IPAddresses { + ipAddr += ip.String() + ", " + } + ipAddr = ipAddr[:len(ipAddr)-2] + fmt.Printf("可选使用的IP: %s\n", ipAddr) + } + if len(c.EmailAddresses) != 0 { + fmt.Printf("可选使用的Email: %s\n", strings.Join(c.EmailAddresses, ", ")) + } + if len(c.URIs) != 0 { + fmt.Printf("可选使用的URI: %v\n", c.URIs) + } + if len(c.PermittedDNSDomains) != 0 { + fmt.Printf("批准使用的DNS: %s\n", strings.Join(c.PermittedDNSDomains, ", ")) + } + if len(c.PermittedIPRanges) != 0 { + ipRange := "" + for _, ip := range c.PermittedIPRanges { + ipRange += ip.String() + ", " + } + ipRange = ipRange[:len(ipRange)-2] + fmt.Printf("批准使用的IP: %s\n", ipRange) + } + if len(c.PermittedEmailAddresses) != 0 { + fmt.Printf("批准使用的Email: %s\n", strings.Join(c.PermittedEmailAddresses, ", ")) + } + if len(c.PermittedURIDomains) != 0 { + fmt.Printf("批准使用的URI: %s\n", strings.Join(c.PermittedURIDomains, ", ")) + } + fmt.Printf("证书密钥用途: %s\n", strings.Join(KeyUsageToString(c.KeyUsage), ", ")) + extKeyUsage := []string{} + for _, v := range c.ExtKeyUsage { + switch v { + case x509.ExtKeyUsageAny: + extKeyUsage = append(extKeyUsage, "任何用途") + case x509.ExtKeyUsageServerAuth: + extKeyUsage = append(extKeyUsage, "服务器认证") + case x509.ExtKeyUsageClientAuth: + extKeyUsage = append(extKeyUsage, "客户端认证") + case x509.ExtKeyUsageCodeSigning: + extKeyUsage = append(extKeyUsage, "代码签名") + case x509.ExtKeyUsageEmailProtection: + extKeyUsage = append(extKeyUsage, "电子邮件保护") + case x509.ExtKeyUsageIPSECEndSystem: + extKeyUsage = append(extKeyUsage, "IPSEC终端系统") + case x509.ExtKeyUsageIPSECTunnel: + extKeyUsage = append(extKeyUsage, "IPSEC隧道") + case x509.ExtKeyUsageIPSECUser: + extKeyUsage = append(extKeyUsage, "IPSEC用户") + case x509.ExtKeyUsageTimeStamping: + extKeyUsage = append(extKeyUsage, "时间戳") + case x509.ExtKeyUsageOCSPSigning: + extKeyUsage = append(extKeyUsage, "OCSP签名") + case x509.ExtKeyUsageMicrosoftServerGatedCrypto: + extKeyUsage = append(extKeyUsage, "Microsoft服务器门控加密") + case x509.ExtKeyUsageNetscapeServerGatedCrypto: + extKeyUsage = append(extKeyUsage, "Netscape服务器门控加密") + case x509.ExtKeyUsageMicrosoftCommercialCodeSigning: + extKeyUsage = append(extKeyUsage, "Microsoft商业代码签名") + case x509.ExtKeyUsageMicrosoftKernelCodeSigning: + extKeyUsage = append(extKeyUsage, "Microsoft内核代码签名") + default: + extKeyUsage = append(extKeyUsage, fmt.Sprintf("未知用途(%d)", v)) + } + } + fmt.Printf("证书扩展密钥用途: %s\n", strings.Join(extKeyUsage, ", ")) + fmt.Printf("证书版本: %d\n", c.Version) + //fmt.Printf("证书扩展信息: %+v\n\n----------", c.Extensions) + } + if dumpPath != "" { + var data []byte + var name string + for _, c := range certs { + if name == "" { + name = c.Subject.CommonName + ".crt" + } + certBlock := &pem.Block{ + Type: "CERTIFICATE", + Bytes: c.Raw, + } + data = append(data, pem.EncodeToMemory(certBlock)...) + } + err = os.WriteFile(filepath.Join(dumpPath, name), data, 0644) + if err != nil { + starlog.Errorln("failed to write file: " + err.Error()) + return + } + starlog.Infoln("dumped to " + filepath.Join(dumpPath, name)) + } + } +} + +func KeyUsageToString(ku x509.KeyUsage) []string { + usages := []string{} + flags := []struct { + Flag x509.KeyUsage + Name string + }{ + {x509.KeyUsageDigitalSignature, "数字签名"}, + {x509.KeyUsageContentCommitment, "内容承诺"}, + {x509.KeyUsageKeyEncipherment, "密钥加密"}, + {x509.KeyUsageDataEncipherment, "数据加密"}, + {x509.KeyUsageKeyAgreement, "密钥协商"}, + {x509.KeyUsageCertSign, "证书签名"}, + {x509.KeyUsageCRLSign, "CRL签名"}, + {x509.KeyUsageEncipherOnly, "仅加密"}, + {x509.KeyUsageDecipherOnly, "仅解密"}, + } + + for _, flag := range flags { + if ku&flag.Flag != 0 { + usages = append(usages, flag.Name) + } + } + + return usages +} diff --git a/tls/cert_test.go b/tls/cert_test.go new file mode 100644 index 0000000..05a6c79 --- /dev/null +++ b/tls/cert_test.go @@ -0,0 +1,7 @@ +package tls + +import "testing" + +func TestCert(t *testing.T) { + showTls("139.199.163.65:443", true, "") +}