我目前正在开发用于连接到 Google Cloud IoT Core 的 Android Things 程序。我曾经对 Google 提供的 Maven 代码进行采样,并针对 Gradle 对其进行了修改(包含所有导入和内容)。进行各种检查后,每当我尝试在运行 Android Things 的 Raspberry Pi3 上运行程序时,它都会不断出现此错误

W/System.err: java.io.FileNotFoundException: com/example/adityaprakash/test/rsa_private.pem (No such file or directory) 

告诉我我应该用于 JWT 的私钥文件不存在,尽管它确实存在并且我已经给出了 pem 文件的路径。这是我的 java 代码

package com.example.adityaprakash.test; 
 
import android.support.v7.app.AppCompatActivity; 
import android.os.Bundle; 
import android.util.Log; 
 
public class MainActivity extends AppCompatActivity { 
 
    @Override 
    protected void onCreate(Bundle savedInstanceState) { 
        super.onCreate(savedInstanceState); 
        //setContentView(R.layout.activity_main); 
        Log.i("#########","######"); 
        MqttExample mqtt = new MqttExample(); 
        try { 
            mqtt.Start(); 
        } catch (Exception e) { 
            e.printStackTrace(); 
        } 
    } 
} 

MqttExample.java

package com.example.adityaprakash.test; 
// [END cloudiotcore_mqtt_imports] 
import org.eclipse.paho.client.mqttv3.MqttClient; 
import org.eclipse.paho.client.mqttv3.MqttConnectOptions; 
import org.eclipse.paho.client.mqttv3.MqttMessage; 
import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence; 
 
import org.joda.time.DateTime; 
 
import java.io.BufferedReader; 
import java.io.FileReader; 
 
import java.security.KeyFactory; 
import java.security.spec.PKCS8EncodedKeySpec; 
 
import android.util.Base64; 
 
import io.jsonwebtoken.JwtBuilder; 
import io.jsonwebtoken.Jwts; 
import io.jsonwebtoken.SignatureAlgorithm; 
 
public class MqttExample { 
 
