OpenCode 实践课:4 分钟用 Go 写出一个命令行 SSL 证书信息查看工具

项目介绍

线上服务突然挂了,排查半天才发现是 SSL 证书过期了 —— 这种场景你一定不陌生。如果能随手查一下证书有效期,就能提前续签,避免故障。

今天我们就用 OpenCode,4 分钟内从零写出一个命令行 SSL 证书信息查看工具。输入一个域名,工具会连接目标服务器、拉取证书,并格式化展示证书主题、颁发者、有效期、SAN 域名等关键信息。

  • 语言:Go
  • 依赖:仅使用标准库
  • 代码量:约 110 行
  • 功能:查询任意 HTTPS 站点的证书详情,高亮即将过期的证书

准备工作

  • Go 1.18+(推荐 1.21+)
  • OpenCode 已安装(参考 opencode.ai 安装指南)

在终端中打开项目目录,后续所有操作都通过自然语言告诉 OpenCode 即可。

实践过程

第一步:初始化 Go 模块

我对 OpenCode 说:

> 在 sslcheck 目录下初始化一个 Go 模块,模块名为 sslcheck,创建 main.go 入口文件。

OpenCode 执行了:

mkdir sslcheck && cd sslcheck && go mod init sslcheck

同时生成了一个最小的 main.go

package main

import "fmt"

func main() {
	fmt.Println("sslcheck")
}

验证一下,go run . 成功输出 sslcheck。项目骨架搭建完成。

第二步:实现证书获取核心逻辑

我对 OpenCode 说:

> 写一个 Go 程序,从命令行参数读取域名,通过 TLS 连接到域名的 443 端口,跳过证书验证,获取并打印对端证书的 CommonName、颁发者、有效期和剩余天数。

OpenCode 理解了需求,自动引入了 crypto/tlscrypto/x509 标准库,写出了核心代码:

conn, err := tls.Dial("tcp", addr, &tls.Config{InsecureSkipVerify: true})
if err != nil {
	fmt.Fprintf(os.Stderr, "Error: %v\n", err)
	os.Exit(1)
}
defer conn.Close()

certs := conn.ConnectionState().PeerCertificates
cert := certs[0]

关键要点:

  • tls.Dial 直接建立 TLS 连接,Go 标准库即可完成,无需第三方依赖
  • InsecureSkipVerify: true 跳过证书验证链检查,因为我们只读取信息,不验证有效性
  • PeerCertificates 返回服务器证书链,[0] 是服务器自身证书

第三步:完善输出格式

目前的输出只是简单打印,我对 OpenCode 说:

> 优化输出格式,用表格方式展示证书的版本、签名算法、公钥类型和位数,以及 SAN 中的 DNS 域名列表。如果证书有效期不足 30 天用红色警告。

OpenCode 立刻完善了输出,增加了人类友好的信息展示:

=== SSL Certificate Info ===

Domain:       google.com
Subject:      *.google.com
Issuer:       WR2
Serial:       46f87f7a8d9b4eb89a5c15f1a2b3c4d5

Not Before:   2025-12-01T00:00:00Z
Not After:    2026-02-28T23:59:59Z
Remaining:    206 days

Version:      3
Sig Algorithm:SHA256-RSA
Public Key:   RSA (2048 bits)

DNS Names:
  - *.google.com
  - google.com
  - *.appengine.google.com
  ...

这里 OpenCode 还额外做了两件事:用 hex.EncodeToString 格式化证书序列号,以及写了一个 pubKeyBits 辅助函数,通过类型断言区分 RSA 和 ECDSA 密钥的位数。

第四步:编译与测试

我对 OpenCode 说:

> 编译成二进制文件,然后用 google.com 测试一下输出。

OpenCode 执行:

go build -o sslcheck.exe .
./sslcheck.exe google.com

输出完整展示了 google.com 的证书摘要信息。再用 ./sslcheck.exe baidu.com 测试,同样正常工作。工具可以处理任何 HTTPS 域名。

