mirror of
https://gitclone.com/github.com/MetaCubeX/Clash.Meta
synced 2025-05-13 13:38:06 +08:00
chore: rebuild fingerprint and keypair handle
This commit is contained in:
parent
468cfc3cc4
commit
c2301f66a4
@ -1,65 +0,0 @@
|
||||
package net
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/sha256"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"encoding/hex"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"math/big"
|
||||
)
|
||||
|
||||
type Path interface {
|
||||
Resolve(path string) string
|
||||
}
|
||||
|
||||
func ParseCert(certificate, privateKey string, path Path) (tls.Certificate, error) {
|
||||
if certificate == "" && privateKey == "" {
|
||||
var err error
|
||||
certificate, privateKey, _, err = NewRandomTLSKeyPair()
|
||||
if err != nil {
|
||||
return tls.Certificate{}, err
|
||||
}
|
||||
}
|
||||
cert, painTextErr := tls.X509KeyPair([]byte(certificate), []byte(privateKey))
|
||||
if painTextErr == nil {
|
||||
return cert, nil
|
||||
}
|
||||
|
||||
certificate = path.Resolve(certificate)
|
||||
privateKey = path.Resolve(privateKey)
|
||||
cert, loadErr := tls.LoadX509KeyPair(certificate, privateKey)
|
||||
if loadErr != nil {
|
||||
return tls.Certificate{}, fmt.Errorf("parse certificate failed, maybe format error:%s, or path error: %s", painTextErr.Error(), loadErr.Error())
|
||||
}
|
||||
return cert, nil
|
||||
}
|
||||
|
||||
func NewRandomTLSKeyPair() (certificate string, privateKey string, fingerprint string, err error) {
|
||||
key, err := rsa.GenerateKey(rand.Reader, 2048)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
template := x509.Certificate{SerialNumber: big.NewInt(1)}
|
||||
certDER, err := x509.CreateCertificate(
|
||||
rand.Reader,
|
||||
&template,
|
||||
&template,
|
||||
&key.PublicKey,
|
||||
key)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
cert, err := x509.ParseCertificate(certDER)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
hash := sha256.Sum256(cert.Raw)
|
||||
fingerprint = hex.EncodeToString(hash[:])
|
||||
privateKey = string(pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(key)}))
|
||||
certificate = string(pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: certDER}))
|
||||
return
|
||||
}
|
@ -1,17 +1,13 @@
|
||||
package ca
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/sha256"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
_ "embed"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
C "github.com/metacubex/mihomo/constant"
|
||||
@ -81,36 +77,6 @@ func getCertPool() *x509.CertPool {
|
||||
return globalCertPool
|
||||
}
|
||||
|
||||
func verifyFingerprint(fingerprint *[32]byte) func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {
|
||||
return func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {
|
||||
// ssl pining
|
||||
for i := range rawCerts {
|
||||
rawCert := rawCerts[i]
|
||||
cert, err := x509.ParseCertificate(rawCert)
|
||||
if err == nil {
|
||||
hash := sha256.Sum256(cert.Raw)
|
||||
if bytes.Equal(fingerprint[:], hash[:]) {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return errNotMatch
|
||||
}
|
||||
}
|
||||
|
||||
func convertFingerprint(fingerprint string) (*[32]byte, error) {
|
||||
fingerprint = strings.TrimSpace(strings.Replace(fingerprint, ":", "", -1))
|
||||
fpByte, err := hex.DecodeString(fingerprint)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(fpByte) != 32 {
|
||||
return nil, fmt.Errorf("fingerprint string length error,need sha256 fingerprint")
|
||||
}
|
||||
return (*[32]byte)(fpByte), nil
|
||||
}
|
||||
|
||||
func GetCertPool(customCA string, customCAString string) (*x509.CertPool, error) {
|
||||
var certificate []byte
|
||||
var err error
|
||||
@ -133,14 +99,6 @@ func GetCertPool(customCA string, customCAString string) (*x509.CertPool, error)
|
||||
}
|
||||
}
|
||||
|
||||
func NewFingerprintVerifier(fingerprint string) (func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error, error) {
|
||||
fingerprintBytes, err := convertFingerprint(fingerprint)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return verifyFingerprint(fingerprintBytes), nil
|
||||
}
|
||||
|
||||
// GetTLSConfig specified fingerprint, customCA and customCAString
|
||||
func GetTLSConfig(tlsConfig *tls.Config, fingerprint string, customCA string, customCAString string) (_ *tls.Config, err error) {
|
||||
if tlsConfig == nil {
|
||||
|
40
component/ca/fingerprint.go
Normal file
40
component/ca/fingerprint.go
Normal file
@ -0,0 +1,40 @@
|
||||
package ca
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/sha256"
|
||||
"crypto/x509"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// NewFingerprintVerifier returns a function that verifies whether a certificate's SHA-256 fingerprint matches the given one.
|
||||
func NewFingerprintVerifier(fingerprint string) (func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error, error) {
|
||||
fingerprint = strings.TrimSpace(strings.Replace(fingerprint, ":", "", -1))
|
||||
fpByte, err := hex.DecodeString(fingerprint)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(fpByte) != 32 {
|
||||
return nil, fmt.Errorf("fingerprint string length error,need sha256 fingerprint")
|
||||
}
|
||||
|
||||
return func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {
|
||||
// ssl pining
|
||||
for _, rawCert := range rawCerts {
|
||||
hash := sha256.Sum256(rawCert)
|
||||
if bytes.Equal(fpByte, hash[:]) {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return errNotMatch
|
||||
}, nil
|
||||
}
|
||||
|
||||
// CalculateFingerprint computes the SHA-256 fingerprint of the given DER-encoded certificate and returns it as a hex string.
|
||||
func CalculateFingerprint(certDER []byte) string {
|
||||
hash := sha256.Sum256(certDER)
|
||||
return hex.EncodeToString(hash[:])
|
||||
}
|
92
component/ca/keypair.go
Normal file
92
component/ca/keypair.go
Normal file
@ -0,0 +1,92 @@
|
||||
package ca
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/ecdsa"
|
||||
"crypto/ed25519"
|
||||
"crypto/elliptic"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"math/big"
|
||||
)
|
||||
|
||||
type Path interface {
|
||||
Resolve(path string) string
|
||||
}
|
||||
|
||||
// LoadTLSKeyPair loads a TLS key pair from the provided certificate and private key data or file paths, supporting fallback resolution.
|
||||
// Returns a tls.Certificate and an error, where the error indicates issues during parsing or file loading.
|
||||
// If both certificate and privateKey are empty, generates a random TLS RSA key pair.
|
||||
// Accepts a Path interface for resolving file paths when necessary.
|
||||
func LoadTLSKeyPair(certificate, privateKey string, path Path) (tls.Certificate, error) {
|
||||
if certificate == "" && privateKey == "" {
|
||||
var err error
|
||||
certificate, privateKey, _, err = NewRandomTLSKeyPair(KeyPairTypeRSA)
|
||||
if err != nil {
|
||||
return tls.Certificate{}, err
|
||||
}
|
||||
}
|
||||
cert, painTextErr := tls.X509KeyPair([]byte(certificate), []byte(privateKey))
|
||||
if painTextErr == nil {
|
||||
return cert, nil
|
||||
}
|
||||
if path == nil {
|
||||
return tls.Certificate{}, painTextErr
|
||||
}
|
||||
|
||||
certificate = path.Resolve(certificate)
|
||||
privateKey = path.Resolve(privateKey)
|
||||
cert, loadErr := tls.LoadX509KeyPair(certificate, privateKey)
|
||||
if loadErr != nil {
|
||||
return tls.Certificate{}, fmt.Errorf("parse certificate failed, maybe format error:%s, or path error: %s", painTextErr.Error(), loadErr.Error())
|
||||
}
|
||||
return cert, nil
|
||||
}
|
||||
|
||||
type KeyPairType string
|
||||
|
||||
const (
|
||||
KeyPairTypeRSA KeyPairType = "rsa"
|
||||
KeyPairTypeP256 KeyPairType = "p256"
|
||||
KeyPairTypeP384 KeyPairType = "p384"
|
||||
KeyPairTypeEd25519 KeyPairType = "ed25519"
|
||||
)
|
||||
|
||||
// NewRandomTLSKeyPair generates a random TLS key pair based on the specified KeyPairType and returns it with a SHA256 fingerprint.
|
||||
// Note: Most browsers do not support KeyPairTypeEd25519 type of certificate, and utls.UConn will also reject this type of certificate.
|
||||
func NewRandomTLSKeyPair(keyPairType KeyPairType) (certificate string, privateKey string, fingerprint string, err error) {
|
||||
var key crypto.Signer
|
||||
switch keyPairType {
|
||||
case KeyPairTypeRSA:
|
||||
key, err = rsa.GenerateKey(rand.Reader, 2048)
|
||||
case KeyPairTypeP256:
|
||||
key, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||
case KeyPairTypeP384:
|
||||
key, err = ecdsa.GenerateKey(elliptic.P384(), rand.Reader)
|
||||
case KeyPairTypeEd25519:
|
||||
_, key, err = ed25519.GenerateKey(rand.Reader)
|
||||
default: // fallback to KeyPairTypeRSA
|
||||
key, err = rsa.GenerateKey(rand.Reader, 2048)
|
||||
}
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
template := x509.Certificate{SerialNumber: big.NewInt(1)}
|
||||
certDER, err := x509.CreateCertificate(rand.Reader, &template, &template, key.Public(), key)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
privBytes, err := x509.MarshalPKCS8PrivateKey(key)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
fingerprint = CalculateFingerprint(certDER)
|
||||
privateKey = string(pem.EncodeToMemory(&pem.Block{Type: "PRIVATE KEY", Bytes: privBytes}))
|
||||
certificate = string(pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: certDER}))
|
||||
return
|
||||
}
|
@ -15,8 +15,8 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/metacubex/mihomo/adapter/inbound"
|
||||
CN "github.com/metacubex/mihomo/common/net"
|
||||
"github.com/metacubex/mihomo/common/utils"
|
||||
"github.com/metacubex/mihomo/component/ca"
|
||||
C "github.com/metacubex/mihomo/constant"
|
||||
"github.com/metacubex/mihomo/log"
|
||||
"github.com/metacubex/mihomo/tunnel/statistic"
|
||||
@ -186,7 +186,7 @@ func startTLS(cfg *Config) {
|
||||
|
||||
// handle tlsAddr
|
||||
if len(cfg.TLSAddr) > 0 {
|
||||
c, err := CN.ParseCert(cfg.Certificate, cfg.PrivateKey, C.Path)
|
||||
c, err := ca.LoadTLSKeyPair(cfg.Certificate, cfg.PrivateKey, C.Path)
|
||||
if err != nil {
|
||||
log.Errorln("External controller tls listen error: %s", err)
|
||||
return
|
||||
|
@ -12,7 +12,7 @@ import (
|
||||
"github.com/metacubex/mihomo/adapter/inbound"
|
||||
"github.com/metacubex/mihomo/common/atomic"
|
||||
"github.com/metacubex/mihomo/common/buf"
|
||||
N "github.com/metacubex/mihomo/common/net"
|
||||
"github.com/metacubex/mihomo/component/ca"
|
||||
C "github.com/metacubex/mihomo/constant"
|
||||
LC "github.com/metacubex/mihomo/listener/config"
|
||||
"github.com/metacubex/mihomo/listener/sing"
|
||||
@ -43,7 +43,7 @@ func New(config LC.AnyTLSServer, tunnel C.Tunnel, additions ...inbound.Addition)
|
||||
|
||||
tlsConfig := &tls.Config{}
|
||||
if config.Certificate != "" && config.PrivateKey != "" {
|
||||
cert, err := N.ParseCert(config.Certificate, config.PrivateKey, C.Path)
|
||||
cert, err := ca.LoadTLSKeyPair(config.Certificate, config.PrivateKey, C.Path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ import (
|
||||
"net"
|
||||
|
||||
"github.com/metacubex/mihomo/adapter/inbound"
|
||||
N "github.com/metacubex/mihomo/common/net"
|
||||
"github.com/metacubex/mihomo/component/ca"
|
||||
C "github.com/metacubex/mihomo/constant"
|
||||
authStore "github.com/metacubex/mihomo/listener/auth"
|
||||
LC "github.com/metacubex/mihomo/listener/config"
|
||||
@ -68,7 +68,7 @@ func NewWithConfig(config LC.AuthServer, tunnel C.Tunnel, additions ...inbound.A
|
||||
var realityBuilder *reality.Builder
|
||||
|
||||
if config.Certificate != "" && config.PrivateKey != "" {
|
||||
cert, err := N.ParseCert(config.Certificate, config.PrivateKey, C.Path)
|
||||
cert, err := ca.LoadTLSKeyPair(config.Certificate, config.PrivateKey, C.Path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -30,7 +30,7 @@ var httpPath = "/inbound_test"
|
||||
var httpData = make([]byte, 10240)
|
||||
var remoteAddr = netip.MustParseAddr("1.2.3.4")
|
||||
var userUUID = utils.NewUUIDV4().String()
|
||||
var tlsCertificate, tlsPrivateKey, tlsFingerprint, _ = N.NewRandomTLSKeyPair()
|
||||
var tlsCertificate, tlsPrivateKey, tlsFingerprint, _ = ca.NewRandomTLSKeyPair(ca.KeyPairTypeP256)
|
||||
var tlsConfigCert, _ = tls.X509KeyPair([]byte(tlsCertificate), []byte(tlsPrivateKey))
|
||||
var tlsConfig = &tls.Config{Certificates: []tls.Certificate{tlsConfigCert}, NextProtos: []string{"h2", "http/1.1"}}
|
||||
var tlsClientConfig, _ = ca.GetTLSConfig(nil, tlsFingerprint, "", "")
|
||||
|
@ -8,6 +8,7 @@ import (
|
||||
"github.com/metacubex/mihomo/adapter/inbound"
|
||||
N "github.com/metacubex/mihomo/common/net"
|
||||
"github.com/metacubex/mihomo/component/auth"
|
||||
"github.com/metacubex/mihomo/component/ca"
|
||||
C "github.com/metacubex/mihomo/constant"
|
||||
authStore "github.com/metacubex/mihomo/listener/auth"
|
||||
LC "github.com/metacubex/mihomo/listener/config"
|
||||
@ -63,7 +64,7 @@ func NewWithConfig(config LC.AuthServer, tunnel C.Tunnel, additions ...inbound.A
|
||||
var realityBuilder *reality.Builder
|
||||
|
||||
if config.Certificate != "" && config.PrivateKey != "" {
|
||||
cert, err := N.ParseCert(config.Certificate, config.PrivateKey, C.Path)
|
||||
cert, err := ca.LoadTLSKeyPair(config.Certificate, config.PrivateKey, C.Path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -13,8 +13,8 @@ import (
|
||||
|
||||
"github.com/metacubex/mihomo/adapter/inbound"
|
||||
"github.com/metacubex/mihomo/adapter/outbound"
|
||||
CN "github.com/metacubex/mihomo/common/net"
|
||||
"github.com/metacubex/mihomo/common/sockopt"
|
||||
"github.com/metacubex/mihomo/component/ca"
|
||||
tlsC "github.com/metacubex/mihomo/component/tls"
|
||||
C "github.com/metacubex/mihomo/constant"
|
||||
LC "github.com/metacubex/mihomo/listener/config"
|
||||
@ -56,7 +56,7 @@ func New(config LC.Hysteria2Server, tunnel C.Tunnel, additions ...inbound.Additi
|
||||
|
||||
sl = &Listener{false, config, nil, nil}
|
||||
|
||||
cert, err := CN.ParseCert(config.Certificate, config.PrivateKey, C.Path)
|
||||
cert, err := ca.LoadTLSKeyPair(config.Certificate, config.PrivateKey, C.Path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -11,7 +11,7 @@ import (
|
||||
"unsafe"
|
||||
|
||||
"github.com/metacubex/mihomo/adapter/inbound"
|
||||
N "github.com/metacubex/mihomo/common/net"
|
||||
"github.com/metacubex/mihomo/component/ca"
|
||||
tlsC "github.com/metacubex/mihomo/component/tls"
|
||||
C "github.com/metacubex/mihomo/constant"
|
||||
LC "github.com/metacubex/mihomo/listener/config"
|
||||
@ -87,7 +87,7 @@ func New(config LC.VlessServer, tunnel C.Tunnel, additions ...inbound.Addition)
|
||||
var httpHandler http.Handler
|
||||
|
||||
if config.Certificate != "" && config.PrivateKey != "" {
|
||||
cert, err := N.ParseCert(config.Certificate, config.PrivateKey, C.Path)
|
||||
cert, err := ca.LoadTLSKeyPair(config.Certificate, config.PrivateKey, C.Path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -10,7 +10,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/metacubex/mihomo/adapter/inbound"
|
||||
N "github.com/metacubex/mihomo/common/net"
|
||||
"github.com/metacubex/mihomo/component/ca"
|
||||
C "github.com/metacubex/mihomo/constant"
|
||||
LC "github.com/metacubex/mihomo/listener/config"
|
||||
"github.com/metacubex/mihomo/listener/reality"
|
||||
@ -80,7 +80,7 @@ func New(config LC.VmessServer, tunnel C.Tunnel, additions ...inbound.Addition)
|
||||
var httpHandler http.Handler
|
||||
|
||||
if config.Certificate != "" && config.PrivateKey != "" {
|
||||
cert, err := N.ParseCert(config.Certificate, config.PrivateKey, C.Path)
|
||||
cert, err := ca.LoadTLSKeyPair(config.Certificate, config.PrivateKey, C.Path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ import (
|
||||
"github.com/metacubex/mihomo/adapter/inbound"
|
||||
N "github.com/metacubex/mihomo/common/net"
|
||||
"github.com/metacubex/mihomo/component/auth"
|
||||
"github.com/metacubex/mihomo/component/ca"
|
||||
C "github.com/metacubex/mihomo/constant"
|
||||
authStore "github.com/metacubex/mihomo/listener/auth"
|
||||
LC "github.com/metacubex/mihomo/listener/config"
|
||||
@ -62,7 +63,7 @@ func NewWithConfig(config LC.AuthServer, tunnel C.Tunnel, additions ...inbound.A
|
||||
var realityBuilder *reality.Builder
|
||||
|
||||
if config.Certificate != "" && config.PrivateKey != "" {
|
||||
cert, err := N.ParseCert(config.Certificate, config.PrivateKey, C.Path)
|
||||
cert, err := ca.LoadTLSKeyPair(config.Certificate, config.PrivateKey, C.Path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -9,7 +9,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/metacubex/mihomo/adapter/inbound"
|
||||
N "github.com/metacubex/mihomo/common/net"
|
||||
"github.com/metacubex/mihomo/component/ca"
|
||||
C "github.com/metacubex/mihomo/constant"
|
||||
LC "github.com/metacubex/mihomo/listener/config"
|
||||
"github.com/metacubex/mihomo/listener/reality"
|
||||
@ -74,7 +74,7 @@ func New(config LC.TrojanServer, tunnel C.Tunnel, additions ...inbound.Addition)
|
||||
var httpHandler http.Handler
|
||||
|
||||
if config.Certificate != "" && config.PrivateKey != "" {
|
||||
cert, err := N.ParseCert(config.Certificate, config.PrivateKey, C.Path)
|
||||
cert, err := ca.LoadTLSKeyPair(config.Certificate, config.PrivateKey, C.Path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -7,8 +7,8 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/metacubex/mihomo/adapter/inbound"
|
||||
CN "github.com/metacubex/mihomo/common/net"
|
||||
"github.com/metacubex/mihomo/common/sockopt"
|
||||
"github.com/metacubex/mihomo/component/ca"
|
||||
tlsC "github.com/metacubex/mihomo/component/tls"
|
||||
C "github.com/metacubex/mihomo/constant"
|
||||
LC "github.com/metacubex/mihomo/listener/config"
|
||||
@ -48,7 +48,7 @@ func New(config LC.TuicServer, tunnel C.Tunnel, additions ...inbound.Addition) (
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cert, err := CN.ParseCert(config.Certificate, config.PrivateKey, C.Path)
|
||||
cert, err := ca.LoadTLSKeyPair(config.Certificate, config.PrivateKey, C.Path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user