免費!讓 Https 證書不再成爲煩惱

前言

書承上回,上期寫了《實戰 goproxy 爲中國 steam 登錄加速》後,總是覺得服務端獲取到的客戶端信息太少了,爲了獲取更多的客戶端信息,就需要解包設置一箇中間代理人,我簡簡單單的用《Lego》申請了證書,兩種申請方式:

一種直接就是 cli,一種是代碼內申請直接使用。

#### 獻上 letsencrypt 的申請證書的工作原理,就知道爲什麼有申請和挑戰的環節了:

先介紹 Lego 的 cli 方式獲取證書與更新證書

# 通過源碼進行安裝
go install github.com/go-acme/lego/v4/cmd/lego@latest

# -----------------申請證書-----------------
lego --email="87066062@qq.com" --domains="www.laghaim.cn" --http run
# 申請的證書有效期爲90天
# 申請成功後就會獲得一下文件了。
# www.laghaim.cn.crt
# www.laghaim.cn.issuer.crt
# www.laghaim.cn.json
# www.laghaim.cn.key

# -----------------更新證書-----------------
lego --email="87066062@qq.com" --domains="www.laghaim.cn" --http renew
# 但是此時需要注意,這時續訂必須要小於30天

# 如果想提前更新證書,指定剩餘天數即可:
lego --email="87066062@qq.com" --domains="www.laghaim.cn" --http renew --days 45

如果想自動續租,寫個 crontab 就可以咯。

接下來就是利用 Lego 的庫代碼進行申請證書和續訂證書

關鍵地方我都進行了註釋,我喜歡一次性把整段代碼貼出來,大家也可以直接粘貼整段對部分常量參數進行修改後,直接調用 GetCertificateFromLego() 函數:

package get_certificate_from_lego

import (
    "crypto"
    "crypto/ecdsa"
    "crypto/elliptic"
    "crypto/rand"

    "github.com/go-acme/lego/v4/certcrypto"
    "github.com/go-acme/lego/v4/certificate"
    "github.com/go-acme/lego/v4/challenge/http01"
    "github.com/go-acme/lego/v4/challenge/tlsalpn01"
    "github.com/go-acme/lego/v4/lego"
    "github.com/go-acme/lego/v4/registration"
)

const (
    EmailStr  = "87066062@qq.com" // 修改爲自己的電子郵件
    OneDomain = "www.laghaim.cn"  // 修改爲自己的域名
)

type MyUser struct {
    Email        string
    Registration *registration.Resource
    key          crypto.PrivateKey
}

func (u *MyUser) GetEmail() string {
    return u.Email
}
func (u MyUser) GetRegistration() *registration.Resource {
    return u.Registration
}
func (u *MyUser) GetPrivateKey() crypto.PrivateKey {
    return u.key
}

func GetCertificateFromLego() (*certificate.Resource, error) {
    // 創建myUser用戶對象。新對象需要email和私鑰才能啓動,私鑰我們自己生成
    privateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
    if err != nil {
        return nil, err
    }

    myUser := MyUser{
        Email: EmailStr,
        key:   privateKey,
    }

    config := lego.NewConfig(&myUser)

    // 這裏配置密鑰的類型和密鑰申請的地址,記得上線後替換成 lego.LEDirectoryProduction ,測試環境下就用 lego.LEDirectoryStaging
    config.CADirURL = lego.LEDirectoryStaging
    config.Certificate.KeyType = certcrypto.RSA2048

    // 創建一個client與CA服務器通信
    client, err := lego.NewClient(config)
    if err != nil {
        return nil, err
    }

    // 這裏需要挑戰我們申請的證書,必須監聽80和443端口,這樣才能讓Let's Encrypt訪問到咱們的服務器
    err = client.Challenge.SetHTTP01Provider(http01.NewProviderServer("""80"))
    if err != nil {
        return nil, err
    }
    err = client.Challenge.SetTLSALPN01Provider(tlsalpn01.NewProviderServer("""443"))
    if err != nil {
        return nil, err
    }

    // 把這個客戶端註冊,傳遞給myUser用戶裏
    reg, err := client.Registration.Register(registration.RegisterOptions{TermsOfServiceAgreed: true})
    if err != nil {
        return nil, err
    }
    myUser.Registration = reg

    request := certificate.ObtainRequest{
        Domains: []string{OneDomain}, // 這裏如果有多個,就寫多個就好了,可以是多個域名
        Bundle:  true,                // 這裏如果是true,將把頒發者證書一起返回,也就是返回裏面certificates.IssuerCertificate
    }
    // 開始申請證書
    certificates, err := client.Certificate.Obtain(request)
    if err != nil {
        return nil, err
    }
    // 申請完了後,裏面會帶有證書的PrivateKey Certificate,都爲[]byte格式,需要存儲的自行轉爲string即可
    return certificates, nil
}

// 如果要進行續訂,可將certificates, err := client.Certificate.Obtain(request)替換爲certificates, err := client.Certificate.Renew(request)
// renew裏面的參數就很簡單了,第一個參數就是第一次申請返回的指針的值certificates,第二個參數bundle上面已經講過傳true即可,後面兩個參數一個傳false,一個傳空字符串""即可。開啓PAC自動配置

然後剩下的內容,就是使用和解析打印一下咯:

package main
import (
    "crypto/tls"
    "crypto/x509"
    "fmt"
    "log"

    "server/get_certificate_from_lego"
)
func main(){
    cs, err := get_certificate_from_lego.GetCertificateFromLego()
    if err != nil {
        log.Fatalln("obtains certificate:", err)
    }
    ca, err := tls.X509KeyPair(cs.Certificate, cs.PrivateKey)
    if err != nil {
        log.Fatalln(err)
    }
    if ca.Leaf, err = x509.ParseCertificate(ca.Certificate[0]); err != nil {
        log.Fatalln(err)
    }
    fmt.Println(ca.Leaf)
}

參考資料

lego**:**_https://github.com/go-acme/lego_

2022 GopherChina 大會報名火熱進行中!

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/QMiwx_2PhL3A1ogAAVeV2w