MQTT:javax.net.ssl.SSLHandshakeException:证书上没有 subjectAltNames 匹配

问题描述 投票:0回答:3

我正在尝试通过泛美卫生组织客户端连接服务器(我使用的版本是

org.eclipse.paho.client.mqttv3-1.2.1.jar

当我尝试连接时,我得到:

“MQTT:javax.net.ssl.SSLHandshakeException:没有 subjectAltNames 证书匹配”

但是我在

org.eclipse.paho.client.mqttv3-1.2.0.jar

中没有遇到这个问题

下面是mosquitto的日志

“sslv3 警报证书未知”

android ssl mqtt paho
3个回答
2
投票

该错误与 Paho 或 MQTT 无关。

这意味着代理提供的证书与用于连接到它的主机名不匹配。

您需要为代理获取一个与用于连接到它的所有主机名相匹配的新证书,或者确保您使用的是证书中包含的主机名。


2
投票

虽然这不是一个好的做法,但如果您信任您的经纪人并且不想验证其身份,您可以构建一个虚假的信任管理器,如下所示,它不会验证任何内容(客户端没有使用 ca 证书)并且客户端会连接到经纪人无一例外(尽管通信保持加密)。

private fun fakeSocketFactory() : SocketFactory {
    val trustManager = object: X509TrustManager {
        override fun checkClientTrusted(chain: Array<out java.security.cert.X509Certificate>?, authType: String?) {}
        override fun checkServerTrusted(chain: Array<out java.security.cert.X509Certificate>?, authType: String?) {}
        override fun getAcceptedIssuers(): Array<java.security.cert.X509Certificate> = arrayOf()
    }
    val sslContext = SSLContext.getInstance("SSL")
    sslContext.init(null, arrayOf(trustManager), SecureRandom())
    return sslContext.socketFactory
}

override fun connect() {
    val options = MqttConnectOptions()
        ...
    options.socketFactory = fakeSocketFactory()
    client.connect(options,null,object:IMqttActionListener{
        ...
    })
}

这类似于 chrome 的警告“您的连接不是私密的。”以及我们的操作“继续(不安全)”。


0
投票

我在我的项目上花了一些时间来解决这个问题,所以即使这是一个老问题,我也会添加一些信息,也许它可以帮助别人。

我也有同样的问题;我的证书与蚊子客户端配合良好,并且与使用

org.eclipse.paho.client.mqttv3-1.2.0.jar
的 Android 应用程序配合良好,但不适用于该库的较新版本。

我明白为什么它适用于

1.2.0
:此版本存在问题,主机名未验证(请参阅 CVE-2019-11777

1.2.1
或以上,主机名已得到很好的验证,所以你会遇到问题
MQTT: javax.net.ssl.SSLHandshakeException: No subjectAltNames on the certificate match

我不知道为什么,但在 android 中,主机名仅使用

SAN
属性进行验证,而不是使用
CN
。我在 okhttp OkHostnameVerifier.java.

中进行了自定义 Android 更改

对我来说有2个解决方案:

  1. 始终添加与证书中的
    SAN
    匹配的
    CN
    属性(可能是最佳解决方案)
  2. 使用自定义主机名验证器(使用 MqttConnectOptions#setSSLHostnameVerifier 方法)您可以使用 OkHostnameVerifier.java 并重新添加注释部分:
  /**
   * Returns true if {@code certificate} matches {@code hostName}.
   */
  private boolean verifyHostName(String hostName, X509Certificate certificate) {
    hostName = hostName.toLowerCase(Locale.US);
    boolean hasDns = false;
    List<String> altNames = getSubjectAltNames(certificate, ALT_DNS_NAME);
    for (int i = 0, size = altNames.size(); i < size; i++) {
      hasDns = true;
      if (verifyHostName(hostName, altNames.get(i))) {
        return true;
      }
    }
    // BEGIN Android-removed: Ignore common name in hostname verification. http://b/70278814
    if (!hasDns) {
      X500Principal principal = certificate.getSubjectX500Principal();
      // RFC 2818 advises using the most specific name for matching.
      String cn = new DistinguishedNameParser(principal).findMostSpecific("cn");
      if (cn != null) {
        return verifyHostName(hostName, cn);
      }
    }
    // END Android-removed: Ignore common name in hostname verification. http://b/70278814
    return false;
  }
© www.soinside.com 2019 - 2024. All rights reserved.