我有以下证书层次结构:
Root - > CA - > 3个叶子证书
整个链都有serverAuth和clientAuth作为显式定义的扩展密钥用法。
在我的go代码中,我创建了一个tls.Config对象,如下所示:
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是证书链文件,keyFile是私钥文件。 caCertFile是信任库,只包含根证书
所以基本上,这是我希望在我的tls.Config对象中出现的函数:
RootCAs:来自caCertFile的根证书 ClientCAs:同样,只是来自caCertFile的根证书,与RootCAs相同 证书:单个证书链,包含certFile中的所有证书,被命令为叶子。
现在,我在这里有3件。服务器,中继和客户端。客户端直接连接到中继,中继又将请求转发给服务器。这三个部分使用相同的配置代码,当然使用不同的证书/密钥。 caCertFile在所有3件之间是相同的。
现在,如果我站起来服务器和继电器并从我的浏览器连接到继电器,一切顺利,所以我可以假设继电器和服务器之间的连接很好。当我尝试将我的客户端连接到中继时出现问题。当我这样做时,TLS握手失败并返回以下错误:
x509:由未知权限签署的证书
在继电器方面,我收到以下错误: http:TLS握手错误来自:远程错误:错误证书
我真的很茫然。我显然有不正确的设置,但我不知道是什么。从浏览器开始工作(意味着从中继到服务器的配置是正确的)真的很奇怪,但它不能与我的客户端使用相同的配置。
因此,如果我将InsecureSkipVerify: true
添加到中继和客户端上的tls.Config对象,则错误将更改为:
:远程错误:错误证书
并在继电器上: http:TLS握手错误来自:tls:客户端没有提供证书
所以看起来客户端拒绝来自服务器(中继)的证书,因为它由于某种原因无效,因此从不将其证书发送到服务器(中继)。
我真的希望有更好的伐木。我甚至无法进入这个过程,看看到底发生了什么。
答案 0 :(得分:0)
当你说
需要翻转数组,因为openssl以与golang tls预期相反的格式将其提供给我们。
我已经使用过openssl生成的证书,并且可以通过以下方式打开它们:
tls.LoadX509KeyPair(cert, key)
无论如何,错误消息错误的证书是由于服务器未设法将客户端提供的证书与其RootCA相匹配。我在Go中使用自签名证书也遇到了这个问题,我发现的唯一解决方法是将 caCertFile 安装到计算机系统证书中,并使用x509.SystemCertPool()
代替x509.NewCertPool()
。
也许其他人会有其他解决方案?
答案 1 :(得分:0)
在beldin0建议的内容旁边。 我尝试了另一种方法。
caCertPool := x509.NewCertPool()
caCertPool.AppendCertsFromPEM(crt)
client := &http.Client{
//some config
Transport: &http.Transport{
TLSClientConfig: &tls.Config{
RootCAs: caCertPool,
},
},
}
此处,变量“ crt”是证书中的内容。
基本上,您只需将其添加到代码中(或作为配置文件读取)。
那一切都会好起来的。