All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.microsoft.azure.sdk.iotcentral.device.SasAuthentication Maven / Gradle / Ivy

There is a newer version: 2.0.0
Show newest version
// Copyright (c) Luca Druda. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
package com.microsoft.azure.sdk.iotcentral.device;

import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.InputStreamReader;
import java.net.NoRouteToHostException;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.UnknownHostException;
import java.nio.charset.StandardCharsets;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import javax.net.ssl.HttpsURLConnection;

import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.microsoft.azure.sdk.iot.deps.util.Base64;
import com.microsoft.azure.sdk.iot.device.DeviceClient;
import com.microsoft.azure.sdk.iot.device.IotHubClientProtocol;
import com.microsoft.azure.sdk.iot.device.auth.Signature;
import com.microsoft.azure.sdk.iotcentral.device.enums.IOTC_CONNECTION_STATE;
import com.microsoft.azure.sdk.iotcentral.device.exceptions.IoTCentralException;

public class SasAuthentication {

    final int DEFAULT_EXPIRATION = 21600; // 6 hours

    private String endpoint;
    private String id;
    private String scopeId;
    private String symKey;
    private ILogger logger;
    IotHubClientProtocol protocol;

    public SasAuthentication(String endpoint, IotHubClientProtocol protocol, String id, String scopeId, String symKey,
            ILogger logger) {
        this.endpoint = endpoint;
        this.id = id;
        this.scopeId = scopeId;
        this.symKey = symKey;
        this.logger = logger;
        this.protocol = protocol;

    }

    public DeviceClient Register() throws IoTCentralException {
        if (this.scopeId == null || this.scopeId.length() == 0 || this.symKey == null || this.symKey.length() == 0
                || this.id == null || this.id.length() == 0) {
            throw new IoTCentralException("Wrong credentials values");
        }
        long time = (System.currentTimeMillis() / 1000 | 0) + DEFAULT_EXPIRATION;
        String deviceKey = this.ComputeKey(this.symKey, this.id);
        Signature sig = new Signature(this.scopeId + "%2fregistrations%2f" + this.id, time, deviceKey);
        String sasKey = String.format(
                "SharedAccessSignature sr=%s%%2fregistrations%%2f%s&sig=%s&skn=registration&se=%s", this.scopeId,
                this.id, sig.toString(), time);
        String operationId = this.RequestRegistration(sasKey);
        String connectionString = this.GetAssignment(deviceKey, sasKey, operationId);
        DeviceClient client;
        try {
            client = new DeviceClient(connectionString, protocol);
            return client;
        } catch (IllegalArgumentException | URISyntaxException e) {
            throw new IoTCentralException("Invalid connection string: " + connectionString);
        }

    }

    private String RequestRegistration(String sasKey) throws IoTCentralException {
        try {
            URL url = new URL(String.format("https://%s/%s/registrations/%s/register?api-version=2018-09-01-preview",
                    this.endpoint, this.scopeId, this.id));
            HttpsURLConnection con = (HttpsURLConnection) url.openConnection();
            con.setRequestMethod("PUT");
            con.setRequestProperty("Accept", "application/json");
            con.setRequestProperty("Host", this.endpoint);
            con.setRequestProperty("Content-Type", "application/json");
            con.setRequestProperty("charset", "utf-8");
            con.setRequestProperty("Connection", "keep-alive");
            con.setRequestProperty("UserAgent", "prov_device_client/1.0");
            con.setRequestProperty("Authorization", sasKey);
            con.setDoOutput(true);
            DataOutputStream os = new DataOutputStream(con.getOutputStream());
            os.writeBytes("{\"registrationId\":\"" + this.id + "\"}");
            os.flush();
            os.close();
            BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream()));
            String inputLine;
            StringBuffer content = new StringBuffer();
            while ((inputLine = in.readLine()) != null) {
                content.append(inputLine);
            }
            in.close();
            JsonObject data = (JsonObject) new JsonParser().parse(content.toString());
            return data.get("operationId").getAsString();
        } catch (Exception ex) {
            if (ex instanceof UnknownHostException) {
                // network not available
                throw new IoTCentralException(IOTC_CONNECTION_STATE.NO_NETWORK);
            }
            throw new IoTCentralException(ex.getMessage());
        }
    }

    private String GetAssignment(String deviceKey, String sasKey, String operationId) throws IoTCentralException {
        try {
            URL url = new URL(
                    String.format("https://%s/%s/registrations/%s/operations/%s?api-version=2018-09-01-preview",
                            this.endpoint, this.scopeId, this.id, operationId));
            HttpsURLConnection con = (HttpsURLConnection) url.openConnection();
            con.setRequestMethod("GET");
            con.setRequestProperty("Accept", "application/json");
            con.setRequestProperty("Host", this.endpoint);
            con.setRequestProperty("Content-Type", "application/json");
            con.setRequestProperty("charset", "utf-8");
            con.setRequestProperty("Connection", "keep-alive");
            con.setRequestProperty("UserAgent", "prov_device_client/1.0");
            con.setRequestProperty("Authorization", sasKey);
            BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream()));
            String inputLine;
            StringBuffer content = new StringBuffer();
            while ((inputLine = in.readLine()) != null) {
                content.append(inputLine);
            }
            in.close();
            JsonObject data = (JsonObject) new JsonParser().parse(content.toString());
            if (data.get("status").getAsString().equals("assigned")) {
                this.logger.Log("Device registered");
                JsonObject registrationState = (JsonObject) data.get("registrationState");
                return String.format("HostName=%s;DeviceId=%s;SharedAccessKey=%s",
                        registrationState.get("assignedHub").getAsString(),
                        registrationState.get("deviceId").getAsString(), deviceKey);
            } else if (data.get("status").getAsString().equals("assigning")) {
                this.logger.Log("Waiting for registration");
                Thread.sleep(2000);
                return this.GetAssignment(deviceKey, sasKey, operationId);
            } else {
                throw new Exception("Wrong request");
            }
        } catch (Exception ex) {
            if (ex instanceof NoRouteToHostException) {
                throw new IoTCentralException(IOTC_CONNECTION_STATE.NO_NETWORK);
            }
            throw new IoTCentralException(ex.getMessage());
        }
    }

    private String ComputeKey(String masterKey, String registrationId) {
        try {
            Mac sha256_hmac = Mac.getInstance("HmacSHA256");
            byte[] keyBytes = Base64.decodeBase64Local(new String(masterKey).getBytes(StandardCharsets.UTF_8));
            sha256_hmac.init(new SecretKeySpec(keyBytes, "HmacSHA256"));
            byte[] regBytes = registrationId.getBytes(StandardCharsets.UTF_8);
            byte[] res = sha256_hmac.doFinal(regBytes);
            return Base64.encodeBase64StringLocal(res);
        } catch (Exception ex) {
            return null;
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy