speech

134

speech -

public class PresignedUrlController : ControllerBase
{
	private const string Service = "transcribe";
	private const string Path = "/stream-transcription-webasocket";
	private const string Scheme = "AWS4";
	private const string Algorithm = "HMAC-SHA256";
	private const string Terminator = "aws4_request";
	private const string HmacSha256 = "HMACSHA256";

	private readonly string _region;
	private readonly string _awsAccessKey;
	private readonly string _awsSecretKey;

	public PresignedUrlController(IOptions<Config> options)
	{
		_region = options.Value.AWS_SPEECH_REGION;
		_awsAccessKey = options.Value.AWS_SPEECH_ACCESS_KEY;
		_awsSecretKey = options.Value.AWS_SPEECH_SECRET_KEY;
	}

	[HttpPost]
	public ActionResult<string> GetPresignedUrl()
	{
		return GenerateUrl();
	}

	private string GenerateUrl()
	{
		var host = $"transcribestreaming.{_region}.amazonaws.com:8443";
		var dateNow = DateTime.UtcNow;
		var dateString = dateNow.ToString("yyyyMMdd");
		var dateTimeString = dateNow.ToString("yyyyMMddTHHmmssZ");
		var credentialScope = $"{dateString}/{_region}/{Service}/{Terminator}";
		var query = GenerateQueryParams(dateTimeString, credentialScope);
		var signature = GetSignature(host, dateString, dateTimeString, credentialScope);
		return $"wss://{host}{Path}?{query}&X-Amz-Signature={signature}";
	}

	private string GenerateQueryParams(string dateTimeString, string credentialScope)
	{
		var credentials = $"{_awsAccessKey}/{credentialScope}";
		var result = new Dictionary<string, string>
		{
			{"X-Amz-Algorithm", "AWS4-HMAC-SHA256"},
			{"X-Amz-Credential", credentials},
			{"X-Amz-Date", dateTimeString},
			{"X-Amz-Expires", "30"},
			{"X-Amz-SignedHeaders", "host"},
			{"language-code", "en-US"},
			{"media-encoding", "pcm"},
			{"sample-rate", "44100"}
		};
		return string.Join("&", result.Select(x => $"{x.Key}={Uri.EscapeDataString(x.Value)}"));
	}

	private string GetSignature(string host, string dateString, string dateTimeString, string credentialScope)
	{
		var canonicalRequest = CanonicalizeRequest(Path, host, dateTimeString, credentialScope);
		var canonicalRequestHashBytes = ComputeHash(canonicalRequest);

		// construct the string to be signed
		var stringToSign = new StringBuilder();
		stringToSign.AppendFormat("{0}-{1}\n{2}\n{3}\n", Scheme, Algorithm, dateTimeString, credentialScope);
		stringToSign.Append(ToHexString(canonicalRequestHashBytes, true));

		var kha = KeyedHashAlgorithm.Create(HmacSha256);
		kha.Key = DeriveSigningKey(HmacSha256, _awsSecretKey, _region, dateString, Service);

		// compute the final signature for the request, place into the result and return to the 
		// user to be embedded in the request as needed
		var signature = kha.ComputeHash(Encoding.UTF8.GetBytes(stringToSign.ToString()));
		var signatureString = ToHexString(signature, true);
		return signatureString;
	}

	private string CanonicalizeRequest(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));
		canonicalRequest.AppendFormat("{0}\n", $"host:{host}");
		canonicalRequest.AppendFormat("{0}\n", "");
		canonicalRequest.AppendFormat("{0}\n", "host");
		canonicalRequest.Append(ToHexString(ComputeHash(""), true));
		return canonicalRequest.ToString();
	}

	private static string ToHexString(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[] DeriveSigningKey(string algorithm, string awsSecretAccessKey, string region, string date, string service)
	{
		char[] ksecret = (Scheme + awsSecretAccessKey).ToCharArray();
		byte[] hashDate = ComputeKeyedHash(algorithm, Encoding.UTF8.GetBytes(ksecret), Encoding.UTF8.GetBytes(date));
		byte[] hashRegion = ComputeKeyedHash(algorithm, hashDate, Encoding.UTF8.GetBytes(region));
		byte[] hashService = ComputeKeyedHash(algorithm, hashRegion, Encoding.UTF8.GetBytes(service));
		return ComputeKeyedHash(algorithm, hashService, Encoding.UTF8.GetBytes(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[] ComputeHash(string data)
	{
		return HashAlgorithm.Create("SHA-256").ComputeHash(Encoding.UTF8.GetBytes(data));
	}
}

Comments

Submit
0 Comments