Android:对于套接字,传递“对等方的证书与预期的主机名不匹配”错误的最安全方法?

Posted

技术标签:

【中文标题】Android:对于套接字,传递“对等方的证书与预期的主机名不匹配”错误的最安全方法?【英文标题】:Android: Most secure way to pass "The certificate of the peer does not match the expected hostname" error, for sockets? 【发布时间】:2021-03-29 07:15:02 【问题描述】:

总结:

当我们收到“对端的证书与预期的主机名不匹配”错误时,如何使用通配符证书创建到服务器的套接字?


基本上,我想为带有通配符证书的服务器创建一个安全的 websocket (wss)。我正在使用this websocket library。现在,当我想连接到网站时,连接会立即关闭并显示此错误日志:

2021-03-16 13:14:16.221 21027-21097/com.....app W/System.err: com.neovisionaries.ws.client.HostnameUnverifiedException: 对等方的证书与预期不符主机名 (sthsth.sthsth.ir) 2021-03-16 13:14:16.221 21027-21097/com.....app W/System.err: at com.neovisionaries.ws.client.SocketConnector.verifyHostname(SocketConnector.java:281) 2021-03-16 13:14:16.222 21027-21097/com .....app W/System.err: at com.neovisionaries.ws.client.SocketConnector.doConnect(SocketConnector.java:246) 2021-03-16 13:14:16.222 21027-21097/com .....app W/System.err: at com.neovisionaries.ws.client.SocketConnector.connect(SocketConnector.java:190) 2021-03-16 13:14:16.222 21027-21097/com .....app W/System.err: at com.neovisionaries.ws.client.WebSocket.connect(WebSocket.java:2351) 2021-03-16 13:14:16.223 21027-21097/com .....app W/System.err: at com .....app.network.GeneralSocketService$1.run(GeneralSocketService.java:167)

2021-03-16 13:14:15.895 21027-21097/com.....app E/Conscrypt: ------------------不受信任的链: ---------------------- 2021-03-16 13: 14:15.896 21027-21097/com .....app E/Conscrypt: == Chain0 == 版本:3 2021-03-16 13:14:15.897 21027-21097/com .....app E/Conscrypt:序列号:e63f80cf7a1220146cc8fd96d8468a4 2021-03-16 13:14:15.900 21027-21097/com .....app E/Conscrypt: SubjectDN: CN=*.sthsthnet.ir, C=US 2021-03-16 13:14:15.908 21027-21097/com.....app E/Conscrypt: IssuerDN: CN=Certum Domain 验证 CA SHA2,OU=Certum 证书颁发机构,O=Unizeto 技术 S.A.,C=PL 2021-03-16 13:14:15.914 21027-21097/com.....app E/Conscrypt:不要早于:7 月 30 日星期二 10:35:53 GMT+04:30 2019 2021-03-16 13:14:15.916 21027-21097/com .....app E/Conscrypt:不要迟到:7 月 29 日星期四 10:35:53 GMT+04:30 2021 2021-03-16 13:14:15.918 21027-21097/com .....app E/Conscrypt:Sig ALG 名称:SHA256withRSA 2021-03-16 13:14:15.921 21027-21097/com .....app E/Conscrypt: 签名: 22bc2d40a15e881292c19f5b47ca610cc3efb90e998b7eddf08ab108f56ec3df1b7ea7f2ac25b8df74cc49735f3fe7f509fe87ea42c080424f450f8f1bf04a35e4c97dac79ee2df66cde96f4a7a1fb6b62f5640f0a111a157feb7ef5ceab2588cad1731bbf22434d2ee3e0fcbec8ff74ad194d0c2a5ab113b4d00302e62721052b7d0afacc6cda129c3ff99db165d3836a1e2900dd66f8eb3b05f4aab6f42de2a85a60c5382d8bf229c3b866a662326606444685f62673cfbe5fd6a4dc21aa8b18e36c9e13423917aabf79b817011a757262f7b16629745a44162eb9a58ebab4adeefb4710a439f34844b0489f44d3afe0a1ba2e0f78dd5fc04009b0096e1837 2021-03-16 13:14:16.213 21027-21097/com .....app E/Conscrypt:公开 关键:

 30 82 01 22 30 0d 06 09 2a 86 48 86 f7 0d 01 01 01 05 00 03
 82 01 0f 00 30 82 01 0a 02 82 01 01 00 9d b2 14 d0 11 c4 8f
 29 42 7a ee 09 1e 30 2e 62 dd 14 b8 6b 7b 1b 6b fe 03 ba 6a
 07 6b 6c 69 b3 07 58 51 cf c0 4c 67 15 7e 0f 46 45 33 2b f5
 50 20 db 2c 45 f7 8a 52 29 d2 a0 10 65 31 1d 9f d9 90 2f 83
 e9 d7 c1 ee 1a a6 e8 47 3d 89 fb 8b cf d0 d7 7f ac de d1 39
 ec 4b 8d 43 a4 d5 c2 95 e4 ab 4e 6e 2b a6 b7 24 f7 62 1b 3e
 4a 27 ca a0 d7 9f 22 c3 25 d8 bd 54 39 27 51 99 d5 fa 13 ed
 88 8a 64 9c ce 60 38 ae ea 7e 5f ee ed b1 ff cf 30 56 6d 5c
 01 ad 0c d8 87 f0 4f c7 89 85 e0 d1 08 89 e8 69 dc 6e 35 c0
 7d fc e6 37 33 00 a9 c8 7b 88 9e eb 98 02 bc 6c 9c f4 b7 6b
 87 ca 15 1d 31 37 95 07 8e d2 c8 6a db 92 e0 93 35 4b f8 b0
 29 b9 8f 21 d8 70 0a 5d 91 c5 fb 9c 51 b2 3e a8 6e 53 78 64
 5c e5 c3 06 02 ab e5 0e 11 96 ea a9 f4 99 ea f2 66 d2 c2 6b
 86 eb 90 0d f1 85 3f ef 51 02 03 01 00 01

