com.microsoft.azure.iot.service.auth.IotHubServiceSasToken.java Source code

Java tutorial

Introduction

Here is the source code for com.microsoft.azure.iot.service.auth.IotHubServiceSasToken.java

Source

/*
 * Copyright (c) Microsoft. All rights reserved.
 * Licensed under the MIT license. See LICENSE file in the project root for full license information.
 */

package com.microsoft.azure.iot.service.auth;

import com.microsoft.azure.iot.service.sdk.IotHubConnectionString;
import org.apache.commons.codec.binary.Base64;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;

/** 
 * Grants device access to an IoT Hub for the specified amount of time. 
 */
public final class IotHubServiceSasToken {
    long TOKEN_VALID_SECS = 365 * 24 * 60 * 60;

    /**
     * The SAS token format. The parameters to be interpolated are, in order:
     * the signature
     * the resource URI
     * the expiry time
     * the key name
     * Example: {@code SharedAccessSignature sr=IOTHUBURI&sig=SIGNATURE&se=EXPIRY&skn=SHAREDACCESSKEYNAME}
     */
    public static final String TOKEN_FORMAT = "SharedAccessSignature sr=%s&sig=%s&se=%s&skn=%s";

    /* The URI for a connection to an IoT Hub */
    protected final String resourceUri;
    /* The value of the SharedAccessKey */
    protected final String keyValue;
    /* The time, as a UNIX timestamp, before which the token is valid. */
    protected final long expiryTime;
    /* The value of SharedAccessKeyName */
    protected final String keyName;
    /* The SAS token that grants access. */
    protected final String token;

    /**
     * Constructor. Generates a SAS token that grants access to an IoT Hub for
     * the specified amount of time. (1 year specified in TOKEN_VALID_SECS)
     *
     * @param iotHubConnectionString Connection string object containing the connection parameters
     */
    public IotHubServiceSasToken(IotHubConnectionString iotHubConnectionString) {
        // Codes_SRS_SERVICE_SDK_JAVA_IOTHUBSERVICESASTOKEN_12_001: [The constructor shall throw IllegalArgumentException if the input object is null]
        if (iotHubConnectionString == null) {
            throw new IllegalArgumentException();
        }
        // Codes_SRS_SERVICE_SDK_JAVA_IOTHUBSERVICESASTOKEN_12_002: [The constructor shall create a target uri from the url encoded host name)]
        // Codes_SRS_SERVICE_SDK_JAVA_IOTHUBSERVICESASTOKEN_12_003: [The constructor shall create a string to sign by concatenating the target uri and the expiry time string (one year)]
        // Codes_SRS_SERVICE_SDK_JAVA_IOTHUBSERVICESASTOKEN_12_004: [The constructor shall create a key from the shared access key signing with HmacSHA256]
        // Codes_SRS_SERVICE_SDK_JAVA_IOTHUBSERVICESASTOKEN_12_005: [The constructor shall compute the final signature by url encoding the signed key]
        // Codes_SRS_SERVICE_SDK_JAVA_IOTHUBSERVICESASTOKEN_12_006: [The constructor shall concatenate the target uri, the signature, the expiry time and the key name using the format: "SharedAccessSignature sr=%s&sig=%s&se=%s&skn=%s"]
        this.resourceUri = iotHubConnectionString.getHostName();
        this.keyValue = iotHubConnectionString.getSharedAccessKey();
        this.keyName = iotHubConnectionString.getSharedAccessKeyName();
        this.expiryTime = buildExpiresOn();
        this.token = buildToken();
    }

    /**
     * Helper function to build the token string
     *
     * @return Valid token string
     */
    private String buildToken() {
        String targetUri;
        try {
            // Codes_SRS_SERVICE_SDK_JAVA_IOTHUBSERVICESASTOKEN_12_002: [The constructor shall create a target uri from the url encoded host name)]
            targetUri = URLEncoder.encode(this.resourceUri.toLowerCase(), String.valueOf(StandardCharsets.UTF_8));
            // Codes_SRS_SERVICE_SDK_JAVA_IOTHUBSERVICESASTOKEN_12_003: [The constructor shall create a string to sign by concatenating the target uri and the expiry time string (one year)]
            String toSign = targetUri + "\n" + this.expiryTime;

            // Codes_SRS_SERVICE_SDK_JAVA_IOTHUBSERVICESASTOKEN_12_004: [The constructor shall create a key from the shared access key signing with HmacSHA256]
            // Get an hmac_sha1 key from the raw key bytes
            byte[] keyBytes = Base64.decodeBase64(this.keyValue.getBytes("UTF-8"));
            SecretKeySpec signingKey = new SecretKeySpec(keyBytes, "HmacSHA256");

            // Get an hmac_sha1 Mac instance and initialize with the signing key
            Mac mac = Mac.getInstance("HmacSHA256");
            mac.init(signingKey);

            // Codes_SRS_SERVICE_SDK_JAVA_IOTHUBSERVICESASTOKEN_12_005: [The constructor shall compute the final signature by url encoding the signed key]
            // Compute the hmac on input data bytes
            byte[] rawHmac = mac.doFinal(toSign.getBytes("UTF-8"));
            // Convert raw bytes to Hex
            String signature = URLEncoder.encode(Base64.encodeBase64String(rawHmac), "UTF-8");

            // Codes_SRS_SERVICE_SDK_JAVA_IOTHUBSERVICESASTOKEN_12_006: [The constructor shall concatenate the target uri, the signature, the expiry time and the key name using the format: "SharedAccessSignature sr=%s&sig=%s&se=%s&skn=%s"]
            String token = String.format(TOKEN_FORMAT, targetUri, signature, this.expiryTime, this.keyName);

            return token;
        } catch (Exception e) {
            // Codes_SRS_SERVICE_SDK_JAVA_IOTHUBSERVICESASTOKEN_12_007: [The constructor shall throw Exception if building the token failed]
            throw new RuntimeException(e);
        }
    }

    /**
     * Helper function to calculate token expiry
     *
     * @return Seconds from now to expiry
     */
    private long buildExpiresOn() {
        long expiresOnDate = System.currentTimeMillis();
        expiresOnDate += TOKEN_VALID_SECS * 1000;
        return expiresOnDate / 1000;
    }

    /**
     * Returns the string representation of the SAS token.
     *
     * @return The string representation of the SAS token.
     */
    @Override
    public String toString() {
        // Codes_SRS_SERVICE_SDK_JAVA_IOTHUBSERVICESASTOKEN_12_008: [The constructor shall return with the generated token]
        return this.token;
    }
}