由于 BoringSSL 证书验证失败,iOS 上的安全 websocket 连接失败

Posted

技术标签:

【中文标题】由于 BoringSSL 证书验证失败,iOS 上的安全 websocket 连接失败【英文标题】:Secure websocket connection fails on iOS due to BoringSSL certificate verification failure 【发布时间】:2020-04-29 23:45:34 【问题描述】:

由于 ios(12 和 13)上出现以下错误,我无法连接到安全的 websocket 连接。

似乎验证端点证书失败,但我找不到确定原因的方法。我可以从浏览器客户端连接到 websocket 端点。我还使用https://www.ssllabs.com/ssltest/ 验证了证书,没有发现任何问题。 SSL 证书由 Let's Encrypt 颁发。

以下是我可以从我的 mac 笔记本电脑上的设备和控制台应用程序捕获的控制台输出:

XCODE 调试控制台

2020-04-29 16:18:03.501170-0700 [BoringSSL] boringssl_context_handle_fatal_alert(1873) [C12.1:1][0x1151122e0] write alert, level: fatal, description: certificate unknown 
2020-04-29 16:18:03.501366-0700 [BoringSSL] boringssl_context_error_print(1863) boringssl ctx 0x282eb41b0: 4450062232:error:1000007d:SSL routines:OPENSSL_internal:CERTIFICATE_VERIFY_FAILED:/AppleInternal/BuildRoot/Library/Caches/com.apple.xbs/Sources/boringssl/boringssl-283.102.1/ssl/handshake.cc:369: 
2020-04-29 16:18:03.510648-0700 [BoringSSL] boringssl_session_handshake_incomplete(164) [C12.1:1][0x1151122e0] SSL library error 
2020-04-29 16:18:03.510740-0700 [BoringSSL] boringssl_session_handshake_error_print(111) [C12.1:1][0x1151122e0] 4450062232:error:1000007d:SSL routines:OPENSSL_internal:CERTIFICATE_VERIFY_FAILED:/AppleInternal/BuildRoot/Library/Caches/com.apple.xbs/Sources/boringssl/boringssl-283.102.1/ssl/handshake.cc:369: 
2020-04-29 16:18:03.510837-0700 [BoringSSL] nw_protocol_boringssl_handshake_negotiate_proceed(726) [C12.1:1][0x1151122e0] handshake failed at state 12288 
2020-04-29 16:18:03.532541-0700 [strings] ERROR: Network.NWError not found in table Error of bundle CFBundle 0x109308b40 </System/Library/Frameworks/CoreFoundation.framework> (framework, loaded) 
???? error: Optional("The operation couldn’t be completed. (NETWORK.NWERROR error 2.)")

MAC 控制台应用程序

