2022-11-10 17:44:38 +08:00
|
|
|
package ssl
|
|
|
|
|
|
|
|
import (
|
2022-11-11 17:41:39 +08:00
|
|
|
"crypto"
|
2022-11-10 17:44:38 +08:00
|
|
|
"crypto/rand"
|
|
|
|
"crypto/rsa"
|
|
|
|
"crypto/x509"
|
2023-11-14 18:44:09 +08:00
|
|
|
"encoding/json"
|
2022-11-10 17:44:38 +08:00
|
|
|
"encoding/pem"
|
2023-11-14 18:44:09 +08:00
|
|
|
"fmt"
|
|
|
|
"github.com/1Panel-dev/1Panel/backend/app/model"
|
2022-11-10 17:44:38 +08:00
|
|
|
"github.com/go-acme/lego/v4/certcrypto"
|
|
|
|
"github.com/go-acme/lego/v4/lego"
|
|
|
|
"github.com/go-acme/lego/v4/registration"
|
2023-11-14 18:44:09 +08:00
|
|
|
"io"
|
|
|
|
"net/http"
|
|
|
|
"net/url"
|
2022-11-10 17:44:38 +08:00
|
|
|
)
|
|
|
|
|
2022-12-04 21:18:24 +08:00
|
|
|
type domainError struct {
|
|
|
|
Domain string
|
|
|
|
Error error
|
|
|
|
}
|
|
|
|
|
2023-11-14 18:44:09 +08:00
|
|
|
type zeroSSLRes struct {
|
|
|
|
Success bool `json:"success"`
|
|
|
|
EabKid string `json:"eab_kid"`
|
|
|
|
EabHmacKey string `json:"eab_hmac_key"`
|
|
|
|
}
|
|
|
|
|
2022-11-11 17:41:39 +08:00
|
|
|
func GetPrivateKey(priKey crypto.PrivateKey) []byte {
|
|
|
|
rsaKey := priKey.(*rsa.PrivateKey)
|
|
|
|
derStream := x509.MarshalPKCS1PrivateKey(rsaKey)
|
2022-11-10 17:44:38 +08:00
|
|
|
block := &pem.Block{
|
|
|
|
Type: "privateKey",
|
|
|
|
Bytes: derStream,
|
|
|
|
}
|
|
|
|
return pem.EncodeToMemory(block)
|
|
|
|
}
|
|
|
|
|
2023-11-14 18:44:09 +08:00
|
|
|
func NewRegisterClient(acmeAccount *model.WebsiteAcmeAccount) (*AcmeClient, error) {
|
|
|
|
var (
|
|
|
|
priKey *rsa.PrivateKey
|
|
|
|
err error
|
|
|
|
)
|
|
|
|
|
|
|
|
if acmeAccount.PrivateKey != "" {
|
|
|
|
block, _ := pem.Decode([]byte(acmeAccount.PrivateKey))
|
|
|
|
priKey, err = x509.ParsePKCS1PrivateKey(block.Bytes)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
priKey, err = rsa.GenerateKey(rand.Reader, 2048)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2022-11-10 17:44:38 +08:00
|
|
|
}
|
2022-11-11 17:41:39 +08:00
|
|
|
|
2022-11-10 17:44:38 +08:00
|
|
|
myUser := &AcmeUser{
|
2023-11-14 18:44:09 +08:00
|
|
|
Email: acmeAccount.Email,
|
2022-11-11 17:41:39 +08:00
|
|
|
Key: priKey,
|
2022-11-10 17:44:38 +08:00
|
|
|
}
|
2023-11-14 18:44:09 +08:00
|
|
|
config := newConfig(myUser, acmeAccount.Type)
|
2022-11-10 17:44:38 +08:00
|
|
|
client, err := lego.NewClient(config)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2023-11-14 18:44:09 +08:00
|
|
|
var reg *registration.Resource
|
|
|
|
if acmeAccount.Type == "zerossl" || acmeAccount.Type == "google" {
|
|
|
|
if acmeAccount.Type == "zerossl" {
|
|
|
|
var res *zeroSSLRes
|
|
|
|
res, err = getZeroSSLEabCredentials(acmeAccount.Email)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if res.Success {
|
|
|
|
acmeAccount.EabKid = res.EabKid
|
|
|
|
acmeAccount.EabHmacKey = res.EabHmacKey
|
|
|
|
} else {
|
|
|
|
return nil, fmt.Errorf("get zero ssl eab credentials failed")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
eabOptions := registration.RegisterEABOptions{
|
|
|
|
TermsOfServiceAgreed: true,
|
|
|
|
Kid: acmeAccount.EabKid,
|
|
|
|
HmacEncoded: acmeAccount.EabHmacKey,
|
|
|
|
}
|
|
|
|
reg, err = client.Registration.RegisterWithExternalAccountBinding(eabOptions)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
reg, err = client.Registration.Register(registration.RegisterOptions{TermsOfServiceAgreed: true})
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2022-11-10 17:44:38 +08:00
|
|
|
}
|
|
|
|
myUser.Registration = reg
|
|
|
|
|
|
|
|
acmeClient := &AcmeClient{
|
|
|
|
User: myUser,
|
|
|
|
Client: client,
|
|
|
|
Config: config,
|
|
|
|
}
|
|
|
|
|
|
|
|
return acmeClient, nil
|
|
|
|
}
|
|
|
|
|
2023-11-14 18:44:09 +08:00
|
|
|
func newConfig(user *AcmeUser, accountType string) *lego.Config {
|
|
|
|
config := lego.NewConfig(user)
|
|
|
|
switch accountType {
|
|
|
|
case "letsEncrypt":
|
|
|
|
config.CADirURL = "https://acme-v02.api.letsencrypt.org/directory"
|
|
|
|
case "zeroSSL":
|
|
|
|
config.CADirURL = "https://acme.zerossl.com/v2/DV90"
|
|
|
|
case "buyPass":
|
|
|
|
config.CADirURL = "https://api.buypass.com/acme/directory"
|
|
|
|
case "google":
|
|
|
|
config.CADirURL = "https://dv.acme-v02.api.pki.goog/directory"
|
|
|
|
}
|
|
|
|
|
|
|
|
config.UserAgent = "1Panel"
|
|
|
|
config.Certificate.KeyType = certcrypto.RSA2048
|
|
|
|
return config
|
|
|
|
}
|
|
|
|
|
|
|
|
func getZeroSSLEabCredentials(email string) (*zeroSSLRes, error) {
|
|
|
|
baseURL := "https://api.zerossl.com/acme/eab-credentials-email"
|
|
|
|
params := url.Values{}
|
|
|
|
params.Add("email", email)
|
|
|
|
requestURL := fmt.Sprintf("%s?%s", baseURL, params.Encode())
|
|
|
|
|
|
|
|
req, err := http.NewRequest("POST", requestURL, nil)
|
2022-11-10 17:44:38 +08:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2023-11-14 18:44:09 +08:00
|
|
|
|
|
|
|
req.Header.Set("Content-Type", "application/json")
|
|
|
|
|
|
|
|
client := &http.Client{}
|
|
|
|
resp, err := client.Do(req)
|
2022-11-10 17:44:38 +08:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2023-11-14 18:44:09 +08:00
|
|
|
defer resp.Body.Close()
|
|
|
|
|
|
|
|
body, err := io.ReadAll(resp.Body)
|
2022-11-10 17:44:38 +08:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2023-11-14 18:44:09 +08:00
|
|
|
if resp.StatusCode != http.StatusOK {
|
|
|
|
return nil, fmt.Errorf("server returned non-200 status: %d %s", resp.StatusCode, http.StatusText(resp.StatusCode))
|
2022-11-10 17:44:38 +08:00
|
|
|
}
|
|
|
|
|
2023-11-14 18:44:09 +08:00
|
|
|
var result zeroSSLRes
|
|
|
|
err = json.Unmarshal(body, &result)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2022-11-10 17:44:38 +08:00
|
|
|
|
2023-11-14 18:44:09 +08:00
|
|
|
return &result, nil
|
2022-11-10 17:44:38 +08:00
|
|
|
}
|