0

我有一个 Greengrass 核心 (Jetson TX2) 广播一个隔离的接入点,因此不同的物联网设备可以相互通信。接入点工作,因为我能够让一个设备通过 OSC 协议与核心通信。

现在我正试图让我的智能手表(运行 WearOS)与 AWS Greengrass MQTT 通信。这需要 SSL 连接以及核心提供的组证书。当核心和智能手表都连接到具有活动互联网连接的网络时,我的实现工作。但是,当我尝试通过隔离接入点与它们通信时,智能手表无法与核心通信

我正在通过paho.mqtt.androidasocketFactory来实现通信以存储我的密钥和证书。我不确定是什么导致我的应用无法与我的核心处理器通信

我试图找出问题所在,智能手表似乎确实连接到了接入点并检索了 IP 地址。但是,该paho.mqtt.android库不使用该 IP 地址来启动 MQTT 连接

这就是我从手表实现客户端的方式

AWSMQTTService.kt

 // ... other code
 socketFactory = AWSIotSslUtility.getSocketFactory(
            applicationContext.resources.openRawResource(R.raw.groupca),
            applicationContext.resources.openRawResource(R.raw.fc7334298c_certificate),
            applicationContext.resources.openRawResource(R.raw.fc7334298c_private)
        )
        mqttClient = MqttAndroidClient(this, "ssl://${getEdgeEndpoint()}:8883", getDeviceName())
        val mqttOptions = MqttConnectOptions()
        mqttOptions.connectionTimeout = 5
        mqttOptions.isAutomaticReconnect = true
        mqttOptions.socketFactory = socketFactory
        mqttClient.connect(mqttOptions, null, object : IMqttActionListener {
            override fun onSuccess(asyncActionToken: IMqttToken?) {
                debugLog("success")
            }

            override fun onFailure(asyncActionToken: IMqttToken?, exception: Throwable) {
                debugLog("failure")
                Log.e("Jarvis/err", "Failure", exception)
            }

        })

AWSIotSslUtility.kt

/*
 * Copyright 2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 *  http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

package com.shelltechworks.jarviswear.lib.utils

import org.bouncycastle.jce.provider.BouncyCastleProvider
import org.bouncycastle.openssl.PEMKeyPair
import org.bouncycastle.openssl.PEMParser
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter
import java.io.*
import java.security.cert.CertificateFactory
import java.security.cert.Certificate as Certificate
import java.security.*
import java.security.Security
import javax.net.ssl.KeyManagerFactory
import javax.net.ssl.SSLContext
import javax.net.ssl.SSLSocketFactory
import javax.net.ssl.TrustManagerFactory


/**
 * This is a helper class to facilitate reading of the configurations and
 * certificate from the resource files.
 */
object AWSIotSslUtility {
    @Throws(Exception::class)
    fun getSocketFactory(
        caCrtFile: InputStream,
        crtFile: InputStream, keyFile: InputStream
    ): SSLSocketFactory {
        Security.addProvider(BouncyCastleProvider())
        val cf = CertificateFactory.getInstance("X.509")

        val caCert = cf.generateCertificate(caCrtFile)

        val cert = cf.generateCertificate(crtFile)

        // load client private key
        val pemParser = PEMParser(InputStreamReader(keyFile))

        val pemRawValue = pemParser.readObject()
        val converter = JcaPEMKeyConverter()
            .setProvider("BC")
        val key: KeyPair
        key = converter.getKeyPair(pemRawValue as PEMKeyPair)
        pemParser.close()

        // CA certificate is used to authenticate server
        val caKs = KeyStore.getInstance(KeyStore.getDefaultType())
        caKs.load(null, null)
        caKs.setCertificateEntry("ca-certificate", caCert)
        val tmf = TrustManagerFactory.getInstance("X509")
        tmf.init(caKs)

        // client key and certificates are sent to server so it can authenticate
        // us
        val ks = KeyStore.getInstance(KeyStore.getDefaultType())
        ks.load(null, null)
        ks.setCertificateEntry("certificate", cert)
        ks.setKeyEntry(
            "private-key", key.private, "".toCharArray(),
            arrayOf(cert)
        )
        val kmf = KeyManagerFactory.getInstance(
            KeyManagerFactory.getDefaultAlgorithm()
        )
        kmf.init(ks, "".toCharArray())

        // finally, create SSL socket factory
        val context = SSLContext.getInstance("TLSv1.2")
        context.init(kmf.keyManagers, tmf.trustManagers, null)
        return context.socketFactory
    }
}

我希望这能够连接并打印success到调试日志。但是,我收到了这个错误

2019-05-29 16:18:31.259 13475-13475/com.shelltechworks.jarviswear D/Jarvis/AWSMQTTService: failure
2019-05-29 16:18:31.270 13475-13475/com.shelltechworks.jarviswear E/Jarvis/err: Failure
    MqttException (0) - java.net.SocketTimeoutException: failed to connect to /192.168.12.1 (port 8883) from /192.168.167.239 (port 41415) after 5000ms
        at org.eclipse.paho.client.mqttv3.internal.ExceptionHelper.createMqttException(ExceptionHelper.java:38)
        at org.eclipse.paho.client.mqttv3.internal.ClientComms$ConnectBG.run(ClientComms.java:715)
        at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:457)
        at java.util.concurrent.FutureTask.run(FutureTask.java:266)
        at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:301)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1162)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:636)
        at java.lang.Thread.run(Thread.java:764)
     Caused by: java.net.SocketTimeoutException: failed to connect to /192.168.12.1 (port 8883) from /192.168.167.239 (port 41415) after 5000ms
        at libcore.io.IoBridge.connectErrno(IoBridge.java:185)
        at libcore.io.IoBridge.connect(IoBridge.java:130)
        at java.net.PlainSocketImpl.socketConnect(PlainSocketImpl.java:129)
        at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:356)
        at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:200)
        at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:182)
        at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:356)
        at java.net.Socket.connect(Socket.java:616)
        at org.eclipse.paho.client.mqttv3.internal.TCPNetworkModule.start(TCPNetworkModule.java:80)
        at org.eclipse.paho.client.mqttv3.internal.SSLNetworkModule.start(SSLNetworkModule.java:103)
        at org.eclipse.paho.client.mqttv3.internal.ClientComms$ConnectBG.run(ClientComms.java:701)
        at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:457) 
        at java.util.concurrent.FutureTask.run(FutureTask.java:266) 
        at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:301) 
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1162) 
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:636) 
        at java.lang.Thread.run(Thread.java:764) 

4

1 回答 1

1

终于想出了解决办法。事实证明,默认情况下,AndroidnetworkManager不会将自己绑定到无法访问 Internet 的 WiFi 连接。此外,如果设备具有蜂窝功能,它将改为绑定。

据我所知,没有解决方案可以在不生根设备的情况下禁用此功能,因此我必须手动/显式地将 WiFi 接口绑定到我的应用程序中的进程

private fun bindAppToWifiNetwork() {
        val builder: NetworkRequest.Builder = NetworkRequest.Builder()
        builder.addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
        val connectivityManager =
            applicationContext.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
        connectivityManager.requestNetwork(builder.build(), object : ConnectivityManager.NetworkCallback() {
            override fun onAvailable(network: Network) {
                connectivityManager.bindProcessToNetwork(network)
            }
        })
    }
于 2019-08-16T13:52:51.143 回答