default 16:18:03.712414-0700    boringssl_session_handshake_incomplete(170) [C12.1:1][0x1151122e0] Handshake incomplete: waiting for data to read [2]
default 16:18:03.712477-0700    boringssl_session_handshake_incomplete(170) [C12.1:1][0x1151122e0] Handshake incomplete: waiting for data to read [2]
default 16:18:03.712583-0700    boringssl_session_handshake_incomplete(170) [C12.1:1][0x1151122e0] Handshake incomplete: waiting for data to read [2]
default 16:18:03.712646-0700    boringssl_session_handshake_incomplete(170) [C12.1:1][0x1151122e0] Handshake incomplete: waiting for data to read [2]
default 16:18:03.712707-0700    boringssl_context_message_handler(2258) [C12.1:1][0x1151122e0] Reading SSL3_RT_HANDSHAKE 122 bytes
default 16:18:03.713628-0700    boringssl_context_info_handler(1983) [C12.1:1][0x1151122e0] Client handshake state: TLS 1.3 client read_hello_retry_request
default 16:18:03.713687-0700    boringssl_context_add_handshake_message_pending(578) [C12.1:1][0x1151122e0] Adding message(2)
default 16:18:03.713747-0700    boringssl_context_message_handler(2258) [C12.1:1][0x1151122e0] Writing SSL3_RT_CHANGE_CIPHER_SPEC 1 bytes
default 16:18:03.713807-0700    boringssl_context_info_handler(1983) [C12.1:1][0x1151122e0] Client handshake state: TLS 1.3 client read_server_hello
default 16:18:03.713865-0700    boringssl_context_info_handler(1983) [C12.1:1][0x1151122e0] Client handshake state: TLS 1.3 client read_encrypted_extensions
default 16:18:03.714153-0700    boringssl_context_message_handler(2258) [C12.1:1][0x1151122e0] Reading SSL3_RT_HANDSHAKE 10 bytes
default 16:18:03.714219-0700    boringssl_context_info_handler(1983) [C12.1:1][0x1151122e0] Client handshake state: TLS 1.3 client read_certificate_request
default 16:18:03.714277-0700    boringssl_context_message_handler(2258) [C12.1:1][0x1151122e0] Reading SSL3_RT_HANDSHAKE 3105 bytes
default 16:18:03.714335-0700    boringssl_context_info_handler(1983) [C12.1:1][0x1151122e0] Client handshake state: TLS 1.3 client read_server_certificate
default 16:18:03.714395-0700    boringssl_context_info_handler(1983) [C12.1:1][0x1151122e0] Client handshake state: TLS 1.3 client read_server_certificate_verify
default 16:18:03.714452-0700    boringssl_context_message_handler(2258) [C12.1:1][0x1151122e0] Reading SSL3_RT_HANDSHAKE 264 bytes
default 16:18:03.714628-0700    boringssl_context_copy_peer_sct_list(1003) [C12.1:1][0x1151122e0] SSL_get0_signed_cert_timestamp_list returned no SCT extension data
default 16:18:03.714914-0700    boringssl_helper_create_sec_trust_with_certificates(607) [C12.1:1][0x1151122e0] SecTrustCreateWithCertificates result: 0
default 16:18:03.714973-0700    boringssl_helper_create_sec_trust_with_certificates(612) [C12.1:1][0x1151122e0] SecTrustSetOCSPResponse result: 0
default 16:18:03.715033-0700    boringssl_helper_create_sec_trust_with_certificates(621) [C12.1:1][0x1151122e0] No TLS-provided SCTs
default 16:18:03.715167-0700    boringssl_context_certificate_verify_callback(2071) [C12.1:1][0x1151122e0] Asyncing for verify block
default 16:18:03.715225-0700    boringssl_session_handshake_incomplete(170) [C12.1:1][0x1151122e0] Handshake incomplete: certificate evaluation result pending [16]
default 16:18:03.715418-0700    boringssl_context_certificate_verify_callback(2040) [C12.1:1][0x1151122e0] Verification already in progress.
default 16:18:03.715481-0700    boringssl_session_handshake_incomplete(170) [C12.1:1][0x1151122e0] Handshake incomplete: certificate evaluation result pending [16]
default 16:18:03.716084-0700    boringssl_context_certificate_verify_callback(2040) [C12.1:1][0x1151122e0] Verification already in progress.
default 16:18:03.716145-0700    boringssl_session_handshake_incomplete(170) [C12.1:1][0x1151122e0] Handshake incomplete: certificate evaluation result pending [16]
default 16:19:16.180121-0700    boringssl_context_message_handler(2258) [C6.1:2][0x11321ccb0] Writing SSL3_RT_ALERT 2 bytes
default 16:19:16.180202-0700    boringssl_context_handle_warning_alert(1893) [C6.1:2][0x11321ccb0] write alert, level: warning, description: close notify
default 16:19:16.180285-0700    boringssl_session_disconnect(504) [C6.1:2][0x11321ccb0] SSL_shutdown 0
default 16:19:16.181104-0700    nw_protocol_boringssl_remove_input_handler(1012) [C6.1:2][0x11321ccb0] nw_protocol_boringssl_remove_input_handler forced true
default 16:19:16.181169-0700    nw_protocol_boringssl_remove_input_handler(1030) [C6.1:2][0x11321ccb0] Transferring nw_protocol_boringssl_t handle back into ARC for autorelease
default 16:19:33.510159-0700    boringssl_context_message_handler(2258) [C8.1:2][0x11328fd50] Writing SSL3_RT_ALERT 2 bytes
default 16:19:33.510247-0700    boringssl_context_handle_warning_alert(1893) [C8.1:2][0x11328fd50] write alert, level: warning, description: close notify
default 16:19:33.510309-0700    boringssl_session_disconnect(504) [C8.1:2][0x11328fd50] SSL_shutdown 0
default 16:19:33.510922-0700    nw_protocol_boringssl_remove_input_handler(1012) [C8.1:2][0x11328fd50] nw_protocol_boringssl_remove_input_handler forced true
default 16:19:33.511105-0700    nw_protocol_boringssl_remove_input_handler(1030) [C8.1:2][0x11328fd50] Transferring nw_protocol_boringssl_t handle back into ARC for autorelease

