Created
June 5, 2021 13:48
-
-
Save dbardouh/5e23ce80a6b62830c4dc8445316b18d6 to your computer and use it in GitHub Desktop.
Get Authentication Token from Anaplan Private/Public Keys for REST API calls
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | |
} | |
} | |
} |
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\n400 Bad Request
\r\ncloudflare\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
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.