令quic也支持客户端证书验证

This commit is contained in:
e1732a364fed
2022-05-08 09:11:15 +08:00
parent 5723c73297
commit 0aaf7608b1
14 changed files with 158 additions and 154 deletions

5
.gitignore vendored
View File

@@ -20,4 +20,9 @@ BUILD_VERSION
client.crt
client.csr
client.key
fakeclient.crt
fakeclient.csr
fakeclient.key
fakeca.key
fakeca.crt
.vscode

View File

@@ -220,6 +220,24 @@ openssl req -new -x509 -days 7305 -key cert.key -out cert.pem
此命令会生成ecc证书这个证书比rsa证书 速度更快, 有利于网速加速加速tls握手
#### 使用客户端证书的 高玩情况:
小白请无视这一段。
```sh
# 生成ca的命令:
openssl ecparam -genkey -name prime256v1 -out ca.key
openssl req -new -x509 -days 365 -sha256 -key ca.key -out ca.crt #会提示让你输入 CountryName 等信息。
# 用ca生成客户端key和crt
openssl ecparam -genkey -name prime256v1 -out client.key
openssl req -new -key client.key -out client.csr #会提示 让你输入 CountryName 等信息。
openssl x509 -req -days 365 -sha256 -in client.csr -CA ca.crt -CAkey ca.key -set_serial 01 -out client.crt
```
之后, ca.crt 用于CA (服务端要配置这个), client.key 和 client.crt 用于 客户端证书 (客户端要配置这个)
注意 上面的openssl 生成 crt 的两个命令 要使用 -sha256参数, 因为默认的sha1已经不安全, 在go1.18中被废弃了。
### 交互模式 生成证书

View File