现在使用我知道使用:

WebSocketFactory.setVerifyHostname(false);

导致另一个错误,我不想更改我的代码,因此它会验证各种证书或自签名证书,如下所示:

TrustManager[] trustAllCerts = new TrustManager[]new X509TrustManager() 
public X509Certificate[] getAcceptedIssuers() 
X509Certificate[] myTrustedAnchors = new X509Certificate[0];
return myTrustedAnchors;


@Override
public void checkClientTrusted(X509Certificate[] certs,
String authType) 


@Override
public void checkServerTrusted(X509Certificate[] certs,
String authType) 

;

SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, trustAllCerts, new SecureRandom());
factory.setSSLContext(sslContext);
socket = factory.createSocket(API_SOCKET);TooTallNate/Java-WebSockethttps://github.comTooTallNate/Java-WebSockethttps://github.com

也不是这样:

SSLContext sslContext = SSLContexts.custom().loadTrustMaterial(null, new TrustSelfSignedStrategy()).build();
                    factory.setSSLContext(sslContext);
                    socket = factory.createSocket(API_SOCKET);

我也不想在我的应用程序中存储服务器的证书,因为它可能会不时更改并且维护会失控:

    //1. Get the cert file from the server 
    //2. Converted the cert to a BKS format
    
    
     // loading CAs from an InputStream 
CertificateFactory cf = CertificateFactory.getInstance("X.509"); 
InputStream cert = context.getResources().openRawResource(R.raw.my_cert); 
Certificate ca;
 try 
 ca = cf.generateCertificate(cert);
  finally 
 cert.close();
  
// creating a KeyStore containing our trusted CAs 
String keyStoreType = KeyStore.getDefaultType(); 
KeyStore keyStore = KeyStore.getInstance(keyStoreType); 
keyStore.load(null, null); 
keyStore.setCertificateEntry("ca", ca); 
// creating a TrustManager that trusts the CAs in our KeyStore 
String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm(); 
TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm); 
tmf.init(keyStore); 
// creating an SSLSocketFactory that uses our TrustManager 
SSLContext sslContext = SSLContext.getInstance("TLS"); 
sslContext.init(null, tmf.getTrustManagers(), null); 

是否有其他解决方案可以仅通过主机验证问题而不影响安全性或维护?

【问题讨论】:

【参考方案1】:

之前遇到同样的问题,发现大公司签署的证书是受设备信任的,所以当其他人试图制作自己的证书时非常危险,因为(在这种情况下)android无法确定,那您的数据已加密

您要么应该拥有正确的证书,要么全部信任它们。第二种解决方案可用于开发,但绝不应在生产中使用

【讨论】:

以上是关于Android:对于套接字,传递“对等方的证书与预期的主机名不匹配”错误的最安全方法?的主要内容,如果未能解决你的问题,请参考以下文章

在 android studio 中使用 AsyncTask 从套接字读取数据

CORBA 代理/代理通过 HTTP 还是通过套接字访问(对于 PHP)?

Android 的 Web 套接字连接

关于原始套接字的两个小问题

在你的 redux 应用程序中哪里有套接字对象? [关闭]

Android:IPC之AIDL的学习和总结