using System;
using System.Linq;
using System.Text;
using System.Collections.Generic;
using System.Security.Cryptography;
public class TranscribeService
{
private const string k_Service = "transcribe";
private const string k_Path = "/stream-transcription-websocket";
private const string k_Scheme = "AWS4";
private const string k_Algorithm = "HMAC-SHA256";
private const string k_Terminator = "aws4_request";
private const string k_HmacSha256 = "HMACSHA256";
private const string k_Region = "us-east-1";
private const string k_Expiration = "300";
private readonly string _accessKeyId;
private readonly string _sessionToken;
private readonly string _secretKey;
public TranscribeService(string accessKeyId, string sessionToken, string secretKey)
{
_accessKeyId = accessKeyId;
_sessionToken = sessionToken;
_secretKey = secretKey;
}
///
///
///
///
///
///
///
public string GenerateUrl(string languageCode, string mediaEncoding = "pcm", string sampleRate = "16000")
{
var host = $"transcribestreaming.{k_Region}.amazonaws.com:8443";
var dateNow = DateTime.Now.ToUniversalTime();
var dateString = dateNow.ToString("yyyyMMdd");
var dateTimeString = dateNow.ToString("yyyyMMddTHHmmssZ");
var credentialScope = $"{dateString}/{k_Region}/{k_Service}/{k_Terminator}";
var query = GenerateQueryParams(dateTimeString, credentialScope, languageCode, mediaEncoding, sampleRate);
var signature = GenerateSignature(languageCode, host, dateString, dateTimeString, credentialScope);
return $"wss://{host}{k_Path}?{query}&X-Amz-Signature={signature}";
}
///
/// Creates and formats Transcribe URL Parameters
///
/// transcribe formatted DateTime.Now string
/// scope for aws region, service, and terminator
/// transcribe language id (defualt en-US)
/// audio format
/// audio rate
private string GenerateQueryParams(string dateTimeString, string credentialScope, string languageCode, string mediaEncoding = "pcm", string sampleRate = "16000")
{
var credentials = $"{_accessKeyId}/{credentialScope}";
var result = new Dictionary
{
{"X-Amz-Algorithm", $"{k_Scheme}-{k_Algorithm}"},
{"X-Amz-Credential", credentials},
{"X-Amz-Date", dateTimeString},
{"X-Amz-Expires", k_Expiration},
{"X-Amz-Security-Token", _sessionToken},
{"X-Amz-SignedHeaders", "host"},
{"language-code", languageCode},
{"media-encoding", mediaEncoding},
{"sample-rate", sampleRate},
// {"transfer-encoding", "chunked"}
};
return string.Join("&", result.Select(x => $"{x.Key}={Uri.EscapeDataString(x.Value)}"));
}
///
///
///
///
///
///
///
///
private string GenerateSignature(string languageCode, string host, string dateString, string dateTimeString, string credentialScope)
{
var canonicalRequest = CanonicalizeRequest(languageCode, k_Path, host, dateTimeString, credentialScope);
var canonicalRequestHashBytes = GetHash(canonicalRequest);
// construct the string to be signed
var stringToSign = new StringBuilder();
stringToSign.AppendFormat("{0}-{1}\n{2}\n{3}\n", k_Scheme, k_Algorithm, dateTimeString, credentialScope);
stringToSign.Append(ToHex(canonicalRequestHashBytes, true));
var kha = KeyedHashAlgorithm.Create(k_HmacSha256);
kha.Key = GetSigningKey(k_HmacSha256, _secretKey, dateString, k_Service);
// generate the final signature for the request, place into the result
var signature = kha.ComputeHash(Encoding.UTF8.GetBytes(stringToSign.ToString()));
var signatureString = ToHex(signature, true);
return signatureString;
}
private string CanonicalizeRequest(string languageCode, string path, string host, string dateTimeString, string credentialScope)
{
var canonicalRequest = new StringBuilder();
canonicalRequest.AppendFormat("{0}\n", "GET");
canonicalRequest.AppendFormat("{0}\n", path);
canonicalRequest.AppendFormat("{0}\n", GenerateQueryParams(dateTimeString, credentialScope, languageCode));
canonicalRequest.AppendFormat("{0}\n", $"host:{host}");
canonicalRequest.AppendFormat("{0}\n", "");
canonicalRequest.AppendFormat("{0}\n", "host");
canonicalRequest.Append(ToHex(GetHash(""), true));
return canonicalRequest.ToString();
}
private static string ToHex(byte[] data, bool lowercase)
{
var sb = new StringBuilder();
for (var i = 0; i < data.Length; i++)
{
sb.Append(data[i].ToString(lowercase ? "x2" : "X2"));
}
return sb.ToString();
}
private static byte[] GetSigningKey(string algorithm, string awsSecretAccessKey, string date, string service)
{
char[] ksecret = (k_Scheme + awsSecretAccessKey).ToCharArray();
byte[] hashDate = ComputeKeyedHash(algorithm, Encoding.UTF8.GetBytes(ksecret), Encoding.UTF8.GetBytes(date));
byte[] hashRegion = ComputeKeyedHash(algorithm, hashDate, Encoding.UTF8.GetBytes(k_Region));
byte[] hashService = ComputeKeyedHash(algorithm, hashRegion, Encoding.UTF8.GetBytes(service));
return ComputeKeyedHash(algorithm, hashService, Encoding.UTF8.GetBytes(k_Terminator));
}
private static byte[] ComputeKeyedHash(string algorithm, byte[] key, byte[] data)
{
var kha = KeyedHashAlgorithm.Create(algorithm);
kha.Key = key;
return kha.ComputeHash(data);
}
private static byte[] GetHash(string data)
{
return HashAlgorithm.Create("SHA-256").ComputeHash(Encoding.UTF8.GetBytes(data));
}
}