mirror of
https://github.com/MetaCubeX/mihomo.git
synced 2026-03-03 12:17:31 +00:00
chore: rebuild fingerprint and keypair handle
This commit is contained in:
@@ -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
|
||||
}
|
||||
Reference in New Issue
Block a user