diff --git a/backend/app/dto/request/website_ssl.go b/backend/app/dto/request/website_ssl.go
index 42a6d7e41..fef59547f 100644
--- a/backend/app/dto/request/website_ssl.go
+++ b/backend/app/dto/request/website_ssl.go
@@ -14,6 +14,7 @@ type WebsiteSSLCreate struct {
AcmeAccountID uint `json:"acmeAccountId" validate:"required"`
DnsAccountID uint `json:"dnsAccountId"`
AutoRenew bool `json:"autoRenew"`
+ KeyType string `json:"keyType"`
}
type WebsiteDNSReq struct {
@@ -28,6 +29,7 @@ type WebsiteSSLRenew struct {
type WebsiteAcmeAccountCreate struct {
Email string `json:"email" validate:"required"`
Type string `json:"type" validate:"required,oneof=letsencrypt zerossl buypass google"`
+ KeyType string `json:"keyType" validate:"required,oneof=P256 P384 2048 3072 4096 8192"`
EabKid string `json:"eabKid"`
EabHmacKey string `json:"eabHmacKey"`
}
diff --git a/backend/app/model/website_acme_account.go b/backend/app/model/website_acme_account.go
index d0e468f5d..eeafc0187 100644
--- a/backend/app/model/website_acme_account.go
+++ b/backend/app/model/website_acme_account.go
@@ -2,12 +2,13 @@ package model
type WebsiteAcmeAccount struct {
BaseModel
- Email string `gorm:"type:varchar(256);not null" json:"email"`
- URL string `gorm:"type:varchar(256);not null" json:"url"`
- PrivateKey string `gorm:"type:longtext;not null" json:"-"`
- Type string `gorm:"type:varchar(64);not null;default:letsencrypt" json:"type"`
- EabKid string `gorm:"type:varchar(256);" json:"eabKid"`
- EabHmacKey string `gorm:"type:varchar(256);" json:"eabHmacKey"`
+ Email string `gorm:"not null" json:"email"`
+ URL string `gorm:"not null" json:"url"`
+ PrivateKey string `gorm:"not null" json:"-"`
+ Type string `gorm:"not null;default:letsencrypt" json:"type"`
+ EabKid string `gorm:"default:null;" json:"eabKid"`
+ EabHmacKey string `gorm:"default:null" json:"eabHmacKey"`
+ KeyType string `gorm:"not null;default:2048" json:"keyType"`
}
func (w WebsiteAcmeAccount) TableName() string {
diff --git a/backend/app/service/website_acme_account.go b/backend/app/service/website_acme_account.go
index 8b1718b56..711fa0b95 100644
--- a/backend/app/service/website_acme_account.go
+++ b/backend/app/service/website_acme_account.go
@@ -15,7 +15,7 @@ type WebsiteAcmeAccountService struct {
type IWebsiteAcmeAccountService interface {
Page(search dto.PageInfo) (int64, []response.WebsiteAcmeAccountDTO, error)
- Create(create request.WebsiteAcmeAccountCreate) (response.WebsiteAcmeAccountDTO, error)
+ Create(create request.WebsiteAcmeAccountCreate) (*response.WebsiteAcmeAccountDTO, error)
Delete(id uint) error
}
@@ -34,34 +34,39 @@ func (w WebsiteAcmeAccountService) Page(search dto.PageInfo) (int64, []response.
return total, accountDTOs, err
}
-func (w WebsiteAcmeAccountService) Create(create request.WebsiteAcmeAccountCreate) (response.WebsiteAcmeAccountDTO, error) {
+func (w WebsiteAcmeAccountService) Create(create request.WebsiteAcmeAccountCreate) (*response.WebsiteAcmeAccountDTO, error) {
exist, _ := websiteAcmeRepo.GetFirst(websiteAcmeRepo.WithEmail(create.Email))
if exist != nil {
- return response.WebsiteAcmeAccountDTO{}, buserr.New(constant.ErrEmailIsExist)
+ return nil, buserr.New(constant.ErrEmailIsExist)
}
if create.Type == "google" && (create.EabKid == "" || create.EabHmacKey == "") {
- return response.WebsiteAcmeAccountDTO{}, buserr.New(constant.ErrEabKidOrEabHmacKeyCannotBlank)
+ return nil, buserr.New(constant.ErrEabKidOrEabHmacKeyCannotBlank)
} else {
create.EabKid = ""
create.EabHmacKey = ""
}
acmeAccount := &model.WebsiteAcmeAccount{
- Email: create.Email,
- Type: create.Type,
+ Email: create.Email,
+ Type: create.Type,
+ KeyType: create.KeyType,
}
client, err := ssl.NewAcmeClient(acmeAccount)
if err != nil {
- return response.WebsiteAcmeAccountDTO{}, err
+ return nil, err
}
- acmeAccount.PrivateKey = string(ssl.GetPrivateKey(client.User.GetPrivateKey()))
+ privateKey, err := ssl.GetPrivateKey(client.User.GetPrivateKey(), ssl.KeyType(create.KeyType))
+ if err != nil {
+ return nil, err
+ }
+ acmeAccount.PrivateKey = string(privateKey)
acmeAccount.URL = client.User.Registration.URI
if err := websiteAcmeRepo.Create(*acmeAccount); err != nil {
- return response.WebsiteAcmeAccountDTO{}, err
+ return nil, err
}
- return response.WebsiteAcmeAccountDTO{WebsiteAcmeAccount: *acmeAccount}, nil
+ return &response.WebsiteAcmeAccountDTO{WebsiteAcmeAccount: *acmeAccount}, nil
}
func (w WebsiteAcmeAccountService) Delete(id uint) error {
diff --git a/backend/app/service/website_ssl.go b/backend/app/service/website_ssl.go
index 3a62cd0c4..6ed1e3057 100644
--- a/backend/app/service/website_ssl.go
+++ b/backend/app/service/website_ssl.go
@@ -2,6 +2,7 @@ package service
import (
"context"
+ "crypto"
"crypto/x509"
"encoding/pem"
"github.com/1Panel-dev/1Panel/backend/app/dto/request"
@@ -13,6 +14,7 @@ import (
"github.com/1Panel-dev/1Panel/backend/global"
"github.com/1Panel-dev/1Panel/backend/utils/files"
"github.com/1Panel-dev/1Panel/backend/utils/ssl"
+ "github.com/go-acme/lego/v4/certcrypto"
"path"
"strconv"
"strings"
@@ -132,7 +134,21 @@ func (w WebsiteSSLService) Create(create request.WebsiteSSLCreate) (request.Webs
if create.OtherDomains != "" {
domains = append(otherDomainArray, domains...)
}
- resource, err := client.ObtainSSL(domains)
+ var privateKey crypto.PrivateKey
+ if create.KeyType != acmeAccount.KeyType {
+ privateKey, err = certcrypto.GeneratePrivateKey(ssl.KeyType(create.KeyType))
+ if err != nil {
+ return res, err
+ }
+ } else {
+ block, _ := pem.Decode([]byte(acmeAccount.PrivateKey))
+ privateKey, err = x509.ParseECPrivateKey(block.Bytes)
+ if err != nil {
+ return res, err
+ }
+ }
+
+ resource, err := client.ObtainSSL(domains, privateKey)
if err != nil {
return res, err
}
diff --git a/backend/init/migration/migrations/v_1_9.go b/backend/init/migration/migrations/v_1_9.go
index be29d53b3..3c2236b04 100644
--- a/backend/init/migration/migrations/v_1_9.go
+++ b/backend/init/migration/migrations/v_1_9.go
@@ -7,7 +7,7 @@ import (
)
var UpdateAcmeAccount = &gormigrate.Migration{
- ID: "20231115-update-acme-account",
+ ID: "20231117-update-acme-account",
Migrate: func(tx *gorm.DB) error {
if err := tx.AutoMigrate(&model.WebsiteAcmeAccount{}); err != nil {
return err
diff --git a/backend/utils/ssl/acme.go b/backend/utils/ssl/acme.go
index 36470d669..25cf683e2 100644
--- a/backend/utils/ssl/acme.go
+++ b/backend/utils/ssl/acme.go
@@ -2,7 +2,7 @@ package ssl
import (
"crypto"
- "crypto/rand"
+ "crypto/ecdsa"
"crypto/rsa"
"crypto/x509"
"encoding/json"
@@ -28,30 +28,70 @@ type zeroSSLRes struct {
EabHmacKey string `json:"eab_hmac_key"`
}
-func GetPrivateKey(priKey crypto.PrivateKey) []byte {
- rsaKey := priKey.(*rsa.PrivateKey)
- derStream := x509.MarshalPKCS1PrivateKey(rsaKey)
- block := &pem.Block{
- Type: "privateKey",
- Bytes: derStream,
+type KeyType = certcrypto.KeyType
+
+const (
+ KeyEC256 = certcrypto.EC256
+ KeyEC384 = certcrypto.EC384
+ KeyRSA2048 = certcrypto.RSA2048
+ KeyRSA3072 = certcrypto.RSA3072
+ KeyRSA4096 = certcrypto.RSA4096
+)
+
+func GetPrivateKey(priKey crypto.PrivateKey, keyType KeyType) ([]byte, error) {
+ var (
+ marshal []byte
+ block *pem.Block
+ err error
+ )
+
+ switch keyType {
+ case KeyEC256, KeyEC384:
+ key := priKey.(*ecdsa.PrivateKey)
+ marshal, err = x509.MarshalECPrivateKey(key)
+ if err != nil {
+ return nil, err
+ }
+ block = &pem.Block{
+ Type: "EC PRIVATE KEY",
+ Bytes: marshal,
+ }
+ case KeyRSA2048, KeyRSA3072, KeyRSA4096:
+ key := priKey.(*rsa.PrivateKey)
+ marshal = x509.MarshalPKCS1PrivateKey(key)
+ block = &pem.Block{
+ Type: "RSA PRIVATE KEY",
+ Bytes: marshal,
+ }
}
- return pem.EncodeToMemory(block)
+
+ return pem.EncodeToMemory(block), nil
}
func NewRegisterClient(acmeAccount *model.WebsiteAcmeAccount) (*AcmeClient, error) {
var (
- priKey *rsa.PrivateKey
+ priKey crypto.PrivateKey
err error
)
if acmeAccount.PrivateKey != "" {
- block, _ := pem.Decode([]byte(acmeAccount.PrivateKey))
- priKey, err = x509.ParsePKCS1PrivateKey(block.Bytes)
- if err != nil {
- return nil, err
+ switch KeyType(acmeAccount.KeyType) {
+ case KeyEC256, KeyEC384:
+ block, _ := pem.Decode([]byte(acmeAccount.PrivateKey))
+ priKey, err = x509.ParseECPrivateKey(block.Bytes)
+ if err != nil {
+ return nil, err
+ }
+ case KeyRSA2048, KeyRSA3072, KeyRSA4096:
+ 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)
+ priKey, err = certcrypto.GeneratePrivateKey(KeyType(acmeAccount.KeyType))
if err != nil {
return nil, err
}
diff --git a/backend/utils/ssl/client.go b/backend/utils/ssl/client.go
index 9f849a26a..bacf46a26 100644
--- a/backend/utils/ssl/client.go
+++ b/backend/utils/ssl/client.go
@@ -131,10 +131,11 @@ func (c *AcmeClient) UseHTTP(path string) error {
return nil
}
-func (c *AcmeClient) ObtainSSL(domains []string) (certificate.Resource, error) {
+func (c *AcmeClient) ObtainSSL(domains []string, privateKey crypto.PrivateKey) (certificate.Resource, error) {
request := certificate.ObtainRequest{
- Domains: domains,
- Bundle: true,
+ Domains: domains,
+ Bundle: true,
+ PrivateKey: privateKey,
}
certificates, err := c.Client.Certificate.Obtain(request)
@@ -222,10 +223,10 @@ func (c *AcmeClient) GetDNSResolve(domains []string) (map[string]Resolve, error)
resolves[domain] = Resolve{Err: err.Error()}
continue
}
- fqdn, value := dns01.GetRecord(domain, keyAuth)
+ challengeInfo := dns01.GetChallengeInfo(domain, keyAuth)
resolves[domain] = Resolve{
- Key: fqdn,
- Value: value,
+ Key: challengeInfo.FQDN,
+ Value: challengeInfo.Value,
}
}
diff --git a/frontend/src/global/mimetype.ts b/frontend/src/global/mimetype.ts
index e065cab3b..7eb8d09f0 100644
--- a/frontend/src/global/mimetype.ts
+++ b/frontend/src/global/mimetype.ts
@@ -129,3 +129,11 @@ export const AcmeAccountTypes = [
{ label: 'Buypass', value: 'buypass' },
{ label: 'Google Cloud', value: 'google' },
];
+
+export const KeyTypes = [
+ { label: 'EC 256', value: 'P256' },
+ { label: 'EC 384', value: 'P384' },
+ { label: 'RSA 2048', value: '2048' },
+ { label: 'RSA 3072', value: '3072' },
+ { label: 'RSA 4096', value: '4096' },
+];
diff --git a/frontend/src/lang/modules/en.ts b/frontend/src/lang/modules/en.ts
index 70e7aadcf..2ec25745d 100644
--- a/frontend/src/lang/modules/en.ts
+++ b/frontend/src/lang/modules/en.ts
@@ -1665,6 +1665,7 @@ const message = {
'OpenResty default HTTP port: {0} HTTPS port: {1}, which may affect website domain name access and HTTPS forced redirect',
primaryDomainHelper: 'Support domain name: port',
acmeAccountType: 'Account Type',
+ keyType: 'Key algorithm',
},
php: {
short_open_tag: 'Short tag support',
diff --git a/frontend/src/lang/modules/tw.ts b/frontend/src/lang/modules/tw.ts
index c0137b517..953ed1398 100644
--- a/frontend/src/lang/modules/tw.ts
+++ b/frontend/src/lang/modules/tw.ts
@@ -1578,6 +1578,7 @@ const message = {
openrestryHelper: 'OpenResty 默認 HTTP 端口:{0} HTTPS 端口:{1},可能影響網站域名訪問和 HTTPS 強制跳轉',
primaryDomainHelper: '支援網域:port',
acmeAccountType: '賬號類型',
+ keyType: '密鑰演算法',
},
php: {
short_open_tag: '短標簽支持',
diff --git a/frontend/src/lang/modules/zh.ts b/frontend/src/lang/modules/zh.ts
index 4263273bd..473a20994 100644
--- a/frontend/src/lang/modules/zh.ts
+++ b/frontend/src/lang/modules/zh.ts
@@ -1578,6 +1578,7 @@ const message = {
openrestryHelper: 'OpenResty 默认 HTTP 端口:{0} HTTPS 端口 :{1},可能影响网站域名访问和 HTTPS 强制跳转',
primaryDomainHelper: '支持域名:端口',
acmeAccountType: '账号类型',
+ keyType: '密钥算法',
},
php: {
short_open_tag: '短标签支持',
diff --git a/frontend/src/views/website/ssl/acme-account/create/index.vue b/frontend/src/views/website/ssl/acme-account/create/index.vue
index 5897ac17f..8a4c2afcf 100644
--- a/frontend/src/views/website/ssl/acme-account/create/index.vue
+++ b/frontend/src/views/website/ssl/acme-account/create/index.vue
@@ -22,6 +22,16 @@
>
+