Commands: Print CA cert's SHA256 in tls ping (#5644)

And https://github.com/XTLS/Xray-core/issues/5642#issuecomment-3840806246

---------

Co-authored-by: RPRX <63339210+RPRX@users.noreply.github.com>
This commit is contained in:
风扇滑翔翼
2026-02-04 05:03:48 +08:00
committed by GitHub
parent d14767d4f3
commit 74c726ff62
7 changed files with 80 additions and 25 deletions

View File

@@ -0,0 +1,17 @@
package utils
import (
"reflect"
"unsafe"
)
// AccessField can used to access unexported field of a struct
// valueType must be the exact type of the field or it will panic
func AccessField[valueType any](obj any, fieldName string) *valueType {
field := reflect.ValueOf(obj).Elem().FieldByName(fieldName)
if field.Type() != reflect.TypeOf(*new(valueType)) {
panic("field type: " + field.Type().String() + ", valueType: " + reflect.TypeOf(*new(valueType)).String())
}
v := (*valueType)(unsafe.Pointer(field.UnsafeAddr()))
return v
}

View File

@@ -1,6 +1,7 @@
package conf
import (
"context"
"encoding/base64"
"encoding/hex"
"encoding/json"
@@ -10,6 +11,7 @@ import (
"strconv"
"strings"
"syscall"
"time"
"github.com/xtls/xray-core/common/errors"
"github.com/xtls/xray-core/common/net"
@@ -747,7 +749,12 @@ func (c *TLSConfig) Build() (proto.Message, error) {
config.MasterKeyLog = c.MasterKeyLog
if c.AllowInsecure {
return nil, errors.PrintRemovedFeatureError(`"allowInsecure"`, `"pinnedPeerCertSha256"`)
if time.Now().After(time.Date(2026, 6, 1, 0, 0, 0, 0, time.UTC)) {
return nil, errors.PrintRemovedFeatureError(`"allowInsecure"`, `"pinnedPeerCertSha256"`)
} else {
errors.LogWarning(context.Background(), `"allowInsecure" will be removed automatically after 2026-06-01, please use "pinnedPeerCertSha256"(pcs) and "verifyPeerCertByName"(vcn) instead, PLEASE CONTACT YOUR SERVICE PROVIDER (AIRPORT)`)
config.AllowInsecure = true
}
}
if c.PinnedPeerCertSha256 != "" {
for v := range strings.SplitSeq(c.PinnedPeerCertSha256, ",") {

View File

@@ -6,8 +6,13 @@ import (
"encoding/hex"
"fmt"
"net"
"os"
"strconv"
"text/tabwriter"
utls "github.com/refraction-networking/utls"
"github.com/xtls/xray-core/common/utils"
"github.com/xtls/xray-core/main/commands/base"
. "github.com/xtls/xray-core/transport/internet/tls"
)
@@ -46,6 +51,7 @@ func executePing(cmd *base.Command, args []string) {
} else {
TargetPort, _ = strconv.Atoi(port)
}
tabWriter := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
var ip net.IP
if len(*pingIPStr) > 0 {
@@ -70,7 +76,7 @@ func executePing(cmd *base.Command, args []string) {
if err != nil {
base.Fatalf("Failed to dial tcp: %s", err)
}
tlsConn := gotls.Client(tcpConn, &gotls.Config{
tlsConn := GeneraticUClient(tcpConn, &gotls.Config{
InsecureSkipVerify: true,
NextProtos: []string{"h2", "http/1.1"},
MaxVersion: gotls.VersionTLS13,
@@ -81,8 +87,9 @@ func executePing(cmd *base.Command, args []string) {
fmt.Println("Handshake failure: ", err)
} else {
fmt.Println("Handshake succeeded")
printTLSConnDetail(tlsConn)
printCertificates(tlsConn.ConnectionState().PeerCertificates)
printTLSConnDetail(tabWriter, tlsConn)
printCertificates(tabWriter, tlsConn.ConnectionState().PeerCertificates)
tabWriter.Flush()
}
tlsConn.Close()
}
@@ -94,7 +101,7 @@ func executePing(cmd *base.Command, args []string) {
if err != nil {
base.Fatalf("Failed to dial tcp: %s", err)
}
tlsConn := gotls.Client(tcpConn, &gotls.Config{
tlsConn := GeneraticUClient(tcpConn, &gotls.Config{
ServerName: domain,
NextProtos: []string{"h2", "http/1.1"},
MaxVersion: gotls.VersionTLS13,
@@ -105,8 +112,9 @@ func executePing(cmd *base.Command, args []string) {
fmt.Println("Handshake failure: ", err)
} else {
fmt.Println("Handshake succeeded")
printTLSConnDetail(tlsConn)
printCertificates(tlsConn.ConnectionState().PeerCertificates)
printTLSConnDetail(tabWriter, tlsConn)
printCertificates(tabWriter, tlsConn.ConnectionState().PeerCertificates)
tabWriter.Flush()
}
tlsConn.Close()
}
@@ -115,38 +123,45 @@ func executePing(cmd *base.Command, args []string) {
fmt.Println("TLS ping finished")
}
func printCertificates(certs []*x509.Certificate) {
func printCertificates(tabWriter *tabwriter.Writer, certs []*x509.Certificate) {
var leaf *x509.Certificate
var CAs []*x509.Certificate
var length int
for _, cert := range certs {
length += len(cert.Raw)
if len(cert.DNSNames) != 0 {
leaf = cert
} else {
CAs = append(CAs, cert)
}
}
fmt.Println("Certificate chain's total length: ", length, "(certs count: "+strconv.Itoa(len(certs))+")")
fmt.Fprintf(tabWriter, "Certificate chain's total length: \t %d (certs count: %s)\n", length, strconv.Itoa(len(certs)))
if leaf != nil {
fmt.Println("Cert's signature algorithm: ", leaf.SignatureAlgorithm.String())
fmt.Println("Cert's publicKey algorithm: ", leaf.PublicKeyAlgorithm.String())
fmt.Println("Cert's allowed domains: ", leaf.DNSNames)
fmt.Println("Cert's leaf SHA256: ", hex.EncodeToString(GenerateCertHash(leaf)))
fmt.Fprintf(tabWriter, "Cert's signature algorithm: \t %s\n", leaf.SignatureAlgorithm.String())
fmt.Fprintf(tabWriter, "Cert's publicKey algorithm: \t %s\n", leaf.PublicKeyAlgorithm.String())
fmt.Fprintf(tabWriter, "Cert's leaf SHA256: \t %s\n", hex.EncodeToString(GenerateCertHash(leaf)))
for _, ca := range CAs {
fmt.Fprintf(tabWriter, "Cert's CA: %s SHA256: \t %s\n", ca.Subject.CommonName, hex.EncodeToString(GenerateCertHash(ca)))
}
fmt.Fprintf(tabWriter, "Cert's allowed domains: \t %v\n", leaf.DNSNames)
}
}
func printTLSConnDetail(tlsConn *gotls.Conn) {
func printTLSConnDetail(tabWriter *tabwriter.Writer, tlsConn *utls.UConn) {
connectionState := tlsConn.ConnectionState()
var tlsVersion string
if connectionState.Version == gotls.VersionTLS13 {
switch connectionState.Version {
case gotls.VersionTLS13:
tlsVersion = "TLS 1.3"
} else if connectionState.Version == gotls.VersionTLS12 {
case gotls.VersionTLS12:
tlsVersion = "TLS 1.2"
}
fmt.Println("TLS Version: ", tlsVersion)
curveID := connectionState.CurveID
if curveID != 0 {
PostQuantum := (curveID == gotls.X25519MLKEM768)
fmt.Println("TLS Post-Quantum key exchange: ", PostQuantum, "("+curveID.String()+")")
fmt.Fprintf(tabWriter, "TLS Version: \t %s\n", tlsVersion)
curveID := utils.AccessField[utls.CurveID](tlsConn.Conn, "curveID")
if curveID != nil {
PostQuantum := (*curveID == utls.X25519MLKEM768)
fmt.Fprintf(tabWriter, "TLS Post-Quantum key exchange: \t %t (%s)\n", PostQuantum, curveID.String())
} else {
fmt.Println("TLS Post-Quantum key exchange: false (RSA Exchange)")
fmt.Fprintf(tabWriter, "TLS Post-Quantum key exchange: false (RSA Exchange)\n")
}
}

View File

@@ -384,6 +384,7 @@ func (c *Config) GetTLSConfig(opts ...Option) *tls.Config {
PinnedPeerCertSha256: c.PinnedPeerCertSha256,
}
config := &tls.Config{
InsecureSkipVerify: c.AllowInsecure,
Rand: randCarrier,
ClientSessionCache: globalSessionCache,
RootCAs: root,

View File

@@ -177,7 +177,8 @@ func (x *Certificate) GetBuildChain() bool {
}
type Config struct {
state protoimpl.MessageState `protogen:"open.v1"`
state protoimpl.MessageState `protogen:"open.v1"`
AllowInsecure bool `protobuf:"varint,1,opt,name=allow_insecure,json=allowInsecure,proto3" json:"allow_insecure,omitempty"`
// List of certificates to be served on server.
Certificate []*Certificate `protobuf:"bytes,2,rep,name=certificate,proto3" json:"certificate,omitempty"`
// Override server name.
@@ -241,6 +242,13 @@ func (*Config) Descriptor() ([]byte, []int) {
return file_transport_internet_tls_config_proto_rawDescGZIP(), []int{1}
}
func (x *Config) GetAllowInsecure() bool {
if x != nil {
return x.AllowInsecure
}
return false
}
func (x *Config) GetCertificate() []*Certificate {
if x != nil {
return x.Certificate
@@ -385,8 +393,9 @@ const file_transport_internet_tls_config_proto_rawDesc = "" +
"\x05Usage\x12\x10\n" +
"\fENCIPHERMENT\x10\x00\x12\x14\n" +
"\x10AUTHORITY_VERIFY\x10\x01\x12\x13\n" +
"\x0fAUTHORITY_ISSUE\x10\x02\"\xce\x06\n" +
"\x06Config\x12J\n" +
"\x0fAUTHORITY_ISSUE\x10\x02\"\xf5\x06\n" +
"\x06Config\x12%\n" +
"\x0eallow_insecure\x18\x01 \x01(\bR\rallowInsecure\x12J\n" +
"\vcertificate\x18\x02 \x03(\v2(.xray.transport.internet.tls.CertificateR\vcertificate\x12\x1f\n" +
"\vserver_name\x18\x03 \x01(\tR\n" +
"serverName\x12#\n" +

View File

@@ -38,6 +38,8 @@ message Certificate {
}
message Config {
bool allow_insecure = 1;
// List of certificates to be served on server.
repeated Certificate certificate = 2;

View File

@@ -126,6 +126,10 @@ func UClient(c net.Conn, config *tls.Config, fingerprint *utls.ClientHelloID) ne
return &UConn{UConn: utlsConn}
}
func GeneraticUClient(c net.Conn, config *tls.Config) *utls.UConn {
return utls.UClient(c, copyConfig(config), utls.HelloChrome_Auto)
}
func copyConfig(c *tls.Config) *utls.Config {
return &utls.Config{
Rand: c.Rand,