Search code examples
azuregoazure-iot-hubazure-rest-api

How to send a message to a device with self signed certification as authentication in Azure IoT Hub through the REST API


I'm using Golang to send messages to Azure IoT Hub and as we may or may not know, Azure does not have an SDK for Golang.

I've found answers on how to do it throughout the REST API using the SAS token

I Found REST API for Azure IoT Hub using the SAS token in SO

code from SO:

func main() {
    
    sasToken :="SharedAccessSignature sr=<your IoT Hub name>.azure-devices.net/devices/<your device ID>&sig=<your signature>&se=<expiry time>&skn=<policy name>"

    
    deviceID := "sampath671"
    iotHubName := "certificate122ravi"

    
    url := fmt.Sprintf("https://%s.azure-devices.net/devices/%s?api-version=2020-05-31-preview", iotHubName, deviceID)

    
    requestBody := map[string]interface{}{
        "deviceId":                  deviceID,
        "status":                    "disabled",                        
        "statusReason":              "Device disabled for maintenance",
        "connectionState":           "Disconnected",                    
        "lastActivityTime":          "2024-03-12T12:00:00Z",        
        "cloudToDeviceMessageCount": 10,                                
    }
    requestBodyBytes, err := json.Marshal(requestBody)
    if err != nil {
        log.Fatal(err)
    }

    
    req, err := http.NewRequest("PUT", url, bytes.NewBuffer(requestBodyBytes))
    if err != nil {
        log.Fatal(err)
    }

    
    req.Header.Set("Content-Type", "application/json")
    req.Header.Set("Authorization", sasToken)


    currentETag, err := getCurrentETag(deviceID, sasToken, iotHubName)
    if err != nil {
        log.Fatal(err)
    }

but I can't find anything on using certification for Azure IoT Hub, my Azure IoT Hub device has an x509 authentication need So can someone help me out with Azure IoT Hub device having an x509 authentication?

Thanks in advance.


Solution

  • Even for creating an X.509 Self-Signed device in Azure IoT Hub the Authorization used is SAS token. The REST APIs for IoT Hub services used for device messaging, device management, and job scheduling. They use the Authorization header as a Shared Access Signature (SAS) token from this document. Thus, the Send Device Event / Send a device-to-cloud message REST API uses a SAS token. We can send messages to an X.509 Self-Signed device using a SAS token only.

    enter image description here

    The code below in go is for creating an IoT device in Azure IoT Hub with authentication type X.509 Self-Signed:

    package main
    
    import (
        "bytes"
        "encoding/json"
        "fmt"
        "io/ioutil"
        "log"
        "net/http"
    )
    
    type DeviceAuthentication struct {
        Type             string `json:"type"`
        X509Thumbprint   X509Thumbprint `json:"x509Thumbprint"`
    }
    
    type X509Thumbprint struct {
        PrimaryThumbprint   string `json:"primaryThumbprint"`
        SecondaryThumbprint string `json:"secondaryThumbprint"`
    }
    
    type DeviceCapabilities struct {
        IotEdge bool `json:"iotEdge"`
    }
    
    type Device struct {
        Authentication           DeviceAuthentication `json:"authentication"`
        Capabilities             DeviceCapabilities   `json:"capabilities"`
        CloudToDeviceMessageCount int                  `json:"cloudToDeviceMessageCount"`
        ConnectionState          string               `json:"connectionState"`
        ConnectionStateUpdated   string               `json:"connectionStateUpdatedTime"`
        DeviceId                 string               `json:"deviceId"`
        DeviceScope              string               `json:"deviceScope"`
        Etag                     string               `json:"etag"`
        GenerationId             string               `json:"generationId"`
        LastActivityTime         string               `json:"lastActivityTime"`
        Status                   string               `json:"status"`
        StatusReason             string               `json:"statusReason"`
        StatusUpdatedTime        string               `json:"statusUpdatedTime"`
    }
    
    func main() {
        sasToken := "SharedAccessSignature sr=<your IoT Hub name>.azure-devices.net/devices/<your device ID>&sig=<your signature>&se=<expiry time>&skn=<policy name>"
    
        deviceID := "sampleDevice"
        iotHubName := "your-iot-hub-name"
    
        url := fmt.Sprintf("https://%s.azure-devices.net/devices/%s?api-version=2020-05-31-preview", iotHubName, deviceID)
    
        requestBody := Device{
            Authentication: DeviceAuthentication{
                Type: "selfSigned",
                X509Thumbprint: X509Thumbprint{
                    PrimaryThumbprint:   "YOUR_PRIMARY_THUMBPRINT",
                    SecondaryThumbprint: "YOUR_SECONDARY_THUMBPRINT",
                },
            },
            Capabilities:             DeviceCapabilities{IotEdge: false},
            CloudToDeviceMessageCount: 0,
            ConnectionState:          "Disconnected",
            ConnectionStateUpdated:   "2024-04-04T00:00:00Z",
            DeviceId:                 deviceID,
            DeviceScope:              "",
            Etag:                     "",
            GenerationId:             "",
            LastActivityTime:         "2024-04-04T00:00:00Z",
            Status:                   "enabled",
            StatusReason:             "",
            StatusUpdatedTime:        "2024-04-04T00:00:00Z",
        }
    
        requestBodyBytes, err := json.Marshal(requestBody)
        if err != nil {
            log.Fatal(err)
        }
    
        req, err := http.NewRequest("PUT", url, bytes.NewBuffer(requestBodyBytes))
        if err != nil {
            log.Fatal(err)
        }
    
        req.Header.Set("Content-Type", "application/json")
        req.Header.Set("Authorization", sasToken)
    
        client := &http.Client{}
        resp, err := client.Do(req)
        if err != nil {
            log.Fatal(err)
        }
        defer resp.Body.Close()
    
        responseBody, err := ioutil.ReadAll(resp.Body)
        if err != nil {
            log.Fatal(err)
        }
    
        if resp.StatusCode != http.StatusOK {
            log.Fatalf("Failed to create/update device: %s", responseBody)
        }
    
        fmt.Println("Device created/updated successfully!")
        fmt.Println("Response Body:", string(responseBody))
    }
    

    enter image description here

    The code below sends a device-to-cloud message to a device using X.509/SAS authentication through the REST API:

    package main
    
    import (
        "bytes"
        "fmt"
        "log"
        "net/http"
    )
    
    func main() {
        // Replace these variables with your actual values
        iotHubName := "your-iot-hub-name"
        deviceID := "your-device-id"
        sasToken := "SharedAccessSignature sr=<your IoT Hub name>.azure-devices.net&sig=<your signature>&se=<expiry time>&skn=<policy name>"
    
        // Message payload
        message := []byte(`{"data":"Hello from your device!"}`)
    
        // Construct the URL
        url := fmt.Sprintf("https://%s.azure-devices.net/devices/%s/messages/events?api-version=2020-03-13", iotHubName, deviceID)
    
        // Create a POST request with the message payload
        req, err := http.NewRequest("POST", url, bytes.NewBuffer(message))
        if err != nil {
            log.Fatal(err)
        }
    
        // Set the required headers
        req.Header.Set("Content-Type", "application/json")
        req.Header.Set("Authorization", sasToken)
    
        // Send the request
        client := &http.Client{}
        resp, err := client.Do(req)
        if err != nil {
            log.Fatal(err)
        }
        defer resp.Body.Close()
    
        // Check the response status
        if resp.StatusCode == http.StatusNoContent {
            fmt.Println("Message sent successfully!")
        } else {
            fmt.Printf("Failed to send message. Status code: %d\n", resp.StatusCode)
        }
    }
    
    

    enter image description here

    To check message is sent use the Azure CLI to monitor events from an Azure IoT Hub for a specific device

    az iot hub monitor-events --hub-name HubName --device-id deviceidname
    

    enter image description here