golang 实现无域名的HTTPS服务器
程序员文章站
2022-07-12 21:59:11
...
场景是这样子的:我们有一个自己搭建的CA来签发证书,CA给我们的服务器签发证书.现在整个产品还处于内部开发阶段,服务器只有一个内网IP,没有域名.搭建WEB服务使用了go语言的echo库.
那么会遇到这样的一个问题:一般来说,服务器的证书common name字段都是服务器的host name/domain name,但是在我们的这个场景下,服务器只有IP,如果服务器证书的common name 直接填入IP地址的话,那么会报错:
x509: cannot validate certificate for 127.0.0.1 because it doesn't contain any IP SANs
解决方案主要参考了:
https://cabforum.org/guidance-ip-addresses-certificates/
https://*.com/questions/1095780/are-ssl-certificates-bound-to-the-servers-ip-address
证书内部有一个subjet alternative name扩展字段,也就是上面的那个错误中提到的SANs,如果说证书里面使用IP不使用域名的话,证书的common name和SAN都要写入IP地址(证书使用IP而不使用域名是不推荐的,我们这里只是为了内部开发调试的目的).
具体的实现参考了:
https://github.com/jcbsmpsn/golang-https-example
也就是说,签发证书的命令是:
openssl req \
-newkey rsa:2048 \
-nodes \
-days 3650 \
-x509 \
-keyout ca.key \
-out ca.crt \
-subj "/CN=*"
openssl req \
-newkey rsa:2048 \
-nodes \
-keyout server.key \
-out server.csr \
-subj "/C=GB/ST=London/L=London/O=Global Security/OU=IT Department/CN=*"
openssl x509 \
-req \
-days 365 \
-sha256 \
-in server.csr \
-CA ca.crt \
-CAkey ca.key \
-CAcreateserial \
-out server.crt \
-extfile <(echo subjectAltName = IP:127.0.0.1)
最后一行把IP地址写入SAN,生成证书.
附上客户端和服务器的Demo代码:
服务器
package main
import (
"crypto/tls"
"net/http"
// "golang.org/x/crypto/acme/autocert"
// "log"
"fmt"
"github.com/labstack/echo"
"github.com/labstack/echo/middleware"
)
const (
server_crt = "server.crt"
server_key = "server.key"
)
func main() {
e := echo.New()
e.Use(middleware.Recover())
e.GET("/", func(c echo.Context) error {
return c.HTML(http.StatusOK, `
<h1>Welcome to Echo!</h1>
<h3>TLS certificates automatically installed from Let's Encrypt :)</h3>
`)
})
e.TLSServer.TLSConfig = new(tls.Config)
//TLSConfig
certificate, err := tls.LoadX509KeyPair(server_crt, server_key)
if err != nil {
fmt.Println(err.Error())
return
}
// Create a certificate pool from the certificate authority
e.TLSServer.TLSConfig.Certificates = []tls.Certificate{certificate}
e.TLSServer.Addr = ":10000"
e.StartServer(e.TLSServer)
}
客户端
func main() {
pool := x509.NewCertPool()
caCertPath := "ca.crt"
caCrt, err := ioutil.ReadFile(caCertPath)
if err != nil {
fmt.Println("ReadFile err:", err)
return
}
pool.AppendCertsFromPEM(caCrt)
tr := &http.Transport{
TLSClientConfig: &tls.Config{RootCAs: pool},
}
client := &http.Client{Transport: tr}
resp, err := client.Get("https://127.0.0.1:10000")
if err != nil {
fmt.Println("Get error:", err)
return
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
fmt.Println(string(body))
}