@@ -32,7 +32,7 @@ type Client struct {
connMapMutex sync.RWMutex
}
func NewClient(addr *netLayer.Addr, alpnList []string, host string, insecure bool, args arguments) *Client {
func NewClient(addr *netLayer.Addr, tConf tls.Config, args arguments) *Client {
if args.hysteriaMaxByteCount <= 0 {
args.hysteriaMaxByteCount = Default_hysteriaMaxByteCount
@@ -40,12 +40,8 @@ func NewClient(addr *netLayer.Addr, alpnList []string, host string, insecure boo
return &Client{
serverAddrStr: addr.String(),
tlsConf: tls.Config{
InsecureSkipVerify: insecure,
ServerName: host,
NextProtos: alpnList,
},
arguments: args,
tlsConf: tConf,
arguments: args,
}
}

View File

@@ -25,6 +25,7 @@ Imperfect Package
package quic
import (
"crypto/tls"
"log"
"reflect"
"time"
@@ -139,7 +140,14 @@ func (Creator) NewClientFromConf(conf *advLayer.Conf) (advLayer.Client, error) {
useHysteria, hysteria_manual, maxbyteCount, _ = getExtra(conf.Extra)
}
return NewClient(&conf.Addr, alpn, conf.Host, conf.TlsConf.InsecureSkipVerify, arguments{
var tConf tls.Config
if conf.TlsConf != nil {
tConf = *conf.TlsConf
}
tConf.NextProtos = alpn
return NewClient(&conf.Addr, tConf, arguments{
early: conf.IsEarly,
useHysteria: useHysteria,
hysteria_manual: hysteria_manual,

View File

@@ -27,6 +27,10 @@ advancedLayer = "quic"
# tls = true
# 客户端证书,小白可无视。
#cert = "client.crt"
#key = "client.key"
# early = true # 0-rtt
# 我们可以选择性使用 “hysteria 阻塞控制” 方法, 但是协议依然为quic协议。具体请参考wiki等github页面上的 本作作者对hysteria的讨论

View File

@@ -13,6 +13,8 @@ cert = "cert.pem"
key = "cert.key"
advancedLayer = "quic"
#ca = "ca.crt" # 可选,用于客户端证书 的验证
# network = udp # 只要 advancedLayer 设成了quicnetwork 就会自动被配置为udp所以不需手动指定udp
#alpn = ["asdfsadf"] #如果指定alpn则客户端和服务端都要指定而且要相同

View File

@@ -73,7 +73,7 @@ utls = true #是否使用 utls 来应用 chrome指纹进行伪装
# 如果要使用 websocket/grpc ,则 客户端和服务端 都要配置 advancedLayer 和 path
# 下面cert和key用于 "客户端证书", 小白可以无视,高级玩家可以用, 详见 vlesss.server.toml
# 下面cert和key用于 "客户端证书", 小白可以无视,高级玩家可以用. 证书的生成命令 详见 README.md
#cert = "client.crt"
#key = "client.key"

View File

@@ -16,18 +16,8 @@ key = "cert.key" # 如果 cert和key中 有一项没给出, 或者文件不
#xver = 1 # 可选, 高级用法, 小白不用管. 若为1或者2, 则监听 PROXY protocol, 用于nginx等回落到 verysimple. 实际上目前无论给出的是1还是2, 都会同时监听 v1和v2. 不过这只是目前代码的实现而已, 也许未来会改动, 所以你还是确定选用一个版本.
# ca = "ca.crt" # 用于验证客户端证书
ca = "ca.crt" # 可选, 用于验证客户端证书
# 生成ca的命令:
# openssl ecparam -genkey -name prime256v1 -out ca.key
# openssl req -new -x509 -days 365 -sha256 -key ca.key -out ca.crt #会提示让你输入 CountryName 等信息。
# 用ca生成客户端key和crt
# openssl ecparam -genkey -name prime256v1 -out client.key
# openssl req -new -key client.key -out client.csr #会提示 让你输入 CountryName 等信息。
# openssl x509 -req -days 365 -sha256 -in client.csr -CA ca.crt -CAkey ca.key -set_serial 01 -out client.crt
# 之后 client.key 和 client.crt 就是我们要的 客户端证书。 注意 上面的openssl 生成 crt 的两个命令 要使用 -sha256参数, 因为默认的sha1已经不安全, 在go1.18中被废弃了。
[[dial]]
protocol = "direct"

View File

@@ -321,25 +321,17 @@ func (b *Base) InitAdvLayer() {
var tConf *tls.Config
if creator.IsSuper() {
tConf = &tls.Config{
InsecureSkipVerify: dc.Insecure,
NextProtos: dc.Alpn,
ServerName: dc.Host,
}
var certConf *tlsLayer.CertConf
if dc.TLSCert != "" && dc.TLSKey != "" {
certArray, err := tlsLayer.GetCertArrayFromFile(dc.TLSCert, dc.TLSKey)
if err != nil {
if ce := utils.CanLogErr("load client cert file failed"); ce != nil {
ce.Write(zap.Error(err))
}
} else {
tConf.Certificates = certArray
certConf = &tlsLayer.CertConf{
CertFile: dc.TLSCert,
KeyFile: dc.TLSKey,
}
}
tConf = tlsLayer.GetTlsConfig(dc.Insecure, dc.Alpn, dc.Host, certConf)
}
aConf := &advLayer.Conf{
@@ -377,52 +369,23 @@ func (b *Base) InitAdvLayer() {
Headers = lc.HttpHeader
}
var certArray []tls.Certificate
if creator.IsSuper() {
if lc.TLSCert != "" && lc.TLSKey != "" {
certArray, err = tlsLayer.GetCertArrayFromFile(lc.TLSCert, lc.TLSKey)
if err != nil {
if ce := utils.CanLogErr("can't create tls cert"); ce != nil {
ce.Write(zap.String("cert", lc.TLSCert), zap.String("key", lc.TLSKey), zap.Error(err))
}
return
}
}
}
tConf := &tls.Config{
InsecureSkipVerify: lc.Insecure,
NextProtos: lc.Alpn,
ServerName: lc.Host,
Certificates: certArray,
}
if lc.CA != "" {
certPool, err := tlsLayer.LoadCA(lc.CA)
if err != nil {
if ce := utils.CanLogErr("load CA failed"); ce != nil {
ce.Write(zap.Error(err))
}
} else {
tConf.ClientCAs = certPool
tConf.ClientAuth = tls.RequireAndVerifyClientCert
}
}
advSer, err := creator.NewServerFromConf(&advLayer.Conf{
aConf := &advLayer.Conf{
Path: lc.Path,
Host: lc.Host,
IsEarly: lc.IsEarly,
Xver: lc.Xver,
Addr: ad,
Headers: Headers,
TlsConf: tConf,
Extra: lc.Extra,
})
}
if creator.IsSuper() {
aConf.TlsConf = tlsLayer.GetTlsConfig(lc.Insecure, lc.Alpn, lc.Host, &tlsLayer.CertConf{
CertFile: lc.TLSCert, KeyFile: lc.TLSKey, CA: lc.CA,
})
}
advSer, err := creator.NewServerFromConf(aConf)
if err != nil {
if ce := utils.CanLogErr("InitAdvLayer server failed "); ce != nil {

View File

@@ -154,6 +154,7 @@ func GetCertArrayFromFile(certFile, keyFile string) (certArray []tls.Certificate
}
certArray = GenerateRandomTLSCert()
err = nil
} else {
certArray = []tls.Certificate{cert}

View File

@@ -29,56 +29,15 @@ func NewClient(host string, insecure bool, use_uTls bool, alpnList []string, cer
c.alpnList = alpnList
if use_uTls {
tConf := utls.Config{
InsecureSkipVerify: insecure,
ServerName: host,
NextProtos: alpnList,
}
if certConf != nil {
certArray, err := GetCertArrayFromFile(certConf.CertFile, certConf.KeyFile)
c.uTlsConfig = GetUTlsConfig(insecure, alpnList, host, certConf)
if err != nil {
if ce := utils.CanLogErr("load client cert file failed"); ce != nil {
ce.Write(zap.Error(err))
}
} else {
var array []utls.Certificate
for _, c := range certArray {
array = append(array, *(*utls.Certificate)(unsafe.Pointer(&c)))
}
tConf.Certificates = array
}
}
c.uTlsConfig = tConf
if ce := utils.CanLogInfo("using utls and Chrome fingerprint for"); ce != nil {
if ce := utils.CanLogInfo("using uTls and Chrome fingerprint for"); ce != nil {
ce.Write(zap.String("host", host))
}
} else {
tConf := &tls.Config{
InsecureSkipVerify: insecure,
ServerName: host,
NextProtos: alpnList,
}
if certConf != nil {
certArray, err := GetCertArrayFromFile(certConf.CertFile, certConf.KeyFile)
if err != nil {
if ce := utils.CanLogErr("load client cert file failed"); ce != nil {
ce.Write(zap.Error(err))
}
} else {
tConf.Certificates = certArray
}
}
c.tlsConfig = tConf
c.tlsConfig = GetTlsConfig(insecure, alpnList, host, certConf)
}

View File

@@ -1,6 +1,3 @@
/*
Package tlsLayer provides facilities for tls, including sniffing.
*/
package tlsLayer
var etStrMap map[int]string

View File

@@ -6,7 +6,6 @@ import (
"unsafe"
"github.com/e1732a364fed/v2ray_simple/utils"
"go.uber.org/zap"
"golang.org/x/exp/slices"
)
@@ -17,17 +16,6 @@ type Server struct {
//如 certFile, keyFile 有一项没给出,则会自动生成随机证书
func NewServer(host string, certConf *CertConf, isInsecure bool, alpnList []string) (*Server, error) {
var certArray []tls.Certificate
var err error
if certConf != nil {
certArray, err = GetCertArrayFromFile(certConf.CertFile, certConf.KeyFile)
if err != nil {
return nil, err
}
}
//发现服务端必须给出 http/1.1 等否则不会协商出这个alpn而我们为了回落是需要协商出所有可能需要的 alpn的。
//而且我们如果不提供 h1 和 h2 的alpn的话很容易被审查者察觉的。
@@ -44,29 +32,8 @@ func NewServer(host string, certConf *CertConf, isInsecure bool, alpnList []stri
}
}
tConf := &tls.Config{
InsecureSkipVerify: isInsecure,
ServerName: host,
Certificates: certArray,
NextProtos: alpnList,
}
if certConf != nil {
if certConf.CA != "" {
certPool, err := LoadCA(certConf.CA)
if err != nil {
if ce := utils.CanLogErr("load CA failed"); ce != nil {
ce.Write(zap.Error(err))
}
} else {
tConf.ClientCAs = certPool
tConf.ClientAuth = tls.RequireAndVerifyClientCert
}
}
}
s := &Server{
tlsConfig: tConf,
tlsConfig: GetTlsConfig(isInsecure, alpnList, host, certConf),
}
return s, nil

94
tlsLayer/tlsLayer.go Normal file
View File

@@ -0,0 +1,94 @@
/*
Package tlsLayer provides facilities for tls, including sniffing.
*/
package tlsLayer
import (
"crypto/tls"
"unsafe"
"github.com/e1732a364fed/v2ray_simple/utils"
utls "github.com/refraction-networking/utls"
"go.uber.org/zap"
)
func GetTlsConfig(insecure bool, alpn []string, host string, certConf *CertConf) *tls.Config {
var certArray []tls.Certificate
var err error
if certConf != nil {
certArray, err = GetCertArrayFromFile(certConf.CertFile, certConf.KeyFile)
if err != nil {
if ce := utils.CanLogErr("can't create tls cert"); ce != nil {
ce.Write(zap.String("cert", certConf.CertFile), zap.String("key", certConf.KeyFile), zap.Error(err))
}
certArray = nil
}
}
tConf := &tls.Config{
InsecureSkipVerify: insecure,
NextProtos: alpn,
ServerName: host,
Certificates: certArray,
}
if certConf != nil && certConf.CA != "" {
certPool, err := LoadCA(certConf.CA)
if err != nil {
if ce := utils.CanLogErr("load CA failed"); ce != nil {
ce.Write(zap.Error(err))
}
} else {
tConf.ClientCAs = certPool
tConf.ClientAuth = tls.RequireAndVerifyClientCert
}
}
return tConf
}
func GetUTlsConfig(insecure bool, alpn []string, host string, certConf *CertConf) utls.Config {
var certArray []utls.Certificate
if certConf != nil {
tlscertArray, err := GetCertArrayFromFile(certConf.CertFile, certConf.KeyFile)
if err != nil {
if ce := utils.CanLogErr("load client cert file failed"); ce != nil {
ce.Write(zap.Error(err))
}
certArray = nil
} else {
for _, c := range tlscertArray {
certArray = append(certArray, *(*utls.Certificate)(unsafe.Pointer(&c)))
}
}
}
tConf := utls.Config{
InsecureSkipVerify: insecure,
NextProtos: alpn,
ServerName: host,
Certificates: certArray,
}
if certConf != nil && certConf.CA != "" {
certPool, err := LoadCA(certConf.CA)
if err != nil {
if ce := utils.CanLogErr("load CA failed"); ce != nil {
ce.Write(zap.Error(err))
}
} else {
tConf.ClientCAs = certPool
tConf.ClientAuth = utls.RequireAndVerifyClientCert
}
}
return tConf
}