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

3 回复

你好,

我也遇到了同样的问题,并且刚刚提交了一个提案来修改这一点: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严格的证书验证或自定义验证逻辑来解决基本约束解析问题。由于这是内部网络上的自签名证书,跳过验证通常是可接受的解决方案。

回到顶部