I am trying to get the temporary credentials for Iam role anywhere. I am using the input parameters
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Text;
namespace ConsoleApp1
{
internal class Program
{
static void Main(string[] args)
{
token().GetAwaiter().GetResult();
}
static string HexToDecimal(string hex)
{
List<int> dec = new List<int> { 0 }; // decimal result
foreach (char c in hex)
{
int carry = Convert.ToInt32(c.ToString(), 16);
// initially holds decimal value of current hex digit;
// subsequently holds carry-over for multiplication
for (int i = 0; i < dec.Count; ++i)
{
int val = dec[i] * 16 + carry;
dec[i] = val % 10;
carry = val / 10;
}
while (carry > 0)
{
dec.Add(carry % 10);
carry /= 10;
}
}
var chars = dec.Select(d => (char)('0' + d));
var cArr = chars.Reverse().ToArray();
return new string(cArr);
}
static async Task token()
{
private_key_file = '' # PEM format
public_certificate_file = '' # PEM format
region = '' # AWS Region
duration_seconds = '1200' # From 900 to 3600
profile_arn = ''
role_arn = '' # IAM Role ARN
session_name = 'test_session'
trust_anchor_arn = ''
// ************* REQUEST VALUES *************
string method = "POST";
string service = "rolesanywhere";
string host = $"rolesanywhere.{region}.amazonaws.com";
string endpoint = $"https://rolesanywhere.{region}.amazonaws.com";
string content_type = "application/json";
// Load public certificate
string amz_x509;
string serial_number_dec;
try
{
string pemContents = File.ReadAllText(public_certificate_file);
string[] pemLines = pemContents.Split('\n');
StringBuilder base64String = new StringBuilder();
for (int i = 1; i < pemLines.Length - 1; i++)
{
base64String.Append(pemLines[i]);
}
byte[] certBytes = Convert.FromBase64String(base64String.ToString());
X509Certificate2 cert = new X509Certificate2(certBytes);
var snum = cert.SerialNumber;
serial_number_dec = HexToDecimal(snum);
amz_x509 = Convert.ToBase64String(cert.Export(X509ContentType.Cert));
}
catch (Exception ex)
{
Console.WriteLine($"Failed to load public certificate: {ex.Message}");
return;
}
// Request parameters for CreateSession--passed in a JSON block.
string request_parameters = $"{{\"durationSeconds\": {duration_seconds},\"profileArn\": \"{profile_arn}\",\"roleArn\": \"{role_arn}\",\"sessionName\": \"{session_name}\",\"trustAnchorArn\": \"{trust_anchor_arn}\"}}";
// Create a date for headers and the credential string
DateTime t = DateTime.UtcNow;
string amz_date = t.ToString("yyyyMMddTHHmmssZ");
string date_stamp = t.ToString("yyyyMMdd");
// ************* TASK 1: CREATE A CANONICAL REQUEST *************
// Step 1 is to define the verb (GET, POST, etc.)--already done.
string canonical_uri = "/sessions";
string canonical_querystring = "";
string canonical_headers = "content-type:" + content_type + @"\n" + "host:" + host + @"\n" + "x-amz-date:" + amz_date + @"\n" + "x-amz-x509:" + amz_x509 + @"\n";
string signed_headers = "content-type;host;x-amz-date;x-amz-x509";
string payload_hash = BitConverter.ToString(SHA256.Create().ComputeHash(Encoding.UTF8.GetBytes(request_parameters))).Replace("-", "").ToLower();
string canonical_request = method + @"\n" + canonical_uri + @"\n" + canonical_querystring + @"\n" + canonical_headers + @"\n" + signed_headers + @"\n" + payload_hash;
// ************* TASK 2: CREATE THE STRING TO SIGN*************
string algorithm = "AWS4-X509-RSA-SHA256";
string credential_scope = $"{date_stamp}/{region}/{service}/aws4_request";
string string_to_sign = algorithm + @"\n" + amz_date + @"\n" + credential_scope + @"\n" + BitConverter.ToString(SHA256.Create().ComputeHash(Encoding.UTF8.GetBytes(canonical_request))).Replace("-", "").ToLower();
// ************* TASK 3: CALCULATE THE SIGNATURE *************
byte[] signature;
{
using (var rsa = RSA.Create(8192))
{
var dataToSign = Encoding.UTF8.GetBytes(string_to_sign);
rsa.ImportFromPem(File.ReadAllText(private_key_file));
signature = rsa.SignData(dataToSign, HashAlgorithmName.SHA512, RSASignaturePadding.Pkcs1);
}
}
string signature_hex = BitConverter.ToString(signature).Replace("-", "").ToLower();
// ************* TASK 4: ADD SIGNING INFORMATION TO THE REQUEST *************
string authorization_header = $"{algorithm} Credential={serial_number_dec.ToString()}/{credential_scope}, SignedHeaders={signed_headers}, Signature={signature_hex}";
HttpClient httpClient = new HttpClient();
httpClient.DefaultRequestHeaders.Clear();
httpClient.DefaultRequestHeaders.Add("X-Amz-Date", amz_date);
httpClient.DefaultRequestHeaders.Add("X-Amz-X509", amz_x509);
httpClient.DefaultRequestHeaders.Add("Accept", "application/json");
httpClient.DefaultRequestHeaders.Add("Authorization", authorization_header);
// ************* SEND THE REQUEST *************
Console.WriteLine("\nBEGIN REQUEST++++++++++++++++++++++++++++++++++++");
Console.WriteLine($"Request URL = {endpoint}");
StringContent strContent = new StringContent(request_parameters, Encoding.UTF8, "application/json");
HttpResponseMessage response = await httpClient.PostAsync(endpoint + canonical_uri, strContent);
Console.WriteLine("\nRESPONSE++++++++++++++++++++++++++++++++++++");
Console.WriteLine($"Response code: {response.StatusCode}");
Console.WriteLine(await response.Content.ReadAsStringAsync());
}
}
}
I am using the method below to add Authorization
httpClient.DefaultRequestHeaders.Add("Authorization", authorization_header);
authorization_header value is in this format -> AWS4-X509-RSA-SHA256 Credential={{Certificate_serial_number}}/20240321/{{region}}/rolesanywhere/aws4_request, SignedHeaders=content-type;host;x-amz-date;x-amz-x509, Signature={{signature}}
I am facing problem while adding the Authorization header.
Error message is authorization_header format is invalid.
We developed this code taking refence of python code
which you find using this link https://nerdydrunk.info/aws:roles_anywhere
This python code is working.
In authorization header there should be serial number as per the documentation. Maybe we are doing something wrong while calculating the signature. Can you guide on how to correctly generate and format signature according to the AWS Signature Version 4 requirements in C#? In the code that we shared can you let us know what mistake we are making while computing the signature?