goproxy icon indicating copy to clipboard operation
goproxy copied to clipboard

feat: should copy information from server when proxy generate certificate for client

Open xudejian opened this issue 4 years ago • 2 comments

we know that some app might verify the SNI or DNSName, and app might also delegate the webview's http connect, so we should copy the correct cert information from server, which would lower the possibility of fail when goproxy handshake with client.

following is my code in my practice.


var certs = make(map[string]*tls.Certificate)

func TLSConfigFromCA(ca *tls.Certificate) func(host string, ctx *goproxy.ProxyCtx) (*tls.Config, error) {
	return func(host string, ctx *goproxy.ProxyCtx) (*tls.Config, error) {
		ctx.Logf("signing for %s", host)

                // should add the certstore back
		cert, err := getCert(host, ca)
		if err != nil {
			ctx.Warnf("Cannot sign host certificate with provided CA: %s", err)
			return nil, err
		}

		config := &tls.Config{
			InsecureSkipVerify: true,
			Certificates:       []tls.Certificate{*cert},
		}
		return config, nil
	}
}


func getCert(hostport string, ca *tls.Certificate) (*tls.Certificate, error) {
	hostname, _, _ := net.SplitHostPort(hostport)
	if cert, ok := certs[hostname]; ok {
		return cert, nil
	}

	conn, err := tls.Dial("tcp", hostport, &tls.Config{InsecureSkipVerify: true})
	if err != nil {
		fmt.Println("failed to connect:", hostport, "Err:", err)
		return nil, err
	}
	defer conn.Close()

	err = conn.Handshake()
	if err != nil {
		return nil, err
	}
	state := conn.ConnectionState()
	cert, err := signHost(ca, state.PeerCertificates[0], []string{hostname})
	if err != nil {
		return nil, err
	}
	certs[hostname] = cert
	return cert, err
}


func signHost(ca *tls.Certificate, orig *x509.Certificate, hosts []string) (cert *tls.Certificate, err error) {
        //
	for _, h := range hosts {
		if ip := net.ParseIP(h); ip != nil {
			template.IPAddresses = append(template.IPAddresses, ip)
		} else {
			template.DNSNames = append(template.DNSNames, h)
			template.Subject.CommonName = h
		}
	}
        //... insert code here, copy information from server
        if orig != nil {
		template.Subject = orig.Subject
		template.DNSNames = orig.DNSNames
		template.EmailAddresses = orig.EmailAddresses
		template.IPAddresses = orig.IPAddresses
		template.URIs = orig.URIs

		template.NotBefore = orig.NotBefore
		template.NotAfter = orig.NotAfter
		template.KeyUsage = orig.KeyUsage
		template.ExtKeyUsage = orig.ExtKeyUsage
	}
        //...

}

xudejian avatar Oct 03 '20 05:10 xudejian

Hello. I was looking at implementing this but the code does not match the current master. There is no "orig" in signHost for example. Do you have a fork where you implemented this?

vbisbest avatar Apr 21 '21 14:04 vbisbest

Hello. I was looking at implementing this but the code does not match the current master. There is no "orig" in signHost for example. Do you have a fork where you implemented this?

this code could work with the current master, according to the example: https://github.com/elazarl/goproxy/blob/master/examples/goproxy-customca/cert.go

we can use our own TLSConfigFromCA to DIY the signHost function.

xudejian avatar Apr 22 '21 02:04 xudejian