一般的な概念

この記事では、Azure Notification Hubs REST API を使用するアプリケーションを開発するときの一般的な概念について説明します。

注意

より高いレベルのセキュリティを確保するために、Notification Hubs では 2020 年 4 月 30 日に TLS バージョン 1.0 および 1.1 のサポートを無効にします。 詳細については、Azure Notification Hubs ドキュメントの トランスポート層セキュリティ (TLS) に関するページを参照してください。

接続文字列を解析する

通知ハブにアクセスするには、ハブ 接続文字列の 2 つの情報が必要です。 接続文字列には、ハブのエンドポイントに関する情報と、それにアクセスするために使用されるセキュリティ資格情報が含まれます (SAS の場合は、ルール名とキー値が含まれます)。

次のコードは、接続文字列を解析して関連情報を抽出します。

public partial class ConnectionStringUtility
{
    public string Endpoint { get; private set; }
    public string SasKeyName { get; private set; }
    public string SasKeyValue { get; private set; }

    public ConnectionStringUtility(string connectionString)
    {
        //Parse Connectionstring
        char[] separator = { ';' };
        string[] parts = connectionString.Split(separator);
        for (int i = 0; i < parts.Length; i++)
        {
            if (parts[i].StartsWith("Endpoint"))
                Endpoint = "https" + parts[i].Substring(11);
            if (parts[i].StartsWith("SharedAccessKeyName"))
                SasKeyName = parts[i].Substring(20);
            if (parts[i].StartsWith("SharedAccessKey"))
                SasKeyValue = parts[i].Substring(16);
        }
    }
}
var parts = connectionString.split(';');
if (parts.length != 3)
throw "Error parsing connection string";

parts.forEach(function(part) {
if (part.indexOf('Endpoint') == 0) {
endpoint = 'https' + part.substring(11);
} else if (part.indexOf('SharedAccessKeyName') == 0) {
sasKeyName = part.substring(20);
} else if (part.indexOf('SharedAccessKey') == 0) {
sasKeyValue = part.substring(16);
}
});

SAS セキュリティ トークンを作成する

SAS を使用して認証するには、クライアントが要求の Authorization ヘッダーに SAS トークンを指定する必要があります。 トークンは、接続文字列から抽出された情報と、認証する必要がある現在の要求から構築されます。 トークンの形式は次のとおりです。

SharedAccessSignature sig=<signature-string>&se=<expiry>&skn=<keyName>&sr=<URL-encoded-resourceURI>

トークンは keyName を参照します (通知を送信するには、通常、すべての通知ハブで自動的に作成される DefaultFullSharedAccessSignature プロパティを使用します)。

SAS トークンの署名は、承認規則の PrimaryKey プロパティを使用して、文字列から署名までの値の HMAC-SHA256 を使用して計算されます。 string-to-sign 値は、リソース URI と有効期限で構成され、次のように書式設定されます。

StringToSign = <resourceURI> + "\n" + expiry;

この操作には、エンコードされていないリソース URI を使用します。 リソース URI とは、アクセスが要求される Service Bus リソースの完全な URI です。 フォームは次のとおりです。

http://<namespace>.servicebus.windows.net/<hubName>

例:

http://contoso.servicebus.windows.net/myHub

有効期限は 1970 年 1 月 1 日の 00 時 00 分 00 秒 UTC からのエポック秒で表されます。

署名に使用される共有アクセス承認規則は、この URI で指定されたエンティティで構成する必要があります。 前の例では、URI は http://contoso.servicebus.windows.net/myHub または http://contoso.servicebus.windows.netです。

URL エンコードされた resourceURI は、署名の計算中に文字列から署名に使用される URI と同じである必要があります。 パーセントエンコードと小文字にする必要があります。

次のコードは、要求 URI を指定して、SAS トークンを作成します。 Java バージョンでは Apache Commons Codec が使用され、Javascript バージョンでは CryptoJS が使用されます。

public partial class ConnectionStringUtility
{
    public string getSaSToken(string uri, int minUntilExpire)
    {
        string targetUri = Uri.EscapeDataString(uri.ToLower()).ToLower();

        // Add an expiration in seconds to it.
        long expiresOnDate = DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond;
        expiresOnDate += minUntilExpire * 60 * 1000;
        long expires_seconds = expiresOnDate / 1000;
        String toSign = targetUri + "\n" + expires_seconds;

        // Generate a HMAC-SHA256 hash or the uri and expiration using your secret key.
        MacAlgorithmProvider macAlgorithmProvider = MacAlgorithmProvider.OpenAlgorithm(MacAlgorithmNames.HmacSha256);
        BinaryStringEncoding encoding = BinaryStringEncoding.Utf8;
        var messageBuffer = CryptographicBuffer.ConvertStringToBinary(toSign, encoding);
        IBuffer keyBuffer = CryptographicBuffer.ConvertStringToBinary(SasKeyValue, encoding);
        CryptographicKey hmacKey = macAlgorithmProvider.CreateKey(keyBuffer);
        IBuffer signedMessage = CryptographicEngine.Sign(hmacKey, messageBuffer);

        string signature = Uri.EscapeDataString(CryptographicBuffer.EncodeToBase64String(signedMessage));

        return "SharedAccessSignature sr=" + targetUri + "&sig=" + signature + "&se=" + expires_seconds + "&skn=" + SasKeyName;
    }
}
var getSelfSignedToken = function(targetUri, sharedKey, ruleId,
expiresInMins) {
targetUri = encodeURIComponent(targetUri.toLowerCase()).toLowerCase();

// Set expiration in seconds
var expireOnDate = new Date();
expireOnDate.setMinutes(expireOnDate.getMinutes() + expiresInMins);
var expires = Date.UTC(expireOnDate.getUTCFullYear(), expireOnDate
.getUTCMonth(), expireOnDate.getUTCDate(), expireOnDate
.getUTCHours(), expireOnDate.getUTCMinutes(), expireOnDate
.getUTCSeconds()) / 1000;
var tosign = targetUri + '\n' + expires;

// using CryptoJS
var signature = CryptoJS.HmacSHA256(tosign, sharedKey);
var base64signature = signature.toString(CryptoJS.enc.Base64);
var base64UriEncoded = encodeURIComponent(base64signature);

// construct autorization string
var token = "SharedAccessSignature sr=" + targetUri + "&sig="
+ base64UriEncoded + "&se=" + expires + "&skn=" + ruleId;
// console.log("signature:" + token);
return token;
};