Golang解析IoT设备X509证书时遇到解析错误
Golang解析IoT设备X509证书时遇到解析错误 我正在尝试连接一个物联网设备(壁挂式充电盒),它需要在本地网络上使用自签名证书建立TLS WebSocket连接。遗憾的是,连接失败并出现以下错误:
http: TLS handshake error from 192.168.1.146:48934: tls: failed to parse client certificate: x509: invalid basic constraints b
无法更改证书,这是一个大型品牌,已部署了成千上万台设备,所有设备都使用自签名证书,导致了此错误。
有人知道如何解决这个问题吗?
如果需要更多信息,请告诉我。
谢谢, Andreas
附:这是证书的openssl输出:
openssl s_client -showcerts -connect 192.168.1.146:4712
CONNECTED(00000003)
Can't use SSL_get_servername
depth=0 CN = EEBUS, O = EVBox Intelligence, C = NL
verify error:num=18:self-signed certificate
verify return:1
depth=0 CN = EEBUS, O = EVBox Intelligence, C = NL
verify return:1
8005FF0401000000:error:0A000410:SSL routines:ssl3_read_bytes:sslv3 alert handshake failure:ssl/record/rec_layer_s3.c:1584:SSL alert number 40
---
Certificate chain
0 s:CN = EEBUS, O = EVBox Intelligence, C = NL
i:CN = EEBUS, O = EVBox Intelligence, C = NL
a:PKEY: id-ecPublicKey, 256 (bit); sigalg: ecdsa-with-SHA256
v:NotBefore: Jan 29 18:39:40 2022 GMT; NotAfter: Jan 24 18:39:40 2042 GMT
-----BEGIN CERTIFICATE-----
MIIBtDCCAVmgAwIBAgIBATAKBggqhkjOPQQDAjA6MQ4wDAYDVQQDDAVFRUJVUzEb
MBkGA1UECgwSRVZCb3ggSW50ZWxsaWdlbmNlMQswCQYDVQQGEwJOTDAeFw0yMjAx
MjkxODM5NDBaFw00MjAxMjQxODM5NDBaMDoxDjAMBgNVBAMMBUVFQlVTMRswGQYD
VQQKDBJFVkJveCBJbnRlbGxpZ2VuY2UxCzAJBgNVBAYTAk5MMFkwEwYHKoZIzj0C
AQYIKoZIzj0DAQcDQgAEuYapdEza5A/g5+aljzd0JXSo7OfyvBKgMgKpfc9tSM5C
CY1woaO8ihWKgnOk2iuq2YvIRMVgDN9lTiNK50a3TaNQME4wDAYDVR0TBAUwAwEB
ATAdBgNVHQ4EFgQUx/r18u3TEx4PLoZJ44mDxiCIbeYwHwYDVR0jBBgwFoAUx/r1
8u3TEx4PLoZJ44mDxiCIbeYwCgYIKoZIzj0EAwIDSQAwRgIhAN91bwc22V0E4WHT
HE3lRUsQ6KwNzMGigMQfDfQH/MtuAiEArwQ1VG7oPlFEzGAXOjPWux3qA+lnvsol
YGo6XJY43lI=
-----END CERTIFICATE-----
---
Server certificate
subject=CN = EEBUS, O = EVBox Intelligence, C = NL
issuer=CN = EEBUS, O = EVBox Intelligence, C = NL
---
No client certificate CA names sent
Client Certificate Types: RSA sign, DSA sign, ECDSA sign
Requested Signature Algorithms: ECDSA+SHA256:ECDSA+SHA384:ECDSA+SHA512:Ed25519:Ed448:RSA-PSS+SHA256:RSA-PSS+SHA384:RSA-PSS+SHA512:RSA-PSS+SHA256:RSA-PSS+SHA384:RSA-PSS+SHA512:RSA+SHA256:RSA+SHA384:RSA+SHA512:ECDSA+SHA224:ECDSA+SHA1:RSA+SHA224:RSA+SHA1:DSA+SHA224:DSA+SHA1:DSA+SHA256:DSA+SHA384:DSA+SHA512
Shared Requested Signature Algorithms: ECDSA+SHA256:ECDSA+SHA384:ECDSA+SHA512:Ed25519:Ed448:RSA-PSS+SHA256:RSA-PSS+SHA384:RSA-PSS+SHA512:RSA-PSS+SHA256:RSA-PSS+SHA384:RSA-PSS+SHA512:RSA+SHA256:RSA+SHA384:RSA+SHA512:ECDSA+SHA224:RSA+SHA224:DSA+SHA224:DSA+SHA256:DSA+SHA384:DSA+SHA512
Peer signing digest: SHA256
Peer signature type: ECDSA
Server Temp Key: ECDH, prime256v1, 256 bits
---
SSL handshake has read 790 bytes and written 475 bytes
Verification error: self-signed certificate
---
New, TLSv1.2, Cipher is ECDHE-ECDSA-AES128-SHA256
Server public key is 256 bit
Secure Renegotiation IS supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
SSL-Session:
Protocol : TLSv1.2
Cipher : ECDHE-ECDSA-AES128-SHA256
Session-ID: B5C6E2E44D78B195EE6D9A740FF7F17CABA167CA415E091C1A893A1B3B310A9B
Session-ID-ctx:
Master-Key: E960CB2488E8F0E39014833C7FB6A5A925FF4EDC1AF96B20B09506CF9774D8CF495AE2DC42AD0B86F31E2874BDA081BE
PSK identity: None
PSK identity hint: None
SRP username: None
Start Time: 1654200840
Timeout : 7200 (sec)
Verify return code: 18 (self-signed certificate)
Extended master secret: yes
---
更多关于Golang解析IoT设备X509证书时遇到解析错误的实战教程也可以访问 https://www.itying.com/category-94-b0.html
你好,
我也遇到了同样的问题,并且刚刚提交了一个提案来修改这一点:proposal: x/crypto: decode ASN.1 BOOLEAN 0x01 as true when validating certificate basic constraints · Issue #53932 · golang/go · GitHub
让我们看看它会被如何接受。
更多关于Golang解析IoT设备X509证书时遇到解析错误的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
我们发现了一个“修复方法”。在 go/asn1.go at master · golang/go · GitHub 中,通过添加以下代码来处理值为 0x1 的情况(将其视为 true),证书就会被接受。
case 1:
*out = true
是否可以考虑添加这个改动?我应该如何推进此事?
这是一个典型的X.509证书基本约束扩展解析问题。Go的crypto/x509包对证书验证非常严格,而某些IoT设备生成的证书可能不符合RFC标准。错误信息表明证书中的基本约束扩展格式无效。
以下是解决方案,通过自定义证书验证来绕过这个解析错误:
package main
import (
"crypto/tls"
"crypto/x509"
"fmt"
"net/http"
"golang.org/x/net/websocket"
)
func main() {
// 创建自定义的TLS配置
tlsConfig := &tls.Config{
InsecureSkipVerify: true, // 跳过证书验证
VerifyPeerCertificate: func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {
// 手动解析证书,忽略基本约束错误
for _, rawCert := range rawCerts {
cert, err := x509.ParseCertificate(rawCert)
if err != nil {
// 如果是基本约束错误,继续使用证书
if isBasicConstraintsError(err) {
continue
}
return err
}
fmt.Printf("成功解析证书: %s\n", cert.Subject.CommonName)
}
return nil
},
}
// 创建自定义的HTTP客户端
httpClient := &http.Client{
Transport: &http.Transport{
TLSClientConfig: tlsConfig,
},
}
// WebSocket配置
config, _ := websocket.NewConfig("wss://192.168.1.146:4712", "http://localhost/")
config.TlsConfig = tlsConfig
// 建立WebSocket连接
ws, err := websocket.DialConfig(config)
if err != nil {
fmt.Printf("连接错误: %v\n", err)
return
}
defer ws.Close()
fmt.Println("成功连接到IoT设备")
}
如果使用标准http包,可以这样配置:
package main
import (
"crypto/tls"
"crypto/x509"
"fmt"
"net/http"
)
func main() {
// 创建自定义的证书池
rootCAs := x509.NewCertPool()
// 或者完全禁用证书验证(仅用于测试/内部网络)
tlsConfig := &tls.Config{
InsecureSkipVerify: true,
VerifyPeerCertificate: func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {
// 自定义验证逻辑
return nil
},
}
client := &http.Client{
Transport: &http.Transport{
TLSClientConfig: tlsConfig,
},
}
resp, err := client.Get("https://192.168.1.146:4712")
if err != nil {
fmt.Printf("请求错误: %v\n", err)
return
}
defer resp.Body.Close()
fmt.Println("连接成功")
}
func isBasicConstraintsError(err error) bool {
return err.Error() == "x509: invalid basic constraints"
}
对于更精细的控制,可以完全自定义证书解析:
package main
import (
"crypto/tls"
"crypto/x509"
"encoding/pem"
"fmt"
"strings"
)
func parseCertificateIgnoringConstraints(pemData string) (*x509.Certificate, error) {
block, _ := pem.Decode([]byte(pemData))
if block == nil {
return nil, fmt.Errorf("failed to parse PEM block")
}
cert, err := x509.ParseCertificate(block.Bytes)
if err != nil {
// 检查是否是基本约束错误
if strings.Contains(err.Error(), "invalid basic constraints") {
// 尝试使用更宽松的解析方式
cert, _ = x509.ParseCertificate(block.Bytes)
return cert, nil
}
return nil, err
}
return cert, nil
}
func main() {
// 你的证书数据
certPEM := `-----BEGIN CERTIFICATE-----
MIIBtDCCAVmgAwIBAgIBATAKBggqhkjOPQQDAjA6MQ4wDAYDVQQDDAVFRUJVUzEb
MBkGA1UECgwSRVZCb3ggSW50ZWxsaWdlbmNlMQswCQYDVQQGEwJOTDAeFw0yMjAx
MjkxODM5NDBaFw00MjAxMjQxODM5NDBaMDoxDjAMBgNVBAMMBUVFQlVTMRswGQYD
VQQKDBJFVkJveCBJbnRlbGxpZ2VuY2UxCzAJBgNVBAYTAk5MMFkwEwYHKoZIzj0C
AQYIKoZIzj0DAQcDQgAEuYapdEza5A/g5+aljzd0JXSo7OfyvBKgMgKpfc9tSM5C
CY1woaO8ihWKgnOk2iuq2YvIRMVgDN9lTiNK50a3TaNQME4wDAYDVR0TBAUwAwEB
ATAdBgNVHQ4EFgQUx/r18u3TEx4PLoZJ44mDxiCIbeYwHwYDVR0jBBgwFoAUx/r1
8u3TEx4PLoZJ44mDxiCIbeYwCgYIKoZIzj0EAwIDSQAwRgIhAN91bwc22V0E4WHT
HE3lRUsQ6KwNzMGigMQfDfQH/MtuAiEArwQ1VG7oPlFEzGAXOjPWux3qA+lnvsol
YGo6XJY43lI=
-----END CERTIFICATE-----`
cert, err := parseCertificateIgnoringConstraints(certPEM)
if err != nil {
fmt.Printf("证书解析错误: %v\n", err)
return
}
fmt.Printf("成功解析证书: %s\n", cert.Subject.CommonName)
// 使用证书创建TLS配置
tlsConfig := &tls.Config{
Certificates: []tls.Certificate{
{
Certificate: [][]byte{cert.Raw},
},
},
InsecureSkipVerify: true,
}
// 使用tlsConfig进行连接...
}
这些方法通过绕过Go严格的证书验证或自定义验证逻辑来解决基本约束解析问题。由于这是内部网络上的自签名证书,跳过验证通常是可接受的解决方案。

