如何在Go中验证SSL证书

1,234 阅读1分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第21天,点击查看活动详情

最近,我遇到了一个SaaS产品,该产品可以验证你的网站的SSL证书。 你需要在你的网站上执行三项重大检查。

  1. 首先,检查您的网站是否有SSL证书。您还需要知道您的网站是否正在使用不被视为有效证书的自签名SSL证书(需要由证书颁发机构签名)。
  2. 其次,查看SSL证书是否有正确的主机名。
  3. 第三,您需要知道服务器证书的到期日期。

先决条件:

  • 你应该go你的电脑上设置。

第1步:检查您的网站是否有SSL证书

首先,我们将尝试检查该网站是否有SSL证书。

为此,我们需要与网站建立TLS连接。如果成功,则表示网站拥有有效的TLS证书。

要建立TLS连接,我们可以使用Gocrypto/tls软件包。我们将使用Dial方法连接到网站,如下所示:

package main

import (
	"crypto/tls"
)

func main() {
	conn, err := tls.Dial("tcp", "example.com:80", nil)
	if err != nil {
		panic("Server doesn't support SSL certificate err: " + err.Error())
	}
}

main.go

我们将尝试在example.com上运行我们的测试。当您运行上述代码时,您应该会收到以下错误:

$ go run main.go
panic: Server doesn't support SSL certificate err: tls: first record does not look like a TLS handshake

goroutine 1 [running]:
main.main()
        /Users/umesh/personal/spike/main.go:99 +0x2ca
exit status 2

终端中的输出

基本上,它试图建立连接,但失败了。只有当网站拥有有效的证书时,Dial方法才会成功(如果证书是自签名,则会失败)。

现在在启用了SSL的网站上尝试相同的代码。我使用我自己网站的URL作为示例:

package main

import (
	"crypto/tls"
)

func main() {
	conn, err := tls.Dial("tcp", "blog.umesh.wtf:443", nil)
	if err != nil {
		panic("Server doesn't support SSL certificate err: " + err.Error())
	}
}

main.go

这一次,我们应该能够在没有代码恐慌的情况下成功建立连接。

在生产中,您不应该使用恐慌,而应该优雅地处理错误。

第2步:检查SSL证书和网站主机名是否匹配

要验证主机名,我们需要通过Dialconn返回时调用VerifyHostname。此方法试图将证书中指定的通用名称或主题alt名称与作为参数传递的域匹配。

package main

import (
	"crypto/tls"
)

func main() {
	conn, err := tls.Dial("tcp", "blog.umesh.wtf:443", nil)
	if err != nil {
		panic("Server doesn't support SSL certificate err: " + err.Error())
	}

	err = conn.VerifyHostname("blog.umesh.wtf")
	if err != nil {
		panic("Hostname doesn't match with certificate: " + err.Error())
	}
}

main.go

这将成功执行,没有任何错误,因为证书的通用名称和主机名是相同的。

第3步:验证服务器SSL证书的到期日期

We can get the certificate chain using conn.ConnectionState().PeerCertificates. We can then use this certificate to get the expiration date of the server certificate. 

我们将使用证书列表中的第一个证书,并尝试使用NotAfter字段获取到期日期。

package main

import (
	"crypto/tls"
	"fmt"
	"time"
)

func main() {
	conn, err := tls.Dial("tcp", "blog.umesh.wtf:443", nil)
	if err != nil {
		panic("Server doesn't support SSL certificate err: " + err.Error())
	}

	err = conn.VerifyHostname("blog.umesh.wtf")
	if err != nil {
		panic("Hostname doesn't match with certificate: " + err.Error())
	}
	expiry := conn.ConnectionState().PeerCertificates[0].NotAfter
	fmt.Printf("Issuer: %s\nExpiry: %v\n", conn.ConnectionState().PeerCertificates[0].Issuer, expiry.Format(time.RFC850))
}

main.go

输出应包含证书的到期日期和发行人名称。

$ go run main.go
Issuer: CN=Let's Encrypt Authority X3, O=Let's Encrypt, C=US
Expiry: Wednesday, 16-Dec-20 16:20:00 UTC

终端中的输出

现在,我们已经成功验证了该网站的证书。

结论

您还可以获得详细信息,如根CA、证书签发日期和所有链式证书。