端点的 nginx 服务器块是:

server 
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    server_name me.example.com;

    ssl_certificate /etc/letsencrypt/live/me.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/me.example.com/privkey.pem;
    ssl_trusted_certificate /etc/letsencrypt/live/me.example.com/chain.pem;

    location /ws 
        proxy_pass http://upstreamserver;

        proxy_http_version  1.1;
        proxy_set_header    Upgrade $http_upgrade;
        proxy_set_header    Connection $connection_upgrade;
        proxy_set_header    Host $host;
        proxy_set_header    X-Real-IP $remote_addr;
        proxy_set_header    X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header    X-Forwarded-Protocol $scheme;
    

对于为什么这在 iOS 上是一个问题有什么想法吗?我也在Apple's Dev Forums

【问题讨论】:

【参考方案1】:

我刚刚编写了一个类来帮助处理套接字和新的苹果 TLS 限制(请参阅我的自述文件)。您很可能错误地配置了您的证书。除了处理套接字的帮助程序类之外,我还尝试布局如何创建将在新的 iOS TLS 限制下工作的证书。

https://github.com/eamonwhiter73/IOSObjCWebSockets

【讨论】:

【参考方案2】:

问题是 LetsEncrypt 颁发的 SSL 证书和 NGINX 设置中的错误配置。由于Apple's Certificate Transparency Policy,iOS 上现在有更严格的证书验证。在 Apple 开发者论坛上获得了 good answer,这有助于将问题追溯到以下问题:

    LetsEncrypt 颁发的证书缺少嵌入式签名证书时间戳 (SCT)。我必须使用“OCSP Must Staple”支持重新创建我的证书:

    certbot --nginx --hsts --staple-ocsp --must-staple -d domain.example.com
    

    要验证为证书创建了嵌入式 SCT,您可以运行 openssl x509 -in cert.pem -text,输出应包含类似于以下内容的内容:

    Certificate:  
        Data:  
            X509v3 extensions:  
                Authority Information Access:  
                    OCSP - URI:http://ocsp.int-x3.letsencrypt.org  
                    CA Issuers - URI:http://cert.int-x3.letsencrypt.org/  
    
            TLS Feature:  
                status_request  
            X509v3 Certificate Policies:  
                Policy: 2.23.140.1.2.1  
                Policy: 1.3.6.1.4.1.44947.1.1.1  
                  CPS: http://cps.letsencrypt.org  
    
            CT Precertificate SCTs:  
                Signed Certificate Timestamp:  
                    Version   : v1 (0x0)  
                    Log ID    : 5E:A7:73:F9:DF:56:C0:E7:B5:36:48:7D:D0:49:E0:32:  
                                7A:91:9A:0C:84:A1:12:12:84:18:75:96:81:71:45:58  
                    Timestamp : May  1 21:02:14.817 2020 GMT  
                    Extensions: none  
                    Signature : ecdsa-with-SHA256  
                                30:45:02:20:2F:2C:22:85:50:DD:FD:DA:62:E9:60:BA:  
                                95:6C:49:03:1E:9E:F9:6C:9F:AA:A0:17:65:7F:D7:D3:  
                                A4:E7:CC:02:02:21:00:D4:2F:55:CF:F6:57:AC:BF:3E:  
                                E5:8B:F5:A2:00:47:2D:C4:5E:A4:10:EE:D7:D6:B4:FF:  
                                9E:21:1D:CC:6A:89:53  
                Signed Certificate Timestamp:  
                    Version   : v1 (0x0)  
                    Log ID    : 07:B7:5C:1B:E5:7D:68:FF:F1:B0:C6:1D:23:15:C7:BA:  
                                E6:57:7C:57:94:B7:6A:EE:BC:61:3A:1A:69:D3:A2:1C  
                    Timestamp : May  1 21:02:14.843 2020 GMT  
                    Extensions: none  
                    Signature : ecdsa-with-SHA256  
                                30:44:02:20:17:63:1D:8E:76:CA:E0:A2:5C:42:92:7C:  
                                BC:06:60:C7:9B:46:BB:59:63:8F:E1:8A:BE:52:CB:15:  
                                FD:C4:DE:09:02:20:28:EF:48:E1:4B:BD:9D:05:29:52:  
                                FC:D9:5A:8B:82:08:9D:1A:A0:58:F0:33:FB:05:5E:E7:  
                                56:A0:AE:64:84:C7  
    

    NGINX 服务器块需要正确的 OCSP 配置,删除 HTTP/2 支持,并使用具有中间证书和嵌入式 SCT 的证书链

     map $http_upgrade $connection_upgrade   
         default upgrade;  
         '' close;  
       
    
     upstream ws-signal   
         server localhost:8080;  
       
    
    server   
        listen 443 ssl;  
        listen [::]:443 ssl;  
        server_name domain.example.com;  
    
        ssl_certificate /etc/letsencrypt/live/domain.example.com/fullchain.pem; # managed by Certbot  
        ssl_certificate_key /etc/letsencrypt/live/domain.example.com/privkey.pem; # managed by Certbot  
    
        # OCSP Stapling  
        ssl_stapling on;  
        ssl_stapling_verify on;  
        ssl_trusted_certificate /etc/letsencrypt/live/domain.example.com/fullchain.pem;  
        resolver 8.8.8.8 8.8.4.4;  
    
        location /ws   
            proxy_pass http://ws-signal;  
    
            proxy_http_version      1.1;  
            proxy_set_header        Upgrade $http_upgrade;  
            proxy_set_header        Connection $connection_upgrade;  
            proxy_set_header        Host $host;  
          
      
    

我还必须将Starscream 升级到 v4.0.3。这个最新版本使用了 iOS 13+ 的 URLSessionWebSocketTask,如果禁用了证书固定,iOS 12 似乎会覆盖验证以返回 true。仍然需要尝试启用证书固定以查看会发生什么。

【讨论】:

以上是关于由于 BoringSSL 证书验证失败,iOS 上的安全 websocket 连接失败的主要内容,如果未能解决你的问题,请参考以下文章

避免在 iOS 上使用 Firebase / BoringSSL 控制台日志

iOS WKWebView 自签名证书单向验证+双向验证

证书验证失败怎么回事?

由于服务器证书验证失败,无法使用 Xcode 6.0.1 创建机器人

VMware vSphere CLient 5.0 登录时提示“出现未知错误。(由于客户端验证服务器的SSL证书,请求失败。)

iOS:由于签名无效,无法在 iTunes Connect 上发布