diff --git a/go.mod b/go.mod
index 807dec1f..7f6b58fb 100644
--- a/go.mod
+++ b/go.mod
@@ -30,6 +30,7 @@ require (
 	gvisor.dev/gvisor v0.0.0-20231104011432-48a6d7d5bd0b
 	h12.io/socks v1.0.3
 	lukechampine.com/blake3 v1.2.2
+	github.com/OmarTariq612/goech v0.0.0-20240405204721-8e2e1dafd3a0
 )
 
 require (
diff --git a/go.sum b/go.sum
index 64362f4e..e3560abd 100644
--- a/go.sum
+++ b/go.sum
@@ -8,6 +8,8 @@ dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1
 dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU=
 git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg=
 github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
+github.com/OmarTariq612/goech v0.0.0-20240405204721-8e2e1dafd3a0 h1:Wo41lDOevRJSGpevP+8Pk5bANX7fJacO2w04aqLiC5I=
+github.com/OmarTariq612/goech v0.0.0-20240405204721-8e2e1dafd3a0/go.mod h1:FVGavL/QEBQDcBpr3fAojoK17xX5k9bicBphrOpP7uM=
 github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M=
 github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY=
 github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
diff --git a/main/commands/all/tls/ech.go b/main/commands/all/tls/ech.go
new file mode 100644
index 00000000..d4e17f9b
--- /dev/null
+++ b/main/commands/all/tls/ech.go
@@ -0,0 +1,69 @@
+package tls
+
+import (
+	"encoding/json"
+	"encoding/pem"
+	"os"
+	"strings"
+
+	"github.com/OmarTariq612/goech"
+	"github.com/cloudflare/circl/hpke"
+	"github.com/xtls/xray-core/common"
+	"github.com/xtls/xray-core/main/commands/base"
+)
+
+var cmdECH = &base.Command{
+	UsageLine: `{{.Exec}} tls ech [--serverName (string)] [--json]`,
+	Short:     `Generate TLS-ECH certificates`,
+	Long: `
+Generate TLS-ECH certificates.
+
+Set serverName to your custom string: {{.Exec}} tls ech --serverName (string)
+Generate into json format: {{.Exec}} tls ech --json
+`, // Enable PQ signature schemes: {{.Exec}} tls ech --pq-signature-schemes-enabled
+}
+
+func init() {
+	cmdECH.Run = executeECH
+}
+
+var input_pqSignatureSchemesEnabled = cmdECH.Flag.Bool("pqSignatureSchemesEnabled", false, "")
+var input_serverName = cmdECH.Flag.String("serverName", "cloudflare-ech.com", "")
+var input_json = cmdECH.Flag.Bool("json", false, "True == turn on json output")
+
+func executeECH(cmd *base.Command, args []string) {
+	var kem hpke.KEM
+
+	if *input_pqSignatureSchemesEnabled {
+		kem = hpke.KEM_X25519_KYBER768_DRAFT00
+	} else {
+		kem = hpke.KEM_X25519_HKDF_SHA256
+	}
+
+	echKeySet, err := goech.GenerateECHKeySet(0, *input_serverName, kem)
+	common.Must(err)
+
+	configBuffer, _ := echKeySet.ECHConfig.MarshalBinary()
+	keyBuffer, _ := echKeySet.MarshalBinary()
+
+	configPEM := string(pem.EncodeToMemory(&pem.Block{Type: "ECH CONFIGS", Bytes: configBuffer}))
+	keyPEM := string(pem.EncodeToMemory(&pem.Block{Type: "ECH KEYS", Bytes: keyBuffer}))
+	if *input_json {
+		jECHConfigs := map[string]interface{}{
+			"configs": strings.Split(strings.TrimSpace(string(configPEM)), "\n"),
+		}
+		jECHKey := map[string]interface{}{
+			"key": strings.Split(strings.TrimSpace(string(keyPEM)), "\n"),
+		}
+
+		for _, i := range []map[string]interface{}{jECHConfigs, jECHKey} {
+			content, err := json.MarshalIndent(i, "", "  ")
+			common.Must(err)
+			os.Stdout.Write(content)
+			os.Stdout.WriteString("\n")
+		}
+	} else {
+		os.Stdout.WriteString(configPEM)
+		os.Stdout.WriteString(keyPEM)
+	}
+}
diff --git a/main/commands/all/tls/tls.go b/main/commands/all/tls/tls.go
index bf653301..a93da1c3 100644
--- a/main/commands/all/tls/tls.go
+++ b/main/commands/all/tls/tls.go
@@ -14,5 +14,6 @@ var CmdTLS = &base.Command{
 		cmdCert,
 		cmdPing,
 		cmdCertChainHash,
+		cmdECH,
 	},
 }