I connect to some server via openssl:
openssl s_client -crlf -connect somehost.com:700 -cert key.pem
And it works. Connection is successful.
But when I tried to do same from Go code (example from documentation), it doesn't work for me:
import (
"crypto/tls"
"crypto/x509"
)
func main() {
// Connecting with a custom root-certificate set.
const rootPEM = `
-----BEGIN CERTIFICATE-----
my key text
-----END CERTIFICATE-----`
// First, create the set of root certificates. For this example we only
// have one. It's also possible to omit this in order to use the
// default root set of the current operating system.
roots := x509.NewCertPool()
ok := roots.AppendCertsFromPEM([]byte(rootPEM))
if !ok {
panic("failed to parse root certificate")
}
conn, err := tls.Dial("tcp", "somehost.com:700", &tls.Config{
RootCAs: roots,
})
if err != nil {
panic("failed to connect: " + err.Error())
}
conn.Close()
}
My text error is:
panic: failed to connect: x509: certificate is valid for otherhost.com, not somehost.com [recovered]
Question: what did I do wrong? And maybe I didn't add some tls.Config parameters?
openssl s_client is just a test tool which connects but it does not care much if the certificate is valid for the connection. Go instead cares if the certificate could be validated, so you get the information that the certificate is invalid because the name does not match.
what did I do wrong?
Based on the error message you did access the host by the wrong hostname. Or you've configured your server badly so that it sends the wrong certificate.
I didn't need to check ssl certificate of server. It was demo server of some domain registry. So I need server to check my certificate.
const certPEM = `-----BEGIN CERTIFICATE-----
-----END CERTIFICATE-----
`
const certKey = `-----BEGIN RSA PRIVATE KEY-----
-----END RSA PRIVATE KEY-----`
cert, err := tls.X509KeyPair([]byte(certPEM), []byte(certKey))
if err != nil {
t.Error("server: loadkeys: %s", err)
}
cfg := tls.Config{
InsecureSkipVerify: true,
ServerName: "somehost.com",
Certificates: []tls.Certificate{cert},
}
conn, err := tls.Dial("tcp", "somehost.com:700", &cfg)
if err != nil {
t.Error("failed to connect: " + err.Error())
}
defer conn.Close()
So this code works in my case.
Related
I bought an SSL certificate from Godaddy for a web site.
I added the files in the server and run the service and it just returns an error:
failed to find any PEM data in certificate input
I used cat to generate a server.pem file with all the files, even added a godaddy pem intermediate pem file they provide for a G2 Certificate Chain and nothing.
cat generated-private-key.txt > server.pem
cat 678f65b8a7391017.crt >> server.pem
cat gd_bundle-g2-g1.crt >> server.pem
cat gdig2.crt.pem >> server.pem
Using self signed certificate works but off course it's not usable in real world.
Code attempt 1:
log.Fatal(http.ListenAndServeTLS(fmt.Sprintf("%s:%d", configuration.HttpServer.Address, configuration.HttpServer.Port), "server.pem", "generated-private-key.txt", router))
Code attempt 2:
cert, err := tls.LoadX509KeyPair("server.pem","generated-private-key.txt")
if err != nil {
log.Fatalf("server: loadkeys: %s", err)
}
pem, err := ioutil.ReadFile("gd_bundle-g2-g1.crt")
if err != nil {
log.Fatalf("Failed to read client certificate authority: %v", err)
}
certpool := x509.NewCertPool()
if !certpool.AppendCertsFromPEM(pem) {
log.Fatalf("Can't parse client certificate authority")
}
tlsConfig := &tls.Config{
ClientCAs: certpool,
Certificates: []tls.Certificate{cert},
}
srv := &http.Server{
Addr: fmt.Sprintf("%s:%d", configuration.HttpServer.Address, configuration.HttpServer.Port),
Handler: router,
ReadTimeout: time.Duration(5) * time.Second,
WriteTimeout: time.Duration(5) * time.Second,
TLSConfig: tlsConfig,
}
log.Fatal(srv.ListenAndServeTLS("678f65b8a7391017.crt","generated-private-key.txt"))
Both give the same error.
I need to have this up and running as I already have the back-end done but now I just want to enable HTTPS for productive environment.
Finally solved it, I had to combine both generated-private-key.txt and generated-csr.txt that Godaddy provided me into a single "server.key" file.
Incredible what lack of knowledge of certain things end up wasting so much time.
But I guess that's why were here, for the thrill of exploring.
Thank you everyone for the help!
I've struggled with this myself and I think your issue here is that you need to process the keys before presenting for the http.Server, and you'll need to include the RootCA. I've downloaded an SSL from GoDaddy (using the Other option) and grabbed their gd_bundle-g2.crt RootCA from here. Once you've grabbed that, create a function like below (added a gist here):
func genTLS() (*tls.Config, error) {
caCert, err := ioutil.ReadFile("/home/sborza/gd_bundle-g2.crt")
if err != nil {
return nil, fmt.Errorf("read root cert: %s", err.Error())
}
// **** START PRIV KEY PROCESSING ****
clientBytes, err := ioutil.ReadFile("/home/sborza/sborza_dev.key")
if err != nil {
return nil, fmt.Errorf("read client priv key: %s", err.Error())
}
cb, _ := pem.Decode(clientBytes)
k, err := x509.ParsePKCS8PrivateKey(cb.Bytes)
if err != nil {
return nil, fmt.Errorf("parse client privkey: %s", err.Error())
}
clientKey, _ := x509.MarshalPKCS8PrivateKey(k)
clientKeyPEM := pem.EncodeToMemory(&pem.Block{
Type: "PRIVATE KEY",
Bytes: clientKey,
})
// **** END PRIV KEY PROCESSING ****
// **** START CERT PROCESSING ****
certBytes, err := ioutil.ReadFile("/home/sborza/sborza_dev.pem")
if err != nil {
return nil, fmt.Errorf("read client cert: %s", err.Error())
}
cbk, _ := pem.Decode(certBytes)
certs, err := x509.ParseCertificates(cbk.Bytes)
if err != nil {
return nil, fmt.Errorf("parse client cert: %s", err.Error())
}
clientCertPEM := pem.EncodeToMemory(&pem.Block{
Type: "CERTIFICATE",
Bytes: certs[0].Raw,
})
// **** END CERT PROCESSING ****
// **** START TLS CONFIG ****
cert, err := tls.X509KeyPair(clientCertPEM, clientKeyPEM)
if err != nil {
return nil, fmt.Errorf("tls key pair: %s", err.Error())
}
caCertPool := x509.NewCertPool()
if ok := caCertPool.AppendCertsFromPEM(caCert); !ok {
return nil, fmt.Errorf("append cert: %s", err.Error())
}
return &tls.Config{
RootCAs: caCertPool,
Certificates: []tls.Certificate{cert},
}, nil
// **** END TLS CONFIG ****
}
As per one of the other comments, for the first param to ListenAndServeTLS, the GoDaddy cert and chain files have to be concatenated. Here is an example
var srv http.Server
srv.Handler = yourHandler
srv.Addr = ":443"
// Load cert files
chainBytes, err := ioutil.ReadFile("gd_bundle-g2-g1.crt") // GoDaddy chain
if err != nil {
log.Fatal(err)
}
certBytes, err := ioutil.ReadFile("server.crt") // Your server cert
if err != nil {
log.Fatal(err)
}
keyBytes, err := ioutil.ReadFile("server.key") // Your server key
if err != nil {
log.Fatal(err)
}
// Concatenate chain and cert
b := bytes.NewBuffer(certBytes)
b.Write([]byte("\n"))
b.Write(chainBytes)
// Setup TLS
cert, err := tls.X509KeyPair(b.Bytes(), keyBytes)
if err != nil {
log.Fatal(err)
}
cfg := &tls.Config{
Certificates: []tls.Certificate{cert},
}
srv.TLSConfig = cfg
// Start server
err = srv.ListenAndServeTLS("", "")
if err != nil {
log.Fatal(err)
}
My experience was that just using the cert file is fine in latest versions of browsers like Google Chrome, but I would get TLS errors with clients like HTTPie or CURL
certificate verify failed
And various errors on the server
tls: bad record MAC
tls: unknown certificate authority
Concatenating the cert and chain files solved above errors.
The issue was with the key file. This was the same key I used given by GoDaddy without any modification. The beginning of the file had some issue (like UTF-8 BOM at the start of the file or similar) as #SteffenUllrich mentioned. To fix this, I added an empty line just above the key file and it worked.
Finally, the key looks like:
<Empty line>
-----BEGIN RSA PRIVATE KEY-----
wlWPpSnGEdNjRapfW/6+xzjDVAaKC41c5b07OAviFchwqGI+88
aZGwBJnTgkbsLddddddd=
-----END RSA PRIVATE KEY-----
in general I got 3 files from GoDaddy:
main Certificate file
Server Private Key
Bundle file
in configured all these files in my Go server in the following way:
cert, err := tls.LoadX509KeyPair("myalcoholist.pem","myalcoholist.key")
if err != nil {
log.Fatalf("server: loadkeys: %s", err)
}
pem, err := ioutil.ReadFile("cert/sf_bundle-g2-g1.crt")
if err != nil {
log.Fatalf("Failed to read client certificate authority: %v", err)
}
if !certpool.AppendCertsFromPEM(pem) {
log.Fatalf("Can't parse client certificate authority")
}
tlsConfig := &tls.Config{
ClientCAs: certpool,
Certificates: []tls.Certificate{cert},
}
srv := &http.Server{
Addr: "myalcoholist.com:443",
Handler: n,
ReadTimeout: time.Duration(5) * time.Second,
WriteTimeout: time.Duration(5) * time.Second,
TLSConfig: tlsConfig,
}
err := srv.ListenAndServeTLS("cert/myalcoholist.pem","cert/myalcoholist.key")
The web server runs properly, it's currently published at https://myalcoholist.com:443.
I validated my SSL using https://www.ssllabs.com/ssltest/analyze.html?d=myalcoholist.com and it's response is This server's certificate chain is incomplete. Grade capped to B.
you can go to this link to see the all detailed result.
what am I missing?
Following that thread, and from the net/http/#ListenAndServeTLS() doc:
If the certificate is signed by a certificate authority, the certFile should be the concatenation of the server's certificate, any intermediates, and the CA's certificate.
Try and make sure your cert/myalcoholist.pem includes the CA certificates as well.
That thread used:
myTLSConfig := &tls.Config{
CipherSuites: []uint16{
tls.TLS_RSA_WITH_RC4_128_SHA,
tls.TLS_ECDHE_RSA_WITH_RC4_128_SHA,
tls.TLS_RSA_WITH_AES_128_CBC_SHA,
tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA},}
myTLSConfig.PreferServerCipherSuites = true
const myWebServerListenAddress = "0.0.0.0:5555"
myTLSWebServer := &http.Server{Addr: myWebServerListenAddress, TLSConfig: myTLSConfig, Handler: router}
if err = myTLSWebServer.ListenAndServeTLS("/home/loongson/webServerKeysV2/golangCertFile2", "/home/loongson/webServerKeysV2/adequatech.ca-comodoinstantssl-exported-privatekey-rsa-ForApache.key"); err != nil {
panic(err)
}
Compared to my previous answer, adding a cipher suite is a good idea, but again, try and see if the certificate file passed to ListenAndServeTLS works better if it includes the CAs.
Sure enough, https://www.ssllabs.com/ssltest/analyze.html?d=myalcoholist.com reports grade A, with the warning: “Chain issues: Contains anchor”.
See "SSL/TLS: How to fix “Chain issues: Contains anchor”" to remove that warning, but this is not an error though:
RFC 2119: the server is allowed to include the root certificate (aka "trust anchor") in the chain, or omit it. Some servers include it
I want to receive a TCP connection over TLS. I want to validate client certificate and use it to authenticate the client to my application.
Go has the standard crypto/tls package. It can validate client/server certificates. But I can't find way to get details of the remote (client) certificate, like the common name.
Have to call crypto/tls/Conn.Handshake.
Then you can read peer certificate:
tlsconn.ConnectionState().PeerCertificates[0].Subject.CommonName
Following code may help you get your answer
package main
import (
"crypto/tls"
"fmt"
"log"
)
func main() {
conf := &tls.Config{
InsecureSkipVerify: true,
}
conn, err := tls.Dial("tcp", "www.google.com:443", conf)
if err != nil {
log.Println("Error in Dial", err)
return
}
defer conn.Close()
certs := conn.ConnectionState().PeerCertificates
for _, cert := range certs {
fmt.Printf("Issuer Name: %s\n", cert.Issuer)
fmt.Printf("Expiry: %s \n", cert.NotAfter.Format("2006-January-02"))
fmt.Printf("Common Name: %s \n", cert.Issuer.CommonName)
}
}
When working with crypto/tls you can query any Conn object for ConnectionState:
func (c *Conn) ConnectionState() ConnectionState
The ConnectionState struct contains information about the client certificate:
type ConnectionState struct {
PeerCertificates []*x509.Certificate // certificate chain presented by remote peer
}
The x509.Certificate should be pretty straightforward to work with.
Before the server requests for client authentication, you have to configure the connection with the server certificate, client CA (otherwise you will have to verify the trust chain manually, you really don't want that), and tls.RequireAndVerifyClientCert. For example:
// Load my SSL key and certificate
cert, err := tls.LoadX509KeyPair(settings.MyCertificateFile, settings.MyKeyFile)
checkError(err, "LoadX509KeyPair")
// Load the CA certificate for client certificate validation
capool := x509.NewCertPool()
cacert, err := ioutil.ReadFile(settings.CAKeyFile)
checkError(err, "loadCACert")
capool.AppendCertsFromPEM(cacert)
// Prepare server configuration
config := tls.Config{Certificates: []tls.Certificate{cert}, ClientCAs: capool, ClientAuth: tls.RequireAndVerifyClientCert}
config.NextProtos = []string{"http/1.1"}
config.Rand = rand.Reader
There is an easier way to do that:
func renewCert(w http.ResponseWriter, r *http.Request) {
if r.TLS != nil && len(r.TLS.PeerCertificates) > 0 {
cn := strings.ToLower(r.TLS.PeerCertificates[0].Subject.CommonName)
fmt.Println("CN: %s", cn)
}
}
I have the following certificate hierarchy:
Root-->CA-->3 leaf certificates
The entire chain has both serverAuth and clientAuth as extended key usages explicitly defined.
In my go code, I create a tls.Config object like so:
func parseCert(certFile, keyFile string) (cert tls.Certificate, err error) {
certPEMBlock , err := ioutil.ReadFile(certFile)
if err != nil {
return
}
var certDERBlock *pem.Block
for {
certDERBlock, certPEMBlock = pem.Decode(certPEMBlock)
if certDERBlock == nil {
break
}
if certDERBlock.Type == "CERTIFICATE" {
cert.Certificate = append(cert.Certificate, certDERBlock.Bytes)
}
}
// Need to flip the array because openssl gives it to us in the opposite format than golang tls expects.
cpy := make([][]byte, len(cert.Certificate))
copy(cpy, cert.Certificate)
var j = 0
for i := len(cpy)-1; i >=0; i-- {
cert.Certificate[j] = cert.Certificate[i]
j++
}
keyData, err := ioutil.ReadFile(keyFile)
if err != nil {
return
}
block, _ := pem.Decode(keyData)
if err != nil {
return
}
ecdsaKey, err := x509.ParseECPrivateKey(block.Bytes)
if err != nil {
return
}
cert.PrivateKey = ecdsaKey
return
}
// configure and create a tls.Config instance using the provided cert, key, and ca cert files.
func configureTLS(certFile, keyFile, caCertFile string) (tlsConfig *tls.Config, err error) {
c, err := parseCert(certFile, keyFile)
if err != nil {
return
}
ciphers := []uint16 {
tls.TLS_RSA_WITH_AES_256_CBC_SHA,
tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
}
certPool := x509.NewCertPool()
buf, err := ioutil.ReadFile(caCertFile)
if nil != err {
log.Println("failed to load ca cert")
log.Fatal(seelog.Errorf("failed to load ca cert.\n%s", err))
}
if !certPool.AppendCertsFromPEM(buf) {
log.Fatalln("Failed to parse truststore")
}
tlsConfig = &tls.Config {
CipherSuites: ciphers,
ClientAuth: tls.RequireAndVerifyClientCert,
PreferServerCipherSuites: true,
RootCAs: certPool,
ClientCAs: certPool,
Certificates: []tls.Certificate{c},
}
return
}
certFile is the certificate chain file and keyFile is the private key file. caCertFile is the truststore and consists of just the root certificate
So basically, here is what I expect to have inside of my tls.Config object that comes out of this function:
RootCAs: Just the root certificate from caCertFile
ClientCAs: Again, just the root certificate from caCertFile, same as RootCAs
Certificates: A single certificate chain, containing all of the certificates in certFile, ordered to be leaf first.
Now, I have 3 pieces here. A server, a relay, and a client. The client connects directly to the relay, which in turn forwards the request to the server. All three pieces use the same configuration code, of course using different certs/keys. The caCertFile is the same between all 3 pieces.
Now, if I stand up the server and the relay and connect to the relay from my browser, all goes well, so I can assume that the connection between relay and server is fine. The issue comes about when I try to connect my client to the relay. When I do so, the TLS handshake fails and the following error is returned:
x509: certificate signed by unknown authority
On the relay side of things, I get the following error:
http: TLS handshake error from : remote error: bad certificate
I am really at a loss here. I obviously have something setup incorrectly, but I am not sure what. It's really weird that it works from the browser (meaning that the config is correct from relay to server), but it doesn't work with the same config from my client.
Update:
So if I add InsecureSkipVerify: true to my tls.Config object on both the relay and the client, the errors change to:
on the client: remote error: bad certificate
and on the relay: http: TLS handshake error from : tls: client didn't provide a certificate
So it looks like the client is rejecting the certificate on from the server (the relay) due to it being invalid for some reason and thus never sending its certificate to the server (the relay).
I really wish go had better logging. I can't even hook into this process to see what, exactly, is going on.
When you say
Need to flip the array because openssl gives it to us in the opposite format than golang tls expects.
I have used certificates generated by openssl and had no problem opening them with:
tls.LoadX509KeyPair(cert, key)
Anyway, the error message bad certificate is due to the server not managing to match the client-provided certificate against its RootCAs. I have also had this problem in Go using self-signed certificats and the only work-around I've found is to install the caCertFile into the machines system certs, and use x509.SystemCertPool() instead of x509.NewCertPool().
Maybe someone else will have another solution?
Beside what beldin0 suggested.
I have tried another way to do this.
caCertPool := x509.NewCertPool()
caCertPool.AppendCertsFromPEM(crt)
client := &http.Client{
//some config
Transport: &http.Transport{
TLSClientConfig: &tls.Config{
RootCAs: caCertPool,
},
},
}
Here, the variable "crt" is the content in your certificate.
Basically, you just add it into your code(or read as a config file).
Then everything would be fine.
I'm trying to establish a TLS connection with the use of a self signed server certificate.
I generated the certificate with this example code: http://golang.org/src/pkg/crypto/tls/generate_cert.go
My relevant client code looks like that:
// server cert is self signed -> server_cert == ca_cert
CA_Pool := x509.NewCertPool()
severCert, err := ioutil.ReadFile("./cert.pem")
if err != nil {
log.Fatal("Could not load server certificate!")
}
CA_Pool.AppendCertsFromPEM(severCert)
config := tls.Config{RootCAs: CA_Pool}
conn, err := tls.Dial("tcp", "127.0.0.1:8000", &config)
if err != nil {
log.Fatalf("client: dial: %s", err)
}
And the relevant server code like that:
cert, err := tls.LoadX509KeyPair("./cert.pem", "./key.pem")
config := tls.Config{Certificates: []tls.Certificate{cert}}
listener, err := tls.Listen("tcp", "127.0.0.1:8000", &config)
for {
conn, err := listener.Accept()
if err != nil {
log.Printf("server: accept: %s", err)
break
}
log.Printf("server: accepted from %s", conn.RemoteAddr())
go handleConnection(conn)
}
Because the server certificate is self signed is use the same certificate for the server and the clients CA_Pool however this does not seem to work since i always get this error:
client: dial: x509: certificate signed by unknown authority
(possibly because of "x509: invalid signature: parent certificate
cannot sign this kind of certificate" while trying to verify
candidate authority certificate "serial:0")
What's my mistake?
It finally worked with the go built in x509.CreateCertificate,
the problem was that I did not set the IsCA:true flag,
I only set the x509.KeyUsageCertSign which made creating the self signed certificate work, but crashed while verifying the cert chain.
The problem is that you need a CA certificate in the server-side config, and this CA must have signed the server's certificate.
I have written some Go code that will generate a CA certificate, but it hasn't been reviewed by anyone and is mostly a toy for playing around with client certs. The safest bet is probably to use openssl ca to generate and sign the certificate. The basic steps will be:
Generate a CA Certificate
Generate a Server key
Sign the Server key with the CA certificate
Add the CA Certificate to the client's tls.Config RootCAs
Set up the server's tls.Config with the Server key and signed certificate.
Kyle, is correct. This tool will do what you want and it simplifies the entire process:
https://github.com/deckarep/EasyCert/releases (only OSX is supported since it uses the openssl tool internally)
and the source:
https://github.com/deckarep/EasyCert
Basically with this tool it will generate a bundle of files but you will need the three that it outputs when it's done.
a CA root cer file
a Server cer file
a Server key file
In my case, the certificate I appended was not encoded correctly in pem format.
If using keytools, ensure to append -rfc while exporting the certificate from keystore, pem encoded could be opened in a text editor to display:
-----BEGIN CERTIFICATE-----
MIIDiDCCAnCgAwIBAgIEHKSkvDANBgkqhkiG9w0BAQsFADBi...
I saw the same error when using mysql client in Go:
Failed to connect to database: x509: cannot validate certificate for 10.111.202.229 because it doesn't contain any IP SANs
and setting InsecureSkipVerify to true (to skip verification of certificate) resolved it for me:
https://godoc.org/crypto/tls#Config
The following code worked for me:
package main
import (
"fmt"
"github.com/go-sql-driver/mysql"
"github.com/jinzhu/gorm"
"crypto/tls"
"crypto/x509"
"io/ioutil"
"log"
)
func main() {
rootCertPool := x509.NewCertPool()
pem, err := ioutil.ReadFile("/usr/local/share/ca-certificates/ccp-root-ca.crt")
if err != nil {
log.Fatal(err)
}
if ok := rootCertPool.AppendCertsFromPEM(pem); !ok {
log.Fatal("Failed to append root CA cert at /usr/local/share/ca-certificates/ccp-root-ca.crt.")
}
mysql.RegisterTLSConfig("custom", &tls.Config{
RootCAs: rootCertPool,
InsecureSkipVerify: true,
})
db, err := gorm.Open("mysql", "ccp-user:I6qnD6zNDmqdDLXYg3HqVAk2P#tcp(10.111.202.229:3306)/ccp?tls=custom")
defer db.Close()
}
You need to use the InsecureSkipVerify flag, refer to https://groups.google.com/forum/#!topic/golang-nuts/c9zEiH6ixyw.
The related code of this post (incase the page is offline):
smtpbox := "mail.foo.com:25"
c, err := smtp.Dial(smtpbox)
host, _, _ := net.SplitHostPort(smtpbox)
tlc := &tls.Config{
InsecureSkipVerify: true,
ServerName: host,
}
if err = c.StartTLS(tlc); err != nil {
fmt.Printf(err)
os.Exit(1)
}
// carry on with rest of smtp transaction
// c.Auth, c.Mail, c.Rcpt, c.Data, etc