    // [START cloudiotcore_mqtt_createjwt] 
    /** Create a Cloud IoT Core JWT for the given project id, signed with the given RSA key. */ 
    public static String createJwtRsa(String projectId, String privateKeyFile) throws Exception { 
        DateTime now = new DateTime(); 
 
        String strKeyPEM = ""; 
        BufferedReader br = new BufferedReader(new FileReader(privateKeyFile)); 
        String line; 
        while ((line = br.readLine()) != null) { 
            strKeyPEM += line + "\n"; 
        } 
        br.close(); 
        // Create a JWT to authenticate this device. The device will be disconnected after the token 
        // expires, and will have to reconnect with a new token. The audience field should always be set 
        // to the GCP project id. 
        JwtBuilder jwtBuilder = 
                Jwts.builder() 
                        .setIssuedAt(now.toDate()) 
                        .setExpiration(now.plusMinutes(20).toDate()) 
                        .setAudience(projectId); 
        String privateKeyPEM = strKeyPEM; 
        privateKeyPEM = privateKeyPEM.replace("-----BEGIN PRIVATE KEY-----\n", ""); 
        privateKeyPEM = privateKeyPEM.replace("-----END PRIVATE KEY-----", ""); 
        byte[] encoded = Base64.decode(privateKeyPEM,Base64.DEFAULT); 
        PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(encoded); 
        KeyFactory kf = KeyFactory.getInstance("RSA"); 
 
        return jwtBuilder.signWith(SignatureAlgorithm.RS256, kf.generatePrivate(spec)).compact(); 
    } 
 
 
    /** Parse arguments, configure MQTT, and publish messages. */ 
    public void Start() throws Exception { 
        // [START cloudiotcore_mqtt_configuremqtt] 
        MqttExampleOptions options = MqttExampleOptions.values(); 
        if (options == null) { 
            // Could not parse. 
            System.exit(1); 
        } 
        // Build the connection string for Google's Cloud IoT Core MQTT server. Only SSL 
        // connections are accepted. For server authentication, the JVM's root certificates 
        // are used. 
        final String mqttServerAddress = 
                String.format("ssl://%s:%s", options.mqttBridgeHostname, options.mqttBridgePort); 
 
        // Create our MQTT client. The mqttClientId is a unique string that identifies this device. For 
        // Google Cloud IoT Core, it must be in the format below. 
        final String mqttClientId = 
                String.format( 
                        "projects/%s/locations/%s/registries/%s/devices/%s", 
                        options.projectId, options.cloudRegion, options.registryId, options.deviceId); 
 
        MqttConnectOptions connectOptions = new MqttConnectOptions(); 
        // Note that the the Google Cloud IoT Core only supports MQTT 3.1.1, and Paho requires that we 
        // explictly set this. If you don't set MQTT version, the server will immediately close its 
        // connection to your device. 
        connectOptions.setMqttVersion(MqttConnectOptions.MQTT_VERSION_3_1_1); 
 
        // With Google Cloud IoT Core, the username field is ignored, however it must be set for the 
        // Paho client library to send the password field. The password field is used to transmit a JWT 
        // to authorize the device. 
        connectOptions.setUserName("unused"); 
 
        System.out.println(options.algorithm); 
        if (options.algorithm.equals("RS256")) { 
            connectOptions.setPassword( 
                    createJwtRsa(options.projectId, options.privateKeyFile).toCharArray()); 
        }else { 
            throw new IllegalArgumentException( 
                    "Invalid algorithm " + options.algorithm + ". Should be one of 'RS256' or 'ES256'."); 
        } 
        // [END cloudiotcore_mqtt_configuremqtt] 
 
        // [START cloudiotcore_mqtt_publish] 
        // Create a client, and connect to the Google MQTT bridge. 
        MqttClient client = new MqttClient(mqttServerAddress, mqttClientId, new MemoryPersistence()); 
        try { 
            client.connect(connectOptions); 
 
            // Publish to the events or state topic based on the flag. 
            String subTopic = options.messageType.equals("event") ? "events" : options.messageType; 
 
            // The MQTT topic that this device will publish telemetry data to. The MQTT topic name is 
            // required to be in the format below. Note that this is not the same as the device registry's 
            // Cloud Pub/Sub topic. 
            String mqttTopic = String.format("/devices/%s/%s", options.deviceId, subTopic); 
 
            // Publish numMessages messages to the MQTT bridge, at a rate of 1 per second. 
            for (int i = 1; i <= options.numMessages; ++i) { 
                String payload = String.format("%s/%s-payload number-%d", options.registryId, options.deviceId, i); 
                System.out.format( 
                        "Publishing %s message %d/%d: '%s'\n", 
                        options.messageType, i, options.numMessages, payload); 
 
                // Publish "payload" to the MQTT topic. qos=1 means at least once delivery. Cloud IoT Core 
                // also supports qos=0 for at most once delivery. 
                MqttMessage message = new MqttMessage(payload.getBytes()); 
                message.setQos(1); 
                client.publish(mqttTopic, message); 
 
                if (options.messageType.equals("event")) { 
                    // Send telemetry events every second 
                    Thread.sleep(1000); 
                } 
                else { 
                    // Note: Update Device state less frequently than with telemetry events 
                    Thread.sleep(5000); 
                } 
            } 
        } finally { 
            // Disconnect the client and finish the run. 
            client.disconnect(); 
        } 
        System.out.println("Finished loop successfully. Goodbye!"); 
        // [END cloudiotcore_mqtt_publish] 
    } 
 
} 

和 MqttExampleOptions.java 代码:

package com.example.adityaprakash.test; 
 
public class MqttExampleOptions { 
    String projectId; 
    String registryId; 
    String deviceId; 
    String privateKeyFile; 
    String algorithm; 
    String cloudRegion; 
    int numMessages; 
    String mqttBridgeHostname; 
    short mqttBridgePort; 
    String messageType; 
 
