com.microsoft.azure.iothub.auth.SignatureHelper.java Source code

Java tutorial

Introduction

Here is the source code for com.microsoft.azure.iothub.auth.SignatureHelper.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.iothub.auth;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Base64;

/** Builds the authorization signature as a composition of functions. */
public final class SignatureHelper {
    /**
     * The device ID will be the prefix. The expiry time, as a UNIX
     * timestamp, will be the suffix.
     */
    public static final String RAW_SIGNATURE_FORMAT = "%s\n%s";

    /** The charset used for the raw and hashed signature. */
    public static final Charset SIGNATURE_CHARSET = StandardCharsets.UTF_8;

    /**
     * Builds the raw signature.
     *
     * @param resourceUri the resource URI.
     * @param expiryTime the signature expiry time, as a UNIX timestamp.
     *
     * @return the raw signature.
     */
    public static byte[] buildRawSignature(String resourceUri, long expiryTime) {
        // Codes_SRS_SIGNATUREHELPER_11_001: [The function shall initialize the message being encoded as "<scope>\n<expiryTime>".]
        // Codes_SRS_SIGNATUREHELPER_11_002: [The function shall decode the message using the charset UTF-8.]
        return String.format(RAW_SIGNATURE_FORMAT, resourceUri, expiryTime).getBytes(SIGNATURE_CHARSET);
    }

    /**
     * Decodes the deviceKey using Base64.
     *
     * @param deviceKey the device key.
     *
     * @return the Base64-decoded device key.
     */
    public static byte[] decodeDeviceKeyBase64(String deviceKey) {
        // Codes_SRS_SIGNATUREHELPER_11_003: [The function shall decode the device key using Base64.]
        return Base64.decodeBase64(deviceKey.getBytes());
    }

    /**
     * Encrypts the signature using HMAC-SHA256.
     *
     * @param sig the unencrypted signature.
     * @param deviceKey the Base64-decoded device key.
     *
     * @return the HMAC-SHA256 encrypted signature.
     */
    public static byte[] encryptSignatureHmacSha256(byte[] sig, byte[] deviceKey) {
        String hmacSha256 = "HmacSHA256";

        // Codes_SRS_SIGNATUREHELPER_11_005: [The function shall use the device key as the secret for the algorithm.]
        SecretKeySpec secretKey = new SecretKeySpec(deviceKey, hmacSha256);

        byte[] encryptedSig = null;
        try {
            // Codes_SRS_SIGNATUREHELPER_11_004: [The function shall encrypt the signature using the HMAC-SHA256 algorithm.]
            Mac hMacSha256 = Mac.getInstance(hmacSha256);
            hMacSha256.init(secretKey);
            encryptedSig = hMacSha256.doFinal(sig);
        } catch (NoSuchAlgorithmException e) {
            // should never happen, since the algorithm is hard-coded.
        } catch (InvalidKeyException e) {
            // should never happen, since the input key type is hard-coded.
        }

        return encryptedSig;
    }

    /**
     * Encodes the signature using Base64 and then further
     * encodes the resulting string using UTF-8 encoding.
     *
     * @param sig the HMAC-SHA256 encrypted signature.
     *
     * @return the Base64-encoded signature.
     */
    public static byte[] encodeSignatureBase64(byte[] sig) {
        // Codes_SRS_SIGNATUREHELPER_11_006: [The function shall encode the signature using Base64.]
        return Base64.encodeBase64(sig);
    }

    /**
     * Encodes the signature using charset UTF-8.
     *
     * @param sig the HMAC-SHA256 encrypted, Base64-encoded signature.
     *
     * @return the signature encoded using charset UTF-8.
     */
    public static String encodeSignatureUtf8(byte[] sig) {
        // Codes_SRS_SIGNATUREHELPER_11_010: [The function shall encode the signature using charset UTF-8.]
        return new String(sig, SIGNATURE_CHARSET);
    }

    /**
     * Safely escapes characters in the signature so that they can be
     * transmitted over the internet. Replaces unsafe characters with a '%'
     * followed by two hexadecimal digits (i.e. %2d).
     *
     * @param sig the HMAC-SHA256 encrypted, Base64-encoded, UTF-8 encoded
     * signature.
     *
     * @return the web-safe encoding of the signature.
     */
    public static String encodeSignatureWebSafe(String sig) {
        String strSig = "";
        try {
            // Codes_SRS_SIGNATUREHELPER_11_007: [The function shall replace web-unsafe characters in the signature with a '%' followed by two hexadecimal digits, where the hexadecimal digits are determined by the UTF-8 charset.]
            // Codes_SRS_SIGNATUREHELPER_11_008: [The function shall replace spaces with '+' signs.]
            strSig = URLEncoder.encode(sig, SIGNATURE_CHARSET.name());
        } catch (UnsupportedEncodingException e) {
            // should never happen, since the encoding is hard-coded.
            throw new IllegalStateException(e);
        }

        return strSig;
    }

    protected SignatureHelper() {
    }
}