Skip to content

Instantly share code, notes, and snippets.

@dbardouh
Created June 5, 2021 13:48
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dbardouh/5e23ce80a6b62830c4dc8445316b18d6 to your computer and use it in GitHub Desktop.
Save dbardouh/5e23ce80a6b62830c4dc8445316b18d6 to your computer and use it in GitHub Desktop.
Get Authentication Token from Anaplan Private/Public Keys for REST API calls
public async static Task<string> GetAnaplanAuthToken(string publicKeyFileLocation, string privateKeyFileLocation)
{
var publickeyText = File.ReadAllText(publicKeyFileLocation);
var publicKey = publickeyText.Replace("-----BEGIN CERTIFICATE-----", "")
.Replace("-----END CERTIFICATE-----", "")
.Replace("\n", "");
var certText = File.ReadAllText(privateKeyFileLocation);
var xml = PemToXml(certText);
Random random = new Random();
const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
var randomStr = new string(Enumerable.Repeat(chars, 100)
.Select(s => s[random.Next(s.Length)]).ToArray());
var randomStrByteArr = Encoding.ASCII.GetBytes(randomStr);
var encoded_string = Convert.ToBase64String(randomStrByteArr);
var data = Convert.FromBase64String(encoded_string);
var rsaAlg = new RSACryptoServiceProvider();
rsaAlg.FromXmlString(xml);
var signed = rsaAlg.SignData(data, SHA512.Create());
var encoded_signed_string = Convert.ToBase64String(signed);
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11;
HttpClient httpClient = new HttpClient();
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("CACertificate", publicKey);
var body = new
{
encodedData = encoded_string,
encodedSignedData = encoded_signed_string
};
var json = JsonConvert.SerializeObject(body);
HttpResponseMessage message = await httpClient.PostAsync($"https://auth.anaplan.com/token/authenticate",
new StringContent(json, Encoding.UTF8, "application/json"));
string response = await message.Content.ReadAsStringAsync();
var authenticationResponse = JsonConvert.DeserializeObject<dynamic>(response);
var token = authenticationResponse.tokenInfo.tokenValue.ToString();
return token;
}
public static string PemToXml(string pem)
{
if (pem.StartsWith("-----BEGIN RSA PRIVATE KEY-----")
|| pem.StartsWith("-----BEGIN PRIVATE KEY-----"))
{
return GetXmlRsaKey(pem, obj =>
{
if ((obj as RsaPrivateCrtKeyParameters) != null)
{
var parms = DotNetUtilities.ToRSAParameters((RsaPrivateCrtKeyParameters)obj);
var rsa = RSA.Create();
rsa.ImportParameters(parms);
return rsa;
}
var keyPair = (AsymmetricCipherKeyPair)obj;
var parms1 = DotNetUtilities.ToRSAParameters((RsaPrivateCrtKeyParameters)keyPair.Private);
var rsa1 = RSA.Create();
rsa1.ImportParameters(parms1);
return rsa1;
}, rsa => rsa.ToXmlString(true));
}
if (pem.StartsWith("-----BEGIN PUBLIC KEY-----"))
{
return GetXmlRsaKey(pem, obj =>
{
var publicKey = (RsaKeyParameters)obj;
return DotNetUtilities.ToRSA(publicKey);
}, rsa => rsa.ToXmlString(false));
}
throw new InvalidKeyException("Unsupported PEM format...");
}
private static string GetXmlRsaKey(string pem, Func<object, RSA> getRsa, Func<RSA, string> getKey)
{
using (var ms = new MemoryStream())
using (var sw = new StreamWriter(ms))
using (var sr = new StreamReader(ms))
{
sw.Write(pem);
sw.Flush();
ms.Position = 0;
var pr = new PemReader(sr);
object keyPair = pr.ReadObject();
using (RSA rsa = getRsa(keyPair))
{
var xml = getKey(rsa);
return xml;
}
}
}
@dbardouh
Copy link
Author

dbardouh commented Jun 5, 2021

This code will work in .NET and dotnet core 3.1 LTS onwards. I believe changes have been made in dotnet core 5 to make dealing with RSA keys much easier, but this should be fine.

It relies on BouncyCastle in order to use the private key to sign the data.

This is essentially a .NET implementation of what is required here and is (hopefully) an effective implementation of this

Note: I have ommitted a bit of my other code for brevity and readability, but please add your own exception handling if you want to use this gist.

@parsam75
Copy link

Hi Dbardoun,

Thanks for your post. Recently I am using this code in the Azure function to connect Anaplan via certificate authentication, I'll get the following error.

\r\n<title>400 Bad Request</title>\r\n\r\n

400 Bad Request

\r\n
cloudflare\r\n\r\n\r\n"

do you have any insight?

Thanks.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment