June 27, 2013

GOLANG SSL Server and Client example

https://gist.github.com/spikebike/2232102

Below is my simple static "SSL Proxy" that listens on port 8000, and connects to another machine 10.3.0.124:443, and the proxy logs traffic both ways on screen.

To generate key.pem and cert.pem, you can use openssl, or use go team's simple program included in go package: http://golang.org/src/pkg/crypto/tls/generate_cert.go

package main
import (
        "io"
        "log"
        "net"
        "fmt"
        "os"
        "crypto/tls"
        "crypto/rand"
)

func checkError(err error) {
        if err != nil {
                fmt.Fprintf(os.Stderr, "Fatal error: %s", err.Error())
                os.Exit(1)
        }
}

/* slower, by we can print/log everything */
func myrawcopy(dst,src net.Conn) (written int64, err error) {
    buf := make([]byte, 32*1024)
    for {
        nr, er := src.Read(buf)
        if nr > 0 {
                        fmt.Printf("%s",string(buf[0:nr]));
            nw, ew := dst.Write(buf[0:nr])
            if nw > 0 {
                written += int64(nw)
            }
            if ew != nil {
                err = ew
                break
            }
            if nr != nw {
                err = io.ErrShortWrite
                break
            }
        }
        if er == io.EOF {
            break
        }
        if er != nil {
            err = er
            break
        }
    }
    return written, err
}

func myiocopy(dst net.Conn, src net.Conn){
        myrawcopy(dst, src)
        //io.Copy(dst,src);
        dst.Close();
        src.Close();
}

func handleclient(c net.Conn){
        config := tls.Config{InsecureSkipVerify: true}
        conn, err := tls.Dial("tcp", "10.3.0.124:443", &config)
        checkError(err)

        go myiocopy(conn,c)

        //io.Copy(c, conn)
        myrawcopy(c, conn)
        c.Close()
        conn.Close();
}

func main() {
        cert, err := tls.LoadX509KeyPair("cert.pem", "key.pem")
        if err != nil {
                log.Fatalf("server: loadkeys: %s", err)
        }
        config := tls.Config{Certificates: []tls.Certificate{cert}}
        config.Rand = rand.Reader
        service := "0.0.0.0:8000"
        listener, err := tls.Listen("tcp", service, &config)
        if err != nil {
                log.Fatalf("server: listen: %s", err)
        }
        log.Printf("server: listening on %s for https, connects to https://10.3.0.124:443",service)
        for {
                conn, err := listener.Accept()
                if err != nil {
                        log.Printf("server: accept: %s", err)
                        break
                }
                defer conn.Close()
                log.Printf("server: accepted from %s", conn.RemoteAddr())
                go handleclient(conn)
        }
}

6 comments:

  1. Why are you initializing config.Rand? The default is fine.

    ReplyDelete
    Replies
    1. Actually it is the same, by default it's using rand.Rand, but that's an implementation detail: http://golang.org/src/pkg/crypto/tls/common.go Line 277

      Delete
  2. Hello, I stumbled up on your blog while tryign to find solution of my question at http://stackoverflow.com/questions/21562269/golang-how-to-specify-certificate-in-tls-config-for-http-client can you help me to find out as how can I achieve what I am looking for?

    ReplyDelete
  3. dmwm2 has this patch:

    I've got the Windows client connecting through your proxy, which is
    useful. It actually receives the certificate MD5 *from* the server,
    which is kind of pointless, and requires the following hack (replace
    cert hashes as appropriate, of course):

    @@ -18,12 +20,15 @@ func checkError(err error) {
    }

    /* slower, by we can print/log everything */
    -func myrawcopy(dst,src net.Conn) (written int64, err error) {
    +func myrawcopy(dst,src net.Conn, direction string) (written int64, err error) {
    buf := make([]byte, 32*1024)
    + realcert := []byte("cert_md5=90ab8294b0c3fe0b84af563bb4adf37c")
    + mycert := []byte("cert_md5=be9ac5a0dac73483037315ac51b81beb")
    for {
    nr, er := src.Read(buf)
    if nr > 0 {
    - fmt.Printf("%s",string(buf[0:nr]));
    + buf := bytes.Replace(buf, realcert, mycert, 1)
    + fmt.Printf("Packet %s:\n%s",direction, hex.Dump(buf[0:nr]));
    nw, ew := dst.Write(buf[0:nr])
    if nw > 0 {
    written += int64(nw)

    ReplyDelete
  4. You might want to remove 'defer conn.Close()' in the main function. It will cause memory leak.

    ReplyDelete
  5. Hi All,
    Can someone help explaining how can we use proxy IP & port for below code.

    tls.Dial("tcp", "10.3.0.124:443", &config)

    Assume that I want to connect to 10.3.0.124 on port 443 but my network has proxy in IE setting.

    Thanks in Advance.

    ReplyDelete