首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >面向云物联网核心代码的java.io.FileNotFoundException

面向云物联网核心代码的java.io.FileNotFoundException
EN

Stack Overflow用户
提问于 2017-11-14 20:17:00
回答 3查看 410关注 0票数 2

我目前正在为连接到谷歌云IoT核心的程序安卓的东西工作。我曾经对Google提供的maven代码进行过示例,并针对Gradle对其进行了修改(包含了所有的导入和其他内容)。在做了各种检查之后,每当我试图在运行安卓系统的树莓Pi3上运行程序时,它总是出现这个错误

代码语言:javascript
复制
W/System.err: java.io.FileNotFoundException: com/example/adityaprakash/test/rsa_private.pem (No such file or directory)

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

代码语言:javascript
复制
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();
        }
    }
}

The MqttExample.java

代码语言:javascript
复制
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代码:

代码语言:javascript
复制
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编程的经验,所以请让它去吧。

EN

回答 3

Stack Overflow用户

发布于 2017-11-15 16:09:19

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

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

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

我在这里写了一篇关于如何与云IoT核心对话的AndroidThings解释:http://blog.blundellapps.co.uk/tut-google-cloud-iot-core-mqtt-on-android/

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

代码语言:javascript
复制
// 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嵌入到IoT中并使用私有文件存储访问。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:

代码语言:javascript
复制
  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); 
 }

代码语言:javascript
复制
    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();
   }
票数 4
EN

Stack Overflow用户

发布于 2017-11-15 02:40:57

我非常确定您没有正确地执行文件I/O。您的文件"com/example/adityaprakash/test/rsa_private.pem"与设备上的实际文件路径不对应。设备上的文件位置可能与项目中的位置不同。您必须确定您的文件在设备上的实际位置。

票数 2
EN

Stack Overflow用户

发布于 2017-11-15 02:43:56

在AndroidThings上,在Android资源中提供身份验证凭据会更容易。请参阅my fork of the WeatherStation sample了解这是如何工作的。

首先,将私钥文件(例如rsa_private_pkcs8)复制到app/src/main/res/raw/privatekey.txt

接下来,您可以加载用于计算JWT的密钥:

代码语言:javascript
复制
Context mContext;
int resIdPk = getResources().getIdentifier("privatekey", "raw", getPackageName());    

...

InputStream privateKey = mContext.getResources().openRawResource(resIdPk);
byte[] keyBytes = inputStreamToBytes(privateKey);
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyBytes);
KeyFactory kf = KeyFactory.getInstance("EC");

最后要注意的是,您引用的文件似乎不是pkcs8格式的,这会导致Java出现问题。在Android (Java)上打开凭证时,请确保使用PKCS8打包的密钥。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/47285679

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档