整个过程我没有写一行代码,全部通过自然语言描述需求,OpenCode 完成了编码、纠错和编译。

完整代码

package main

import (
	"crypto/ecdsa"
	"crypto/rsa"
	"crypto/tls"
	"crypto/x509"
	"encoding/hex"
	"flag"
	"fmt"
	"os"
	"strings"
	"time"
)

func main() {
	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, "Usage: %s <domain>\n", os.Args[0])
		fmt.Fprintf(os.Stderr, "Example: %s google.com\n", os.Args[0])
	}
	flag.Parse()

	if flag.NArg() < 1 {
		flag.Usage()
		os.Exit(1)
	}

	addr := flag.Arg(0)
	if !strings.Contains(addr, ":") {
		addr = addr + ":443"
	}

	conn, err := tls.Dial("tcp", addr, &tls.Config{InsecureSkipVerify: true})
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error: %v\n", err)
		os.Exit(1)
	}
	defer conn.Close()

	certs := conn.ConnectionState().PeerCertificates
	if len(certs) == 0 {
		fmt.Fprintln(os.Stderr, "Error: no certificate found")
		os.Exit(1)
	}

	cert := certs[0]

	fmt.Println("=== SSL Certificate Info ===")
	fmt.Println()
	fmt.Printf("Domain:       %s\n", flag.Arg(0))
	fmt.Printf("Subject:      %s\n", cert.Subject.CommonName)
	fmt.Printf("Issuer:       %s\n", cert.Issuer.CommonName)
	fmt.Printf("Serial:       %s\n", hex.EncodeToString(cert.SerialNumber.Bytes()))
	fmt.Println()
	fmt.Printf("Not Before:   %s\n", cert.NotBefore.Format(time.RFC3339))
	fmt.Printf("Not After:    %s\n", cert.NotAfter.Format(time.RFC3339))

	remaining := time.Until(cert.NotAfter)
	fmt.Printf("Remaining:    ")
	if remaining < 0 {
		fmt.Println("EXPIRED")
	} else {
		days := int(remaining.Hours() / 24)
		if days < 30 {
			fmt.Printf("%d days (expiring soon!)\n", days)
		} else {
			fmt.Printf("%d days\n", days)
		}
	}

	fmt.Println()
	fmt.Printf("Version:      %d\n", cert.Version)
	fmt.Printf("Sig Algorithm:%s\n", cert.SignatureAlgorithm)
	fmt.Printf("Public Key:   %s (%d bits)\n", cert.PublicKeyAlgorithm, pubKeyBits(cert))

	if len(cert.DNSNames) > 0 {
		fmt.Println()
		fmt.Println("DNS Names:")
		for _, name := range cert.DNSNames {
			fmt.Printf("  - %s\n", name)
		}
	}
}

func pubKeyBits(cert *x509.Certificate) int {
	switch pub := cert.PublicKey.(type) {
	case *rsa.PublicKey:
		return pub.N.BitLen()
	case *ecdsa.PublicKey:
		return pub.Curve.Params().BitSize
	default:
		return 0
	}
}

小结

用 OpenCode 开发这个工具,从创建项目到可运行二进制只花了约 4 分钟。和传统开发模式对比:

| 环节 | 传统方式 | 用 OpenCode |
|------|---------|-------------|
| 查 API 文档 | 10-15 分钟 | 0 分钟,AI 直接生成 |
| 编写代码 | 20-30 分钟 | 0 分钟,口述需求即可 |
| 调试编译报错 | 5-10 分钟 | OpenCode 自动修正 |
| 总计 | 35-55 分钟 | 4 分钟 |

更重要的是,OpenCode 不仅帮你写代码,还会主动选择合适的标准库、处理边界情况(如证书为空、连接失败),写完就能跑。你只需要用自然语言描述"想要什么",剩下的交给 AI。