mirror of https://github.com/v2ray/v2ray-core
automatic issuing certificates from provided CA
parent
8ba8ce7d6a
commit
318a36fe58
|
@ -7,17 +7,39 @@ import (
|
||||||
"encoding/pem"
|
"encoding/pem"
|
||||||
"math/big"
|
"math/big"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"v2ray.com/core/common"
|
||||||
)
|
)
|
||||||
|
|
||||||
//go:generate go run $GOPATH/src/v2ray.com/core/common/errors/errorgen/main.go -pkg cert -path Protocol,TLS,Cert
|
//go:generate go run $GOPATH/src/v2ray.com/core/common/errors/errorgen/main.go -pkg cert -path Protocol,TLS,Cert
|
||||||
|
|
||||||
type Certificate struct {
|
type Certificate struct {
|
||||||
// Cerificate in x509 format
|
// Cerificate in ASN.1 DER format
|
||||||
Certificate []byte
|
Certificate []byte
|
||||||
// Private key in x509 format
|
// Private key in ASN.1 DER format
|
||||||
PrivateKey []byte
|
PrivateKey []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ParseCertificate(certPEM []byte, keyPEM []byte) (*Certificate, error) {
|
||||||
|
certBlock, _ := pem.Decode(certPEM)
|
||||||
|
if certBlock == nil {
|
||||||
|
return nil, newError("failed to decode certificate")
|
||||||
|
}
|
||||||
|
keyBlock, _ := pem.Decode(keyPEM)
|
||||||
|
if keyBlock == nil {
|
||||||
|
return nil, newError("failed to decode key")
|
||||||
|
}
|
||||||
|
return &Certificate{
|
||||||
|
Certificate: certBlock.Bytes,
|
||||||
|
PrivateKey: keyBlock.Bytes,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Certificate) ToPEM() ([]byte, []byte) {
|
||||||
|
return pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: c.Certificate}),
|
||||||
|
pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: c.PrivateKey})
|
||||||
|
}
|
||||||
|
|
||||||
type Option func(*x509.Certificate)
|
type Option func(*x509.Certificate)
|
||||||
|
|
||||||
func Authority(isCA bool) Option {
|
func Authority(isCA bool) Option {
|
||||||
|
@ -50,10 +72,31 @@ func CommonName(name string) Option {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func Generate(parent *x509.Certificate, opts ...Option) (*Certificate, error) {
|
func KeyUsage(usage x509.KeyUsage) Option {
|
||||||
priv, err := rsa.GenerateKey(rand.Reader, 2048)
|
return func(c *x509.Certificate) {
|
||||||
|
c.KeyUsage = usage
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func MustGenerate(parent *Certificate, opts ...Option) *Certificate {
|
||||||
|
cert, err := Generate(parent, opts...)
|
||||||
|
common.Must(err)
|
||||||
|
return cert
|
||||||
|
}
|
||||||
|
|
||||||
|
func Generate(parent *Certificate, opts ...Option) (*Certificate, error) {
|
||||||
|
selfKey, err := rsa.GenerateKey(rand.Reader, 2048)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, newError("failed to generate RSA private key").Base(err)
|
return nil, newError("failed to generate self private key").Base(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
parentKey := selfKey
|
||||||
|
if parent != nil {
|
||||||
|
pKey, err := x509.ParsePKCS1PrivateKey(parent.PrivateKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, newError("failed to parse parent private key").Base(err)
|
||||||
|
}
|
||||||
|
parentKey = pKey
|
||||||
}
|
}
|
||||||
|
|
||||||
serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
|
serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
|
||||||
|
@ -75,20 +118,22 @@ func Generate(parent *x509.Certificate, opts ...Option) (*Certificate, error) {
|
||||||
opt(template)
|
opt(template)
|
||||||
}
|
}
|
||||||
|
|
||||||
if parent == nil {
|
parentCert := template
|
||||||
parent = template
|
if parent != nil {
|
||||||
|
pCert, err := x509.ParseCertificate(parent.Certificate)
|
||||||
|
if err != nil {
|
||||||
|
return nil, newError("failed to parse parent certificate").Base(err)
|
||||||
|
}
|
||||||
|
parentCert = pCert
|
||||||
}
|
}
|
||||||
|
|
||||||
derBytes, err := x509.CreateCertificate(rand.Reader, template, parent, priv.Public(), priv)
|
derBytes, err := x509.CreateCertificate(rand.Reader, template, parentCert, selfKey.Public(), parentKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, newError("failed to create certificate").Base(err)
|
return nil, newError("failed to create certificate").Base(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
certPEM := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
|
|
||||||
keyPEM := pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(priv)})
|
|
||||||
|
|
||||||
return &Certificate{
|
return &Certificate{
|
||||||
Certificate: certPEM,
|
Certificate: derBytes,
|
||||||
PrivateKey: keyPEM,
|
PrivateKey: x509.MarshalPKCS1PrivateKey(selfKey),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,8 @@ package scenarios
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
|
"crypto/x509"
|
||||||
|
"runtime"
|
||||||
"sync"
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
@ -10,6 +12,7 @@ import (
|
||||||
"v2ray.com/core/app/proxyman"
|
"v2ray.com/core/app/proxyman"
|
||||||
"v2ray.com/core/common/net"
|
"v2ray.com/core/common/net"
|
||||||
"v2ray.com/core/common/protocol"
|
"v2ray.com/core/common/protocol"
|
||||||
|
"v2ray.com/core/common/protocol/tls/cert"
|
||||||
"v2ray.com/core/common/serial"
|
"v2ray.com/core/common/serial"
|
||||||
"v2ray.com/core/common/uuid"
|
"v2ray.com/core/common/uuid"
|
||||||
"v2ray.com/core/proxy/dokodemo"
|
"v2ray.com/core/proxy/dokodemo"
|
||||||
|
@ -19,7 +22,6 @@ import (
|
||||||
"v2ray.com/core/proxy/vmess/outbound"
|
"v2ray.com/core/proxy/vmess/outbound"
|
||||||
"v2ray.com/core/testing/servers/tcp"
|
"v2ray.com/core/testing/servers/tcp"
|
||||||
"v2ray.com/core/testing/servers/udp"
|
"v2ray.com/core/testing/servers/udp"
|
||||||
tlsgen "v2ray.com/core/testing/tls"
|
|
||||||
"v2ray.com/core/transport/internet"
|
"v2ray.com/core/transport/internet"
|
||||||
"v2ray.com/core/transport/internet/http"
|
"v2ray.com/core/transport/internet/http"
|
||||||
"v2ray.com/core/transport/internet/tls"
|
"v2ray.com/core/transport/internet/tls"
|
||||||
|
@ -49,7 +51,7 @@ func TestSimpleTLSConnection(t *testing.T) {
|
||||||
SecurityType: serial.GetMessageType(&tls.Config{}),
|
SecurityType: serial.GetMessageType(&tls.Config{}),
|
||||||
SecuritySettings: []*serial.TypedMessage{
|
SecuritySettings: []*serial.TypedMessage{
|
||||||
serial.ToTypedMessage(&tls.Config{
|
serial.ToTypedMessage(&tls.Config{
|
||||||
Certificate: []*tls.Certificate{tlsgen.GenerateCertificateForTest()},
|
Certificate: []*tls.Certificate{tls.ParseCertificate(cert.MustGenerate(nil))},
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -141,6 +143,137 @@ func TestSimpleTLSConnection(t *testing.T) {
|
||||||
CloseAllServers(servers)
|
CloseAllServers(servers)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestAutoIssuingCertificate(t *testing.T) {
|
||||||
|
if runtime.GOOS == "windows" {
|
||||||
|
// Not supported on Windows yet.
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
assert := With(t)
|
||||||
|
|
||||||
|
tcpServer := tcp.Server{
|
||||||
|
MsgProcessor: xor,
|
||||||
|
}
|
||||||
|
dest, err := tcpServer.Start()
|
||||||
|
assert(err, IsNil)
|
||||||
|
defer tcpServer.Close()
|
||||||
|
|
||||||
|
caCert, err := cert.Generate(nil, cert.Authority(true), cert.KeyUsage(x509.KeyUsageDigitalSignature|x509.KeyUsageKeyEncipherment|x509.KeyUsageCertSign))
|
||||||
|
assert(err, IsNil)
|
||||||
|
certPEM, keyPEM := caCert.ToPEM()
|
||||||
|
|
||||||
|
userID := protocol.NewID(uuid.New())
|
||||||
|
serverPort := tcp.PickPort()
|
||||||
|
serverConfig := &core.Config{
|
||||||
|
Inbound: []*core.InboundHandlerConfig{
|
||||||
|
{
|
||||||
|
ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{
|
||||||
|
PortRange: net.SinglePortRange(serverPort),
|
||||||
|
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
||||||
|
StreamSettings: &internet.StreamConfig{
|
||||||
|
SecurityType: serial.GetMessageType(&tls.Config{}),
|
||||||
|
SecuritySettings: []*serial.TypedMessage{
|
||||||
|
serial.ToTypedMessage(&tls.Config{
|
||||||
|
Certificate: []*tls.Certificate{{
|
||||||
|
Certificate: certPEM,
|
||||||
|
Key: keyPEM,
|
||||||
|
Usage: tls.Certificate_AUTHORITY_ISSUE,
|
||||||
|
}},
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
ProxySettings: serial.ToTypedMessage(&inbound.Config{
|
||||||
|
User: []*protocol.User{
|
||||||
|
{
|
||||||
|
Account: serial.ToTypedMessage(&vmess.Account{
|
||||||
|
Id: userID.String(),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Outbound: []*core.OutboundHandlerConfig{
|
||||||
|
{
|
||||||
|
ProxySettings: serial.ToTypedMessage(&freedom.Config{}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
clientPort := tcp.PickPort()
|
||||||
|
clientConfig := &core.Config{
|
||||||
|
Inbound: []*core.InboundHandlerConfig{
|
||||||
|
{
|
||||||
|
ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{
|
||||||
|
PortRange: net.SinglePortRange(clientPort),
|
||||||
|
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
||||||
|
}),
|
||||||
|
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
||||||
|
Address: net.NewIPOrDomain(dest.Address),
|
||||||
|
Port: uint32(dest.Port),
|
||||||
|
NetworkList: &net.NetworkList{
|
||||||
|
Network: []net.Network{net.Network_TCP},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Outbound: []*core.OutboundHandlerConfig{
|
||||||
|
{
|
||||||
|
ProxySettings: serial.ToTypedMessage(&outbound.Config{
|
||||||
|
Receiver: []*protocol.ServerEndpoint{
|
||||||
|
{
|
||||||
|
Address: net.NewIPOrDomain(net.LocalHostIP),
|
||||||
|
Port: uint32(serverPort),
|
||||||
|
User: []*protocol.User{
|
||||||
|
{
|
||||||
|
Account: serial.ToTypedMessage(&vmess.Account{
|
||||||
|
Id: userID.String(),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
SenderSettings: serial.ToTypedMessage(&proxyman.SenderConfig{
|
||||||
|
StreamSettings: &internet.StreamConfig{
|
||||||
|
SecurityType: serial.GetMessageType(&tls.Config{}),
|
||||||
|
SecuritySettings: []*serial.TypedMessage{
|
||||||
|
serial.ToTypedMessage(&tls.Config{
|
||||||
|
ServerName: "v2ray.com",
|
||||||
|
Certificate: []*tls.Certificate{{
|
||||||
|
Certificate: certPEM,
|
||||||
|
Usage: tls.Certificate_AUTHORITY_VERIFY,
|
||||||
|
}},
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
servers, err := InitializeServerConfigs(serverConfig, clientConfig)
|
||||||
|
assert(err, IsNil)
|
||||||
|
|
||||||
|
conn, err := net.DialTCP("tcp", nil, &net.TCPAddr{
|
||||||
|
IP: []byte{127, 0, 0, 1},
|
||||||
|
Port: int(clientPort),
|
||||||
|
})
|
||||||
|
assert(err, IsNil)
|
||||||
|
|
||||||
|
payload := "dokodemo request."
|
||||||
|
nBytes, err := conn.Write([]byte(payload))
|
||||||
|
assert(err, IsNil)
|
||||||
|
assert(nBytes, Equals, len(payload))
|
||||||
|
|
||||||
|
response := readFrom(conn, time.Second*2, len(payload))
|
||||||
|
assert(response, Equals, xor([]byte(payload)))
|
||||||
|
assert(conn.Close(), IsNil)
|
||||||
|
|
||||||
|
CloseAllServers(servers)
|
||||||
|
}
|
||||||
|
|
||||||
func TestTLSOverKCP(t *testing.T) {
|
func TestTLSOverKCP(t *testing.T) {
|
||||||
assert := With(t)
|
assert := With(t)
|
||||||
|
|
||||||
|
@ -164,7 +297,7 @@ func TestTLSOverKCP(t *testing.T) {
|
||||||
SecurityType: serial.GetMessageType(&tls.Config{}),
|
SecurityType: serial.GetMessageType(&tls.Config{}),
|
||||||
SecuritySettings: []*serial.TypedMessage{
|
SecuritySettings: []*serial.TypedMessage{
|
||||||
serial.ToTypedMessage(&tls.Config{
|
serial.ToTypedMessage(&tls.Config{
|
||||||
Certificate: []*tls.Certificate{tlsgen.GenerateCertificateForTest()},
|
Certificate: []*tls.Certificate{tls.ParseCertificate(cert.MustGenerate(nil))},
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -280,7 +413,7 @@ func TestTLSOverWebSocket(t *testing.T) {
|
||||||
SecurityType: serial.GetMessageType(&tls.Config{}),
|
SecurityType: serial.GetMessageType(&tls.Config{}),
|
||||||
SecuritySettings: []*serial.TypedMessage{
|
SecuritySettings: []*serial.TypedMessage{
|
||||||
serial.ToTypedMessage(&tls.Config{
|
serial.ToTypedMessage(&tls.Config{
|
||||||
Certificate: []*tls.Certificate{tlsgen.GenerateCertificateForTest()},
|
Certificate: []*tls.Certificate{tls.ParseCertificate(cert.MustGenerate(nil))},
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -412,7 +545,7 @@ func TestHTTP2(t *testing.T) {
|
||||||
SecurityType: serial.GetMessageType(&tls.Config{}),
|
SecurityType: serial.GetMessageType(&tls.Config{}),
|
||||||
SecuritySettings: []*serial.TypedMessage{
|
SecuritySettings: []*serial.TypedMessage{
|
||||||
serial.ToTypedMessage(&tls.Config{
|
serial.ToTypedMessage(&tls.Config{
|
||||||
Certificate: []*tls.Certificate{tlsgen.GenerateCertificateForTest()},
|
Certificate: []*tls.Certificate{tls.ParseCertificate(cert.MustGenerate(nil))},
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -9,8 +9,8 @@ import (
|
||||||
"v2ray.com/core/common"
|
"v2ray.com/core/common"
|
||||||
"v2ray.com/core/common/buf"
|
"v2ray.com/core/common/buf"
|
||||||
"v2ray.com/core/common/net"
|
"v2ray.com/core/common/net"
|
||||||
|
"v2ray.com/core/common/protocol/tls/cert"
|
||||||
"v2ray.com/core/testing/servers/tcp"
|
"v2ray.com/core/testing/servers/tcp"
|
||||||
tlsgen "v2ray.com/core/testing/tls"
|
|
||||||
"v2ray.com/core/transport/internet"
|
"v2ray.com/core/transport/internet"
|
||||||
. "v2ray.com/core/transport/internet/http"
|
. "v2ray.com/core/transport/internet/http"
|
||||||
"v2ray.com/core/transport/internet/tls"
|
"v2ray.com/core/transport/internet/tls"
|
||||||
|
@ -24,7 +24,7 @@ func TestHTTPConnection(t *testing.T) {
|
||||||
|
|
||||||
lctx := context.Background()
|
lctx := context.Background()
|
||||||
lctx = internet.ContextWithSecuritySettings(lctx, &tls.Config{
|
lctx = internet.ContextWithSecuritySettings(lctx, &tls.Config{
|
||||||
Certificate: []*tls.Certificate{tlsgen.GenerateCertificateForTest()},
|
Certificate: []*tls.Certificate{tls.ParseCertificate(cert.MustGenerate(nil, cert.CommonName("www.v2ray.com")))},
|
||||||
})
|
})
|
||||||
lctx = internet.ContextWithTransportSettings(lctx, &Config{})
|
lctx = internet.ContextWithTransportSettings(lctx, &Config{})
|
||||||
|
|
||||||
|
|
|
@ -3,8 +3,11 @@ package tls
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
|
"crypto/x509"
|
||||||
|
"time"
|
||||||
|
|
||||||
"v2ray.com/core/common/net"
|
"v2ray.com/core/common/net"
|
||||||
|
"v2ray.com/core/common/protocol/tls/cert"
|
||||||
"v2ray.com/core/transport/internet"
|
"v2ray.com/core/transport/internet"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -12,9 +15,36 @@ var (
|
||||||
globalSessionCache = tls.NewLRUClientSessionCache(128)
|
globalSessionCache = tls.NewLRUClientSessionCache(128)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func ParseCertificate(c *cert.Certificate) *Certificate {
|
||||||
|
certPEM, keyPEM := c.ToPEM()
|
||||||
|
return &Certificate{
|
||||||
|
Certificate: certPEM,
|
||||||
|
Key: keyPEM,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Config) GetCertPool() *x509.CertPool {
|
||||||
|
pool, err := x509.SystemCertPool()
|
||||||
|
if err != nil {
|
||||||
|
newError("failed to get system cert pool.").Base(err).WriteToLog()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if pool != nil {
|
||||||
|
for _, cert := range c.Certificate {
|
||||||
|
if cert.Usage == Certificate_AUTHORITY_VERIFY {
|
||||||
|
pool.AppendCertsFromPEM(cert.Certificate)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return pool
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Config) BuildCertificates() []tls.Certificate {
|
func (c *Config) BuildCertificates() []tls.Certificate {
|
||||||
certs := make([]tls.Certificate, 0, len(c.Certificate))
|
certs := make([]tls.Certificate, 0, len(c.Certificate))
|
||||||
for _, entry := range c.Certificate {
|
for _, entry := range c.Certificate {
|
||||||
|
if entry.Usage != Certificate_ENCIPHERMENT {
|
||||||
|
continue
|
||||||
|
}
|
||||||
keyPair, err := tls.X509KeyPair(entry.Certificate, entry.Key)
|
keyPair, err := tls.X509KeyPair(entry.Certificate, entry.Key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
newError("ignoring invalid X509 key pair").Base(err).AtWarning().WriteToLog()
|
newError("ignoring invalid X509 key pair").Base(err).AtWarning().WriteToLog()
|
||||||
|
@ -25,9 +55,29 @@ func (c *Config) BuildCertificates() []tls.Certificate {
|
||||||
return certs
|
return certs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func isCertificateExpired(c *tls.Certificate) bool {
|
||||||
|
// If leaf is not there, the certificate is probably not used yet. We trust user to provide a valid certificate.
|
||||||
|
return c.Leaf != nil && c.Leaf.NotAfter.After(time.Now().Add(-time.Minute))
|
||||||
|
}
|
||||||
|
|
||||||
|
func issueCertificate(rawCA *Certificate, domain string) (*tls.Certificate, error) {
|
||||||
|
parent, err := cert.ParseCertificate(rawCA.Certificate, rawCA.Key)
|
||||||
|
if err != nil {
|
||||||
|
return nil, newError("failed to parse raw certificate").Base(err)
|
||||||
|
}
|
||||||
|
newCert, err := cert.Generate(parent, cert.CommonName(domain), cert.DNSNames(domain))
|
||||||
|
if err != nil {
|
||||||
|
return nil, newError("failed to generate new certificate for ", domain).Base(err)
|
||||||
|
}
|
||||||
|
newCertPEM, newKeyPEM := newCert.ToPEM()
|
||||||
|
cert, err := tls.X509KeyPair(newCertPEM, newKeyPEM)
|
||||||
|
return &cert, err
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Config) GetTLSConfig(opts ...Option) *tls.Config {
|
func (c *Config) GetTLSConfig(opts ...Option) *tls.Config {
|
||||||
config := &tls.Config{
|
config := &tls.Config{
|
||||||
ClientSessionCache: globalSessionCache,
|
ClientSessionCache: globalSessionCache,
|
||||||
|
RootCAs: c.GetCertPool(),
|
||||||
}
|
}
|
||||||
if c == nil {
|
if c == nil {
|
||||||
return config
|
return config
|
||||||
|
@ -40,6 +90,53 @@ func (c *Config) GetTLSConfig(opts ...Option) *tls.Config {
|
||||||
config.InsecureSkipVerify = c.AllowInsecure
|
config.InsecureSkipVerify = c.AllowInsecure
|
||||||
config.Certificates = c.BuildCertificates()
|
config.Certificates = c.BuildCertificates()
|
||||||
config.BuildNameToCertificate()
|
config.BuildNameToCertificate()
|
||||||
|
config.GetCertificate = func(hello *tls.ClientHelloInfo) (*tls.Certificate, error) {
|
||||||
|
domain := hello.ServerName
|
||||||
|
certExpired := false
|
||||||
|
if certificate, found := config.NameToCertificate[domain]; found {
|
||||||
|
if !isCertificateExpired(certificate) {
|
||||||
|
return certificate, nil
|
||||||
|
}
|
||||||
|
certExpired = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if certExpired {
|
||||||
|
newCerts := make([]tls.Certificate, 0, len(config.Certificates))
|
||||||
|
|
||||||
|
for _, certificate := range config.Certificates {
|
||||||
|
if !isCertificateExpired(&certificate) {
|
||||||
|
newCerts = append(newCerts, certificate)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
config.Certificates = newCerts
|
||||||
|
}
|
||||||
|
|
||||||
|
var issuedCertificate *tls.Certificate
|
||||||
|
|
||||||
|
// Create a new certificate from existing CA if possible
|
||||||
|
for _, rawCert := range c.Certificate {
|
||||||
|
if rawCert.Usage == Certificate_AUTHORITY_ISSUE {
|
||||||
|
newCert, err := issueCertificate(rawCert, domain)
|
||||||
|
if err != nil {
|
||||||
|
newError("failed to issue new certificate for ", domain).Base(err).WriteToLog()
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
config.Certificates = append(config.Certificates, *newCert)
|
||||||
|
issuedCertificate = &config.Certificates[len(config.Certificates)-1]
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if issuedCertificate == nil {
|
||||||
|
return nil, newError("failed to create a new certificate for ", domain)
|
||||||
|
}
|
||||||
|
|
||||||
|
config.BuildNameToCertificate()
|
||||||
|
|
||||||
|
return issuedCertificate, nil
|
||||||
|
}
|
||||||
if len(c.ServerName) > 0 {
|
if len(c.ServerName) > 0 {
|
||||||
config.ServerName = c.ServerName
|
config.ServerName = c.ServerName
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,11 +15,36 @@ var _ = math.Inf
|
||||||
// proto package needs to be updated.
|
// proto package needs to be updated.
|
||||||
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
|
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
|
||||||
|
|
||||||
|
type Certificate_Usage int32
|
||||||
|
|
||||||
|
const (
|
||||||
|
Certificate_ENCIPHERMENT Certificate_Usage = 0
|
||||||
|
Certificate_AUTHORITY_VERIFY Certificate_Usage = 1
|
||||||
|
Certificate_AUTHORITY_ISSUE Certificate_Usage = 2
|
||||||
|
)
|
||||||
|
|
||||||
|
var Certificate_Usage_name = map[int32]string{
|
||||||
|
0: "ENCIPHERMENT",
|
||||||
|
1: "AUTHORITY_VERIFY",
|
||||||
|
2: "AUTHORITY_ISSUE",
|
||||||
|
}
|
||||||
|
var Certificate_Usage_value = map[string]int32{
|
||||||
|
"ENCIPHERMENT": 0,
|
||||||
|
"AUTHORITY_VERIFY": 1,
|
||||||
|
"AUTHORITY_ISSUE": 2,
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x Certificate_Usage) String() string {
|
||||||
|
return proto.EnumName(Certificate_Usage_name, int32(x))
|
||||||
|
}
|
||||||
|
func (Certificate_Usage) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{0, 0} }
|
||||||
|
|
||||||
type Certificate struct {
|
type Certificate struct {
|
||||||
// TLS certificate in x509 format.
|
// TLS certificate in x509 format.
|
||||||
Certificate []byte `protobuf:"bytes,1,opt,name=Certificate,proto3" json:"Certificate,omitempty"`
|
Certificate []byte `protobuf:"bytes,1,opt,name=Certificate,proto3" json:"Certificate,omitempty"`
|
||||||
// TLS key in x509 format.
|
// TLS key in x509 format.
|
||||||
Key []byte `protobuf:"bytes,2,opt,name=Key,proto3" json:"Key,omitempty"`
|
Key []byte `protobuf:"bytes,2,opt,name=Key,proto3" json:"Key,omitempty"`
|
||||||
|
Usage Certificate_Usage `protobuf:"varint,3,opt,name=usage,enum=v2ray.core.transport.internet.tls.Certificate_Usage" json:"usage,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Certificate) Reset() { *m = Certificate{} }
|
func (m *Certificate) Reset() { *m = Certificate{} }
|
||||||
|
@ -41,6 +66,13 @@ func (m *Certificate) GetKey() []byte {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *Certificate) GetUsage() Certificate_Usage {
|
||||||
|
if m != nil {
|
||||||
|
return m.Usage
|
||||||
|
}
|
||||||
|
return Certificate_ENCIPHERMENT
|
||||||
|
}
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
// Whether or not to allow self-signed certificates.
|
// Whether or not to allow self-signed certificates.
|
||||||
AllowInsecure bool `protobuf:"varint,1,opt,name=allow_insecure,json=allowInsecure" json:"allow_insecure,omitempty"`
|
AllowInsecure bool `protobuf:"varint,1,opt,name=allow_insecure,json=allowInsecure" json:"allow_insecure,omitempty"`
|
||||||
|
@ -88,28 +120,34 @@ func (m *Config) GetNextProtocol() []string {
|
||||||
func init() {
|
func init() {
|
||||||
proto.RegisterType((*Certificate)(nil), "v2ray.core.transport.internet.tls.Certificate")
|
proto.RegisterType((*Certificate)(nil), "v2ray.core.transport.internet.tls.Certificate")
|
||||||
proto.RegisterType((*Config)(nil), "v2ray.core.transport.internet.tls.Config")
|
proto.RegisterType((*Config)(nil), "v2ray.core.transport.internet.tls.Config")
|
||||||
|
proto.RegisterEnum("v2ray.core.transport.internet.tls.Certificate_Usage", Certificate_Usage_name, Certificate_Usage_value)
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() { proto.RegisterFile("v2ray.com/core/transport/internet/tls/config.proto", fileDescriptor0) }
|
func init() { proto.RegisterFile("v2ray.com/core/transport/internet/tls/config.proto", fileDescriptor0) }
|
||||||
|
|
||||||
var fileDescriptor0 = []byte{
|
var fileDescriptor0 = []byte{
|
||||||
// 278 bytes of a gzipped FileDescriptorProto
|
// 358 bytes of a gzipped FileDescriptorProto
|
||||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x84, 0x90, 0x41, 0x6a, 0xeb, 0x30,
|
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x91, 0xd1, 0x6e, 0xda, 0x30,
|
||||||
0x10, 0x86, 0x71, 0xfc, 0x08, 0x2f, 0x72, 0x52, 0x8a, 0x56, 0xde, 0xd5, 0x49, 0x09, 0x78, 0x25,
|
0x14, 0x40, 0x97, 0x64, 0xa0, 0xe1, 0x00, 0x8b, 0xbc, 0x3d, 0xe4, 0x6d, 0x81, 0x09, 0x29, 0x4f,
|
||||||
0x83, 0x7b, 0x82, 0xd6, 0xab, 0x50, 0x28, 0x46, 0x84, 0x2e, 0xba, 0x31, 0xaa, 0x98, 0x14, 0x83,
|
0x8e, 0x94, 0xed, 0x07, 0xb6, 0x34, 0x15, 0x69, 0x55, 0x1a, 0x99, 0x80, 0x44, 0x5f, 0x22, 0xd7,
|
||||||
0x2c, 0x85, 0xd1, 0x34, 0xad, 0xaf, 0xd4, 0x8b, 0xf4, 0x5a, 0xc5, 0x4e, 0x1c, 0x9c, 0x55, 0x76,
|
0x32, 0x28, 0x52, 0x12, 0x23, 0xdb, 0xd0, 0xf2, 0x4b, 0xfd, 0x91, 0x7e, 0x40, 0x7f, 0xa8, 0x4a,
|
||||||
0xd2, 0xa7, 0x4f, 0xf3, 0xff, 0x0c, 0xcb, 0x0f, 0x39, 0xaa, 0x56, 0x68, 0xd7, 0x64, 0xda, 0x21,
|
0x02, 0x14, 0x9e, 0x50, 0xdf, 0x7c, 0x8f, 0xcf, 0xbd, 0xd7, 0xf7, 0x1a, 0xf8, 0x5b, 0x5f, 0x90,
|
||||||
0x64, 0x84, 0xca, 0xfa, 0xbd, 0x43, 0xca, 0x6a, 0x4b, 0x80, 0x16, 0x28, 0x23, 0xe3, 0x33, 0xed,
|
0x1d, 0xa2, 0xbc, 0xf0, 0x28, 0x17, 0xcc, 0x53, 0x82, 0x94, 0x72, 0xcd, 0x85, 0xf2, 0xb2, 0x52,
|
||||||
0xec, 0xae, 0xfe, 0x10, 0x7b, 0x74, 0xe4, 0xf8, 0x72, 0xf8, 0x83, 0x20, 0xce, 0xbe, 0x18, 0x7c,
|
0x31, 0x51, 0x32, 0xe5, 0xa9, 0x5c, 0x7a, 0x94, 0x97, 0xcb, 0x6c, 0x85, 0xd6, 0x82, 0x2b, 0x0e,
|
||||||
0x41, 0xc6, 0xaf, 0x1e, 0x59, 0x54, 0x00, 0x52, 0xbd, 0xab, 0xb5, 0x22, 0xe0, 0xc9, 0xc5, 0x35,
|
0x07, 0x87, 0x1c, 0xc1, 0xd0, 0xd1, 0x47, 0x07, 0x1f, 0xa9, 0x5c, 0x0e, 0xdf, 0x34, 0x60, 0x06,
|
||||||
0x0e, 0x92, 0x20, 0x9d, 0xcb, 0x0b, 0xe3, 0x96, 0x85, 0xcf, 0xd0, 0xc6, 0x93, 0xfe, 0xa5, 0x3b,
|
0x4c, 0xa8, 0x6c, 0x99, 0x51, 0xa2, 0x18, 0x74, 0xce, 0x42, 0x5b, 0x73, 0x34, 0xb7, 0x8b, 0xcf,
|
||||||
0xae, 0x7e, 0x03, 0x36, 0x2d, 0xfa, 0x58, 0xbe, 0x66, 0x37, 0xca, 0x18, 0xf7, 0x55, 0xd5, 0xd6,
|
0x0c, 0x0b, 0x18, 0xb7, 0x6c, 0x67, 0xeb, 0xf5, 0x4d, 0x75, 0x84, 0x37, 0xa0, 0xb5, 0x91, 0x64,
|
||||||
0x83, 0xfe, 0xc4, 0xe3, 0x84, 0xff, 0x72, 0xd1, 0xd3, 0xcd, 0x09, 0xf2, 0x92, 0x45, 0x7a, 0x94,
|
0xc5, 0x6c, 0xc3, 0xd1, 0xdc, 0xbe, 0xff, 0x17, 0x5d, 0x6c, 0x8b, 0x4e, 0x0a, 0xa2, 0x59, 0x95,
|
||||||
0x32, 0x49, 0xc2, 0x34, 0xca, 0x85, 0xb8, 0xda, 0x56, 0x8c, 0x8a, 0xc8, 0xf1, 0x08, 0x7e, 0xc7,
|
0x8b, 0x9b, 0x12, 0xc3, 0x2b, 0xd0, 0xaa, 0x63, 0x68, 0x81, 0x6e, 0x38, 0x09, 0xa2, 0x78, 0x1c,
|
||||||
0x22, 0x0f, 0x78, 0x00, 0xac, 0xac, 0x6a, 0x20, 0x0e, 0x93, 0x20, 0x9d, 0x49, 0x76, 0x44, 0x2f,
|
0xe2, 0xbb, 0x70, 0x92, 0x58, 0x5f, 0xe0, 0x4f, 0x60, 0xfd, 0x9b, 0x25, 0xe3, 0x7b, 0x1c, 0x25,
|
||||||
0xaa, 0x01, 0x7e, 0xcf, 0x16, 0x16, 0xbe, 0xa9, 0xea, 0x17, 0xa3, 0x9d, 0x89, 0xff, 0x25, 0x61,
|
0x8b, 0x74, 0x1e, 0xe2, 0xe8, 0x7a, 0x61, 0x69, 0xf0, 0x07, 0xf8, 0xfe, 0x41, 0xa3, 0xe9, 0x74,
|
||||||
0x3a, 0x93, 0xf3, 0x0e, 0x96, 0x27, 0xf6, 0x24, 0xd9, 0x5a, 0xbb, 0xe6, 0x7a, 0x8f, 0x32, 0x78,
|
0x16, 0x5a, 0xfa, 0xf0, 0x55, 0x03, 0xed, 0xa0, 0xde, 0x04, 0x1c, 0x81, 0x3e, 0xc9, 0x73, 0xfe,
|
||||||
0x0b, 0xc9, 0xf8, 0x9f, 0xc9, 0xf2, 0x35, 0x97, 0xaa, 0x15, 0x45, 0xa7, 0x6e, 0xcf, 0xea, 0x66,
|
0x94, 0x66, 0xa5, 0x64, 0x74, 0x23, 0x9a, 0x99, 0xbe, 0xe1, 0x5e, 0x4d, 0xa3, 0x3d, 0x84, 0x31,
|
||||||
0x50, 0xb7, 0xc6, 0xbf, 0x4f, 0xfb, 0xc4, 0x87, 0xbf, 0x00, 0x00, 0x00, 0xff, 0xff, 0x3f, 0x84,
|
0x30, 0xe9, 0xc9, 0xdc, 0xba, 0x63, 0xb8, 0xa6, 0x8f, 0x3e, 0x37, 0x09, 0x3e, 0x2d, 0x01, 0x7f,
|
||||||
0xfb, 0x07, 0xc0, 0x01, 0x00, 0x00,
|
0x01, 0x53, 0x32, 0xb1, 0x65, 0x22, 0x2d, 0x49, 0xd1, 0xec, 0xa6, 0x83, 0x41, 0x83, 0x26, 0xa4,
|
||||||
|
0x60, 0xf0, 0x37, 0xe8, 0x95, 0xec, 0x59, 0xa5, 0xf5, 0x5f, 0x51, 0x9e, 0xdb, 0x5f, 0x1d, 0xc3,
|
||||||
|
0xed, 0xe0, 0x6e, 0x05, 0xe3, 0x3d, 0xfb, 0x8f, 0xc1, 0x88, 0xf2, 0xe2, 0xf2, 0x3b, 0x62, 0xed,
|
||||||
|
0xc1, 0x50, 0xb9, 0x7c, 0xd1, 0x07, 0x73, 0x1f, 0x93, 0x1d, 0x0a, 0x2a, 0x35, 0x39, 0xaa, 0xd1,
|
||||||
|
0x41, 0x4d, 0x72, 0xf9, 0xd8, 0xae, 0x3b, 0xfe, 0x79, 0x0f, 0x00, 0x00, 0xff, 0xff, 0x0b, 0xcd,
|
||||||
|
0x2a, 0x68, 0x53, 0x02, 0x00, 0x00,
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,14 @@ message Certificate {
|
||||||
|
|
||||||
// TLS key in x509 format.
|
// TLS key in x509 format.
|
||||||
bytes Key = 2;
|
bytes Key = 2;
|
||||||
|
|
||||||
|
enum Usage {
|
||||||
|
ENCIPHERMENT = 0;
|
||||||
|
AUTHORITY_VERIFY = 1;
|
||||||
|
AUTHORITY_ISSUE = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
Usage usage = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
message Config {
|
message Config {
|
||||||
|
|
|
@ -7,9 +7,9 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"v2ray.com/core/common/net"
|
"v2ray.com/core/common/net"
|
||||||
tlsgen "v2ray.com/core/testing/tls"
|
"v2ray.com/core/common/protocol/tls/cert"
|
||||||
"v2ray.com/core/transport/internet"
|
"v2ray.com/core/transport/internet"
|
||||||
v2tls "v2ray.com/core/transport/internet/tls"
|
"v2ray.com/core/transport/internet/tls"
|
||||||
. "v2ray.com/core/transport/internet/websocket"
|
. "v2ray.com/core/transport/internet/websocket"
|
||||||
. "v2ray.com/ext/assert"
|
. "v2ray.com/ext/assert"
|
||||||
)
|
)
|
||||||
|
@ -109,9 +109,9 @@ func Test_listenWSAndDial_TLS(t *testing.T) {
|
||||||
ctx := internet.ContextWithTransportSettings(context.Background(), &Config{
|
ctx := internet.ContextWithTransportSettings(context.Background(), &Config{
|
||||||
Path: "wss",
|
Path: "wss",
|
||||||
})
|
})
|
||||||
ctx = internet.ContextWithSecuritySettings(ctx, &v2tls.Config{
|
ctx = internet.ContextWithSecuritySettings(ctx, &tls.Config{
|
||||||
AllowInsecure: true,
|
AllowInsecure: true,
|
||||||
Certificate: []*v2tls.Certificate{tlsgen.GenerateCertificateForTest()},
|
Certificate: []*tls.Certificate{tls.ParseCertificate(cert.MustGenerate(nil, cert.CommonName("localhost")))},
|
||||||
})
|
})
|
||||||
listen, err := ListenWS(ctx, net.DomainAddress("localhost"), 13143, func(conn internet.Connection) {
|
listen, err := ListenWS(ctx, net.DomainAddress("localhost"), 13143, func(conn internet.Connection) {
|
||||||
go func() {
|
go func() {
|
||||||
|
|
Loading…
Reference in New Issue