The certificates used in this section were discussed in the section TL: General . They are
A Go client talking to an HTTPS server with a valid certificate is TLSGetHead.go:
/* TLSGetHead
*/
package main
import (
"crypto/tls"
"fmt"
"io/ioutil"
"os"
)
func main() {
service := "www.verisign.com:443"
conn, err := tls.Dial("tcp", service, nil)
checkError(err)
_, err = conn.Write([]byte("HEAD / HTTP/1.0\r\n\r\n"))
checkError(err)
result, err := ioutil.ReadAll(conn)
checkError(err)
fmt.Println(string(result))
conn.Close()
os.Exit(0)
}
func checkError(err error) {
if err != nil {
fmt.Println("Fatal error ", err.Error())
os.Exit(1)
}
}
When run by
go run TLSGetHead.go
it produces essentially the same results as the Java client above.
If the server uses its own CA, then it needs to load its CA signed certificate and set this into its TLS configuration. It can then listen for client connections.
The server is TLSEchoServer.go:
/* TLSEchoServer
*/
package main
import (
"crypto/rand"
"crypto/tls"
"fmt"
"net"
"os"
"time"
)
func main() {
cert, err := tls.LoadX509KeyPair("../certs/CA-signed-cert.pem", "../certs/key.pem")
//cert, err := tls.LoadX509KeyPair("../certs/self-signed-cert.pem", "../certs/key.pem")
checkError(err)
config := tls.Config{Certificates: []tls.Certificate{cert}}
now := time.Now()
config.Time = func() time.Time { return now }
config.Rand = rand.Reader
service := "0.0.0.0:1200"
listener, err := tls.Listen("tcp", service, &config)
checkError(err)
fmt.Println("Listening")
for {
conn, err := listener.Accept()
if err != nil {
fmt.Println(err.Error())
continue
}
fmt.Println("Accepted")
go handleClient(conn)
}
}
func handleClient(conn net.Conn) {
defer conn.Close()
var buf [512]byte
for {
fmt.Println("Trying to read")
n, err := conn.Read(buf[0:])
if err != nil {
fmt.Println(err)
return
}
_, err = conn.Write(buf[0:n])
if err != nil {
return
}
}
}
func checkError(err error) {
if err != nil {
fmt.Println("Fatal error ", err.Error())
os.Exit(1)
}
}
The client that talks to this server needs to have the private CA certificate. It reads this and then appends it to a new CertificatePool. If successful, it can then read and write to a server using certificates signed by that CA.
The client is TLSEchoClient.go:
/* TLSEchoClient
*/
package main
import (
"crypto/tls"
"crypto/x509"
"fmt"
"os"
)
func main() {
if len(os.Args) != 2 {
fmt.Println("Usage: ", os.Args[0], "host:port")
os.Exit(1)
}
service := os.Args[1]
certPemFile, err := os.Open("../certs/CA-cert.pem")
//certPemFile, err := os.Open("../certs/self-signed-cert.pem")
checkError(err)
pemBytes := make([]byte, 10000) // bigger than the file
_, err = certPemFile.Read(pemBytes)
checkError(err)
certPemFile.Close()
// Create a new certificate pool
certPool := x509.NewCertPool()
// and add our certificate
ok := certPool.AppendCertsFromPEM(pemBytes)
if !ok {
fmt.Println("PEM read failed")
} else {
fmt.Println("PEM read ok")
}
// Dial, using a config with root cert set to ours
conn, err := tls.Dial("tcp", service, &tls.Config{RootCAs: certPool})
checkError(err)
// Now write and read lots
for n := 0; n < 10; n++ {
fmt.Println("Writing...")
conn.Write([]byte("Hello " + string(n+48)))
var buf [512]byte
n, err := conn.Read(buf[0:])
checkError(err)
fmt.Println(string(buf[0:n]))
}
conn.Close()
os.Exit(0)
}
func checkError(err error) {
if err != nil {
fmt.Println("Fatal error ", err.Error())
os.Exit(1)
}
}
The client and server in the last section had lines to load the appropriate certificates from the private CA. Following these were commented lines to load the self-signed certificates instead. If these are uncommented and the others commented out, they both talk to each other using the self-signed certificates. No other changes are needed.
If you get a certificate request signed by a CA, you will probably get two certificates
If you want to build your own server, you need to concatenate these two files
into a single one for the call to
tls.LoadX509KeyPair()
.
This can be done by e.g.
cat <signed-cert> <intermediate-cert> &tg; combined-cert
That is all that is needed, and go
will do the rest.
(In my case, the signed certificate did not have a newline terminator
so I had to add one using an editor - an advantage of PEM format!).
Copyright © Jan Newmarch, jan@newmarch.name
" Network Programming using Java, Go, Python, Rust, JavaScript and Julia"
by
Jan Newmarch
is licensed under a
Creative Commons Attribution-ShareAlike 4.0 International License
.
Based on a work at
https://jan.newmarch.name/NetworkProgramming/
.