最新版本的x/crypto/acme/autocert在编写时为:
https://pkg.go.dev/golang.org/x/crypto@v0.0.0-20211215153901-e495a2d5b3d3/acme/autocert
你做了什么?
调用Manager.GetCertificate以强制立即检索丢失的证书(在应用程序启动期间)。
你会看到什么?
我期望autocert获取一个ECDSA证书。
你看到的是什么?
实际上,我第一次连接到我的服务(使用标准的工具,如curl和openssl s_client)时,获取了一个ECDSA证书。因为这些工具通常更倾向于使用ECDSA而不是RSA。
分析
我的初始tls.ClientHelloInfo只设置了ServerName,没有设置ciphersuites、版本等。这(正确地)在以下位置失败了supportsECDSA检查: https://cs.opensource.google/go/x/crypto/+/e495a2d5:acme/autocert/autocert.go;drc=e495a2d5b3d3be43468d0ebb413f46eeaedf7eb3;l=322
这是公平的,所以我尝试了一个仅支持ECDSA的hello
:
tryGetCertificate := func(name string) {
hello := &tls.ClientHelloInfo{
ServerName: name,
CipherSuites: []uint16{tls.TLS_AES_128_GCM_SHA256},
SupportedCurves: []tls.CurveID{tls.CurveP256},
SignatureSchemes: []tls.SignatureScheme{tls.ECDSAWithP256AndSHA256},
SupportedVersions: []uint16{tls.VersionTLS13},
}
m.GetCertificate(hello)
}
然而,这仍然导致获取到了一个RSA证书:supportsECDSA没有考虑到TLS1.3配置,需要一个支持TLS <= 1.2的ECDSA签名方案。参见: https://cs.opensource.google/go/x/crypto/+/e495a2d5:acme/autocert/autocert.go;l=353;drc=e495a2d5b3d3be43468d0ebb413f46eeaedf7eb3
背景信息:在TLS1.3中,签名方案已经被从CipherSuite定义中提取/分离出来。
我认为supportsECDSA应该更新以识别TLS1.3,并在SignatureSchemes中检查ECDSA。
此外,我认为supportsECDSA应该修改为同时检查ClientHelloInfo中的RSA支持:如果客户端无法使用它,那么获取RSA证书是没有意义的。我知道RSA曾经是默认的签名方案。但未来可能不再是这样,客户端已经可以自由选择他们希望使用的签名方案。
我可以通过简单地将tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256包含在CipherSuites切片中来解决我当前的问题(强制获取一个ECDSA证书)。然后它通过了supportsECDSA检查。但是实际的TLS1.3-only连接仍然会获取一个RSA证书,而不是一个ECDSA证书,即使客户端不支持RSA。
如果这听起来合理,我可以写一个补丁。
另一个想法:我们可以将Manager.GetCertificate更改为在不存在ECDSA证书的情况下接受现有的RSA证书。我们必须小心,支持RSA使用的密码套件不会比ECDSA密码套件弱。这就是为什么我不确定这是否值得。但是,如果我们将签名方案兼容性扩展到检查RSA,也许它是值得的。
5条答案
按热度按时间efzxgjgh1#
@FiloSottile,你能看一下这个吗?
8ehkhllq2#
/cc @golang/security
46scxncf3#
我对此进行了进一步的研究。我尝试验证我确实会因为仅使用ECDSA而导致TLS1.3连接出错。我想配置一个仅使用ECDSA作为签名方案的tls.Config。看起来tls.Config似乎不允许设置签名方案。我不知道这是否是由于TLS1.3规范,还是Go实现的原因。我确实注意到了关于CipherSuites的注解:“请注意,TLS 1.3的ciphersuites不可配置。”我知道现代加密的目标是减少选项/协商。也许对于TLS1.3,始终需要支持RSA。如果是这样的话,那么这个问题的标题声明是不正确的(我是从我看到的autocert推断出来的)。至少很明显,我对TLS1.3的知识是不足的。
但是还有更多。我编写了这个程序来连接到我的autocert服务器。为了查看tls.ClientHelloInfo是什么样子,以及连接是否成功:
它导致了这个tls.ClientHelloInfo:
我很惊讶TLS <=1.2的密码套件会出现在仅用于TLS1.3的连接列表中。我在go1.17.6和go1.18beta1上进行了测试,结果相同。我以为可能是crypto/tls服务器插入了它们。
为了验证这一点,我尝试使用不同的生态系统:
curl --tlsv1.3 https://host.example
。那次没有出现TLS<=1.2的密码套件。所以看起来crypto/tls客户端在仅用于TLS1.3的连接中插入了TLS<=1.2的密码套件。这导致了autocert中的supportsECDSA检查成功。curl发出的请求导致supportsECDSA返回false,因此(成功的)连接使用了RSA,而不是ECDSA。
在上面的ClientHelloInfo中,签名方案是不同的。所以我得出结论,TLS1.3中的签名方案是可以配置的。但是RSA支持可能始终是必需的,“仅使用ECDSA的TLS1.3连接”可能并不存在。
参考资料:这里使用的是我用于测试的acmecert服务器:https://github.com/letsencrypt/pebble ;以及go.mod:
7y4bm7vi4#
ECDSA TLS1.3连接确实失败了:
从acmecert记录的日志如下(根据当前的支持检测,在"supportsECDSA"中添加了一条额外的日志行):
w8f9ii695#
支持ECDSA确实是一个非常有限和部分的检查。假设RSA始终是安全的后备,这并不一定是正确的。我在Go 1.14中添加了ClientHelloInfo.SupportsCertificate来解决这个问题。它已经在标准库中存在足够长的时间了,我们应该将其切换到autocert并删除supportsECDSA。