如何在Golang服务器中添加OCSP stapling?

hs1rzwqc  于 5个月前  发布在  Go
关注(0)|答案(1)|浏览(77)

下面是我如何配置我的Golang服务器:

func main() {
    ....
    config := &tls.Config{
        MinVersion: tls.VersionTLS12,
        PreferServerCipherSuites: true,
    }
    server := &http.Server{Addr: ":"+port, Handler: router, TLSConfig: config}
    err = server.ListenAndServeTLS(cert, key)
    ....
}

字符串
但我们安全供应商抱怨:
该服务器不支持OCSP装订
这一点究竟如何落实?

1u4esq0p

1u4esq0p1#

一个简单的使用golang.org/x/crypto/ocsp包的OCSP装订实现。

package ocsp_stapling

import (
    "crypto/tls"
    "crypto/x509"
    "encoding/base64"
    "errors"
    "golang.org/x/crypto/ocsp"
    "io"
    "net/http"
    "time"
)

type OcspHandler struct {
    crt                tls.Certificate
    isRevoked          bool
    ocspNextUpdate     time.Time
    cachedOcspResponse []byte
    leaf, issuer       *x509.Certificate
    client             *http.Client
}

func (h *OcspHandler) getResponse() ([]byte, error) {
    ocspReq, err := ocsp.CreateRequest(h.leaf, h.issuer, nil)
    if err != nil {
        return nil, err
    }
    ocspReqBase64 := base64.StdEncoding.EncodeToString(ocspReq)

    reqURL := h.leaf.OCSPServer[0] + "/" + ocspReqBase64
    httpReq, err := http.NewRequest("GET", reqURL, nil)
    httpReq.Header.Add("Content-Language", "application/ocsp-request")
    httpReq.Header.Add("Accept", "application/ocsp-response")

    resp, err := h.client.Do(httpReq)
    if err != nil {
        return nil, err
    }

    barOcspResp, err := io.ReadAll(resp.Body)
    if err != nil {
        return nil, err
    }

    ocspRes, err := ocsp.ParseResponse(barOcspResp, h.issuer)
    if err != nil {
        return nil, err
    }

    h.isRevoked = ocspRes.Status == ocsp.Revoked
    if ocspRes.Status == ocsp.Good {
        h.ocspNextUpdate = ocspRes.NextUpdate
        h.cachedOcspResponse = barOcspResp
        return barOcspResp, nil
    } else {
        h.ocspNextUpdate = time.Time{}
        h.cachedOcspResponse = nil
    }

    return nil, nil
}

func (h *OcspHandler) Start() {
    for {
        res, err := h.getResponse()
        h.crt.OCSPStaple = res

        if h.isRevoked {
            break
        }

        var sleep time.Duration
        if err != nil || h.ocspNextUpdate.IsZero() {
            sleep = 5 * time.Minute
        } else {
            sleep = h.ocspNextUpdate.Sub(time.Now())
        }
        time.Sleep(sleep)
    }
}

func NewOcspHandler(crt tls.Certificate) (*OcspHandler, error) {
    if len(crt.Certificate) < 2 {
        return nil, errors.New("no issuer in chain")
    }
    leaf, err := x509.ParseCertificate(crt.Certificate[0])
    if err != nil {
        return nil, err
    }
    issuer, err := x509.ParseCertificate(crt.Certificate[1])
    if err != nil {
        return nil, err
    }

    return &OcspHandler{
        crt:    crt,
        leaf:   leaf,
        issuer: issuer,
        client: &http.Client{Timeout: 5 * time.Second},
    }, nil
}

func (h *OcspHandler) GetCertificate(info *tls.ClientHelloInfo) (*tls.Certificate, error) {
    return &h.crt, nil
}

字符串
要在TLS服务器中使用它,您只需将tls.Config.GetCertificate字段设置为OcspHandler.GetCertificate方法。

func panicIfError(err error) {
    if err != nil {
        panic(err)
    }
}

func handleRequest(w http.ResponseWriter, r *http.Request) {
    w.WriteHeader(http.StatusOK)
    w.Write([]byte("hello\n"))
}

func main() {
    var addr = flag.String("addr", ":8443", "the TCP address for the server to listen on")
    flag.Parse()

    certFile, keyFile := "server.crt", "server.key"

    crt, err := tls.LoadX509KeyPair(certFile, keyFile)
    panicIfError(err)

    ocspHandler, err := ocsp_stapling.NewOcspHandler(crt)
    panicIfError(err)
    go ocspHandler.Start()

    srv := &http.Server{
        Addr: *addr, Handler: http.HandlerFunc(handleRequest), TLSConfig: &tls.Config{
            GetCertificate: ocspHandler.GetCertificate,
        },
    }
    log.Fatal(srv.ListenAndServeTLS("", ""))

}

相关问题