    /** Construct an MqttExampleOptions class. */ 
    public static MqttExampleOptions values() { 
 
        try { 
 
            MqttExampleOptions res = new MqttExampleOptions(); 
 
            res.projectId = "_"; 
            res.registryId = "_"; 
            res.deviceId = "_"; 
            res.privateKeyFile = "com/example/adityaprakash/test/rsa_private.pem"; 
            res.algorithm = "RS256"; 
            res.cloudRegion = "asia-east1"; 
            res.numMessages = 100; 
            res.mqttBridgeHostname = "mqtt.googleapis.com"; 
            res.mqttBridgePort = 8883; 
            res.messageType = "event"; 
            return res; 
 
        } catch (Exception e) { 
            System.err.println(e.getMessage()); 
            return null; 
        } 
    } 
} 

请问谁能解决这个问题。
附言我知道代码看起来很糟糕。我没有 Android 编程经验,所以请随它去吧。

请您参考如下方法:

您所遵循的示例不是为 Android 设计的。

res.privateKeyFile = "com/example/adityaprakash/test/rsa_private.pem";

不会与 Android 文件系统上的相同目录相关。


我在这里写了一个 AndroidThings 解释如何与 Cloud IoT Core 对话:http://blog.blundellapps.co.uk/tut-google-cloud-iot-core-mqtt-on-android/

您可以像这样设置通信(将您的 pem 文件放入 /raw 目录)

// Setup the communication with your Google IoT Core details 
        communicator = new IotCoreCommunicator.Builder() 
                .withContext(this) 
                .withCloudRegion("your-region") // ex: europe-west1 
                .withProjectId("your-project-id")   // ex: supercoolproject23236 
                .withRegistryId("your-registry-id") // ex: my-devices 
                .withDeviceId("a-device-id") // ex: my-test-raspberry-pi 
                .withPrivateKeyRawFileId(R.raw.rsa_private) 
                .build(); 

源代码在这里:https://github.com/blundell/CloudIoTCoreMQTTExample


请注意,以上内容对于安全环境或端到端工作测试来说已经足够了。但是,如果您想发布生产 IoT 设备,您会考虑将 PEM 嵌入 ROM 并使用私有(private)文件存储访问。 https://developer.android.com/training/articles/keystore.html

可以在这里找到一个例子:https://github.com/androidthings/sensorhub-cloud-iot

特别是这个类:

https://github.com/androidthings/sensorhub-cloud-iot/blob/e50bde0100fa81818ebbadb54561b3b68ccb64b8/app/src/main/java/com/example/androidthings/sensorhub/cloud/cloudiot/MqttAuthentication.java

然后您可以在设备上生成和使用 PEM:

  public Certificate getCertificate() { 
    KeyStore ks = KeyStore.getInstance("AndroidKeyStore"); 
    ks.load(null); 
 
    certificate = ks.getCertificate("Cloud IoT Authentication"); 
    if (certificate == null) { 
      Log.w(TAG, "No IoT Auth Certificate found, generating new cert"); 
      generateAuthenticationKey(); 
      certificate = ks.getCertificate(keyAlias); 
    } 
    Log.i(TAG, "loaded certificate: " + keyAlias);  
 } 

    private void generateAuthenticationKey() throws GeneralSecurityException { 
      KeyPairGenerator kpg = KeyPairGenerator.getInstance(KeyProperties.KEY_ALGORITHM_RSA, "AndroidKeyStore"); 
      kpg.initialize(new KeyGenParameterSpec.Builder("Cloud IoT Authentication",KeyProperties.PURPOSE_SIGN) 
       .setKeySize(2048) 
       .setCertificateSubject(new X500Principal("CN=unused")) 
       .setDigests(KeyProperties.DIGEST_SHA256) 
     .setSignaturePaddings(KeyProperties.SIGNATURE_PADDING_RSA_PKCS1) 
       .build()); 
 
      kpg.generateKeyPair(); 
   } 


评论关闭
IT序号网

微信公众号号:IT虾米 (左侧二维码扫一扫)欢迎添加!