2017-11-14 5 views
2

현재 Google Cloud IoT Core에 연결하기위한 Android 관련 프로그램에 대한 작업을 진행 중입니다. Google에서 제공 한 maven 코드 샘플을 사용하여 Gradle 용으로 수정했습니다 (모든 가져 오기 및 물건 포함). 검사의 모든 종류의 일을 한 후, 때마다 나는이 오류가 내가 생각하고있는 개인 키 파일이 JWT에 사용할 것을 말해Cloud Iot 핵심 코드의 java.io.FileNotFoundException

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

에게주는 유지 안드로이드 사항을 실행하는 나무 딸기 PI3에 프로그램을 실행하려고 그것은 않는 사실에도 불구하고 존재하지 않는 나는 file.Here이 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] 
    } 

} 

과 MqttExa 내 자바 코드

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(); 
     } 
    } 
} 

있는 PEM의 경로를 준 mpleOptions.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; 
     } 
    } 
} 

누구든지이 문제를 해결할 수 있습니까?
P. 코드가 완전히 엉뚱 해 보입니다. Android 프로그래밍 경험이 없으므로 놓아주세요.

+0

가장 큰 이유는 파일이 존재하지만 읽을 권한이 없기 때문입니다. –

답변

2

나는 파일 I/O를 올바르게 수행하지 않을 것이라고 확신한다. 파일 "com/example/adityaprakash/test/rsa_private.pem"은 장치의 실제 파일 경로와 일치하지 않습니다. 장치의 파일 위치가 프로젝트의 위치와 다를 수 있습니다. 파일에서 실제로 파일의 위치를 ​​확인해야합니다.

+0

나는 adb push를 사용하여 rsa_private_pkcs8 파일을 Android의 sdcard에있는 모든 디렉토리에 저장하고 그것은 변수에서 전체 경로입니다, 그럼 제대로 작동해야합니까? – Aditya

+0

예 파일 경로가 사용중인 문자열과 일치해야합니다. –

1

AndroidThings에서 Android 리소스에 인증 자격 증명을 제공하는 것이 더 쉽습니다. 어떻게 작동하는지 보려면 my fork of the WeatherStation sample을 참조하십시오.

:

다음/원료 privatekey.txt

먼저, 응용 프로그램에 (예를 들어 rsa_private_pkcs8) 개인 키 파일을 복사/SRC/메인/입술 /, 당신은 당신의 JWT를 계산하는 데 사용되는 키를로드 할 수 있습니다

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"); 

마지막으로 Java에서 문제를 일으킬 수있는 pkcs8 형식이 아닌 파일을 참조하고있는 것으로 보입니다. Android (Java)에서 자격 증명을 열 때 PKCS8에 패키지 된 키를 사용해야합니다.

3

아래의 예는 Android 용으로 설계되지 않았습니다.

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

은 안드로이드 파일 시스템의 같은 디렉토리에 관련하지 않음.


가 나는 AndroidThings에게 여기 클라우드의 IoT 코어 대화하는 방법 설명을 썼다 : 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에 포함시키고 개인 파일 저장소 액세스를 사용합니다. 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(); 
    }