Generate access token to authenticate Azure Active directory App - c#

I am using Azure Active Directory App to authenticate my rest endpoint deployed on Azure.
I was using pfx cert type and below code to generate access token so that my endpoint can be accessed through that access token.
var authority = string.Format(authorityUri, credentialConfigOptions.TenantId);
var authContext = new AuthenticationContext(authority);
X509Certificate2 certificate = default;using (var store = new X509Store(StoreName.My, StoreLocation.CurrentUser, OpenFlags.ReadOnly))
{
var certificateCollection = store.Certificates.Find(X509FindType.FindBySubjectName, credentialConfigOptions.CertificateName, false);
if (certificateCollection.Count > 0)
{
certificate = certificateCollection[0];
}
};
var clientAssertionCertificate = new ClientAssertionCertificate(credentialConfigOptions.AppId, certificate);
AuthenticationResult token = await authContext.AcquireTokenAsync(appId, clientAssertionCertificate);
return token?.AccessToken;
Now I have to use PEM cert type instead of pfx cert type so I am getting issues while converting PEM format to X509Certificate2.
How can I generate access token with PEM certificate?

If you use Net 5.0, we can directly create X509Certificate2 with a cert and key with method X509Certificate2.CreateFromPemFile(<certpath>,<keypath>). For more details, please refer to here.
If you use other versions, we can create an X509Certificate2 with cert file then import private key with method CopyWithPrivateKey. At last we create Certificate with code
new X509Certificate2(pubKey.Export(X509ContentType.Pfx)). For more details, please refer to here.

Related

Creating apple JWT token

Im trying to the Apple App Store Connect API from a .core application.
I currently have
var key = <contents of p8 file>;
var credentials = new SigningCredentials(
new SymmetricSecurityKey(Encoding.ASCII.GetBytes(key)),
SecurityAlgorithms.EcdsaSha256
);
var header = new JwtHeader(credentials);
header.Add("kid", KeyID);
var payload = new JwtPayload
{
{ "aud ", "appstoreconnect-v1"},
{ "exp", exp},
{"iss", issuerID }
};
var secToken = new JwtSecurityToken(header, payload);
var handler = new JwtSecurityTokenHandler();
var tokenString = handler.WriteToken(secToken);
The problem im having is
Unable to create the SignatureProvider.\nAlgorithm: 'System.String', SecurityKey: 'Microsoft.IdentityModel.Tokens.SymmetricSecurityKey'\n is not supported. The list of supported algorithms is available here: https://aka.ms/IdentityModel/supported-algorithms"
The p8 file tha you downaload from apple is unencrypted. Using openssl commands you can convert it to .pem file unencrypted and encrypted. You can also extract the public key.
Personallly I created the pem file containing the private key and I used this file for creating the client secret and then and therefore the jwt which I verified in https://jwt.io
Ecdsa is an asymmetric algorithm, and you are using a symmetric key within your signing credentials. You should either use an asymmetric key with Ecdsa or use a symmetric algorithm with the SymmetricSecurityKey.

IdentityServer4 shared X509 certificate at AWS

I have IdentityServer4 endpoint as AWS Lambda and I need to share signing/validation certificate between lambda instances.
This is a current workarnound:
var identityServer = services.AddIdentityServer(options => {});
if (env.IsDevelopment())
{
identityServer.AddDeveloperSigningCredential();
}
else
{
identityServer.AddSigningCredential(GetIdentityServerCertificate(Configuration));
and
private static X509Certificate2 GetIdentityServerCertificate(
IConfiguration configuration
)
{
var pfxSecret = System.Environment.GetEnvironmentVariable("CERT_PRIVATE");
var pfxBytes = Convert.FromBase64String(pfxSecret);
var certificate = new X509Certificate2(pfxBytes);
return certificate;
}
Does AWS have any product suitable for storing certificates?
Thanks!
You can store private key of a certificate in AWS Secrets Manager.
Here you can find some additional inforamtion how to use AWS Secrets Manager for storing certificates.

retrieve secret from azure key vault

I am not able to retrieve a secret from azure key vault to a .net console app which runs in azure windows VM. Below is the code i have used and i have given service principal all permission in key vault.
var kvc = new KeyVaultClient(new KeyVaultClient.AuthenticationCallback(
async (string authority, string resource, string scope) => {
var authContext = new AuthenticationContext(authority);
var credential = new ClientCredential("App id, "secret identifier uri");
AuthenticationResult result = await authContext.AcquireTokenAsync(resource, credential);
if (result == null) {
throw new InvalidOperationException("Failed to retrieve JWT token");
}
return result.AccessToken;
}
));
Please reference this tutorial in the Microsoft documentation, where you can find the correct way to use Azure Key Vault inside a Windows VM, and using .NET. Note: In this solution, you will use Managed Service Identity, instead of the traditional Service Principal.

How to sign/encrypt JWT in C# with PEM key?

I need to create custom tokens that need to be signed using a key provided by Google. The key is provided as text, like -----BEGIN PRIVATE KEY-----\nMIIE....
I had this working by using BouncyCastle to read the PEM key and get the RSA keys, but now I need this project to run under Linux so I can't use BouncyCastle as it only works under Windows (it makes uses of RSACryptoServiceProvider).
So, is there a way I can achieve the same result but using only .NET Standard code?
I already tried to convert the PEM file to a PFX file, but I don't have any certificates. Tried to get the certificates from here or here didn't work (OpenSSL says that the certificates don't belong to the key provided).
You can convert your PEM file to p12 file and signed your JWT with that p12
var payload = new Dictionary<string, object>()
{
{ "sub", "mr.x#contoso.com" },
{ "exp", 1300819380 }
};
var privateKey=new X509Certificate2("my-key.p12", "password").GetRSAPrivateKey();
string token=Jose.JWT.Encode(payload, privateKey, JwsAlgorithm.RS256);
Here is the details link https://github.com/dvsekhvalnov/jose-jwt#rs--and-ps--family
The PemUtils library will do exactly what you need, it decodes the PEM and DER data without the use of RSACryptoServiceProvider. It's available as a NuGet package and in case you have any additional needs, I'm the author of that library, so feel free to open an issue on github.
Something like this should work:
using (var stream = File.OpenRead(path))
using (var reader = new PemReader(stream))
{
var rsaParameters = reader.ReadRsaKey();
var key = new RsaSecurityKey(RSA.Create(rsaParameters));
var signingCredentials = new SigningCredentials(key, SecurityAlgorithms.RsaSha256);
}
Just to expand on the solution, this is the full code, working (this is for firebase auth tokens):
var privateKey = new X509Certificate2("cert.p12", "password");
var credentials = new SigningCredentials(new X509SecurityKey(privateKey), SecurityAlgorithms.RsaSha256);
var claims = new List<Claim>(8)
{
new Claim(JwtRegisteredClaimNames.Sub, "firebase-adminsdk-vifbe#your-service.iam.gserviceaccount.com"),
};
var tokenHandler = new JwtSecurityTokenHandler();
return tokenHandler.CreateEncodedJwt(
"firebase-adminsdk-vifbe#your-service.iam.gserviceaccount.com",
"https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit",
new ClaimsIdentity(claims),
DateTime.UtcNow.AddSeconds(-60),
DateTime.UtcNow.AddSeconds(10 * 60),
DateTime.UtcNow,
_credentials
);

Office 365 API authentication form REST API

I'm trying to get calendars from Office 365 to use them in a REST API (WEB API 2).
I already tried a lot of stuff to generate the JWT, but for each try, I get another error.
My application is properly registred in Azure AAD, the public key is uploaded.
The last thing I tried was from this article : https://blogs.msdn.microsoft.com/exchangedev/2015/01/21/building-daemon-or-service-apps-with-office-365-mail-calendar-and-contacts-apis-oauth2-client-credential-flow/
In his example, I can generate the JWT from two differents ways, but I get the error : x-ms-diagnostics: 2000003;reason="The audience claim value is invalid 'https://outlook.office365.com'.";error_category="invalid_resource"
Here is my code :
`
string tenantId = ConfigurationManager.AppSettings.Get("ida:TenantId");
/**
* use the tenant specific endpoint for requesting the app-only access token
*/
string tokenIssueEndpoint = "https://login.windows.net/" + tenantId + "/oauth2/authorize";
string clientId = ConfigurationManager.AppSettings.Get("ida:ClientId");
/**
* sign the assertion with the private key
*/
String certPath = System.Web.Hosting.HostingEnvironment.MapPath("~/App_Data/cert.pfx");
X509Certificate2 cert = new X509Certificate2(
certPath,
"lol",
X509KeyStorageFlags.MachineKeySet);
/**
* Example building assertion using Json Tokenhandler.
* Sort of cheating, but just if someone wonders ... there are always more ways to do something :-)
*/
Dictionary<string, string> claims = new Dictionary<string, string>()
{
{ "sub", clientId },
{ "jti", Guid.NewGuid().ToString() },
};
JwtSecurityTokenHandler tokenHandler = new JwtSecurityTokenHandler();
X509SigningCredentials signingCredentials = new X509SigningCredentials(cert, SecurityAlgorithms.RsaSha256Signature, SecurityAlgorithms.Sha256Digest);
JwtSecurityToken selfSignedToken = new JwtSecurityToken(
clientId,
tokenIssueEndpoint,
claims.Select(c => new Claim(c.Key, c.Value)),
DateTime.UtcNow,
DateTime.UtcNow.Add(TimeSpan.FromMinutes(15)),
signingCredentials);
string signedAssertion = tokenHandler.WriteToken(selfSignedToken);
//---- End example with Json Tokenhandler... now to the fun part doing it all ourselves ...
/**
* Example building assertion from scratch with Crypto APIs
*/
JObject clientAssertion = new JObject();
clientAssertion.Add("aud", "https://outlook.office365.com");
clientAssertion.Add("iss", clientId);
clientAssertion.Add("sub", clientId);
clientAssertion.Add("jti", Guid.NewGuid().ToString());
clientAssertion.Add("scp", "Calendars.Read");
clientAssertion.Add("nbf", WebConvert.EpocTime(DateTime.UtcNow + TimeSpan.FromMinutes(-5)));
clientAssertion.Add("exp", WebConvert.EpocTime(DateTime.UtcNow + TimeSpan.FromMinutes(15)));
string assertionPayload = clientAssertion.ToString(Newtonsoft.Json.Formatting.None);
X509AsymmetricSecurityKey x509Key = new X509AsymmetricSecurityKey(cert);
RSACryptoServiceProvider rsa = x509Key.GetAsymmetricAlgorithm(SecurityAlgorithms.RsaSha256Signature, true) as RSACryptoServiceProvider;
RSACryptoServiceProvider newRsa = GetCryptoProviderForSha256(rsa);
SHA256Cng sha = new SHA256Cng();
JObject header = new JObject(new JProperty("alg", "RS256"));
string thumbprint = WebConvert.Base64UrlEncoded(WebConvert.HexStringToBytes(cert.Thumbprint));
header.Add(new JProperty("x5t", thumbprint));
string encodedHeader = WebConvert.Base64UrlEncoded(header.ToString());
string encodedPayload = WebConvert.Base64UrlEncoded(assertionPayload);
string signingInput = String.Concat(encodedHeader, ".", encodedPayload);
byte[] signature = newRsa.SignData(Encoding.UTF8.GetBytes(signingInput), sha);
signedAssertion = string.Format("{0}.{1}.{2}",
encodedHeader,
encodedPayload,
WebConvert.Base64UrlEncoded(signature));
`
My JWT looks like this :
`
{
alg: "RS256",
x5t: "8WkmVEiCU9mHkshRp65lyowGOAk"
}.
{
aud: "https://outlook.office365.com",
iss: "clientId",
sub: "clientId",
jti: "38a34d8a-0764-434f-8e1d-c5774cf37007",
scp: "Calendars.Read",
nbf: 1512977093,
exp: 1512978293
}
`
I put this token in the Authorization header after the "Bearer" string.
Any ideas to solve this kind of issue ? I guess I need a external point of view :)
Thanks
You do not generate the JWT, Azure AD does that.
You would use your certificate to get the access token. Example borrowed from article you linked:
string authority = appConfig.AuthorizationUri.Replace("common", tenantId);
AuthenticationContext authenticationContext = new AuthenticationContext(
authority,
false);
string certfile = Server.MapPath(appConfig.ClientCertificatePfx);
X509Certificate2 cert = new X509Certificate2(
certfile,
appConfig.ClientCertificatePfxPassword, // password for the cert file containing private key
X509KeyStorageFlags.MachineKeySet);
ClientAssertionCertificate cac = new ClientAssertionCertificate(
appConfig.ClientId, cert);
var authenticationResult = await authenticationContext.AcquireTokenAsync(
resource, // always https://outlook.office365.com for Mail, Calendar, Contacts API
cac);
return authenticationResult.AccessToken;
The resulting access token can then be attached to the request to the API.
The reason it does not work is that the Outlook API does not consider you a valid token issuer. It will only accept tokens signed with Azure AD's private key. Which you obviously do not have.
The private key from the key pair you generated can only be used to authenticate your app to Azure AD.
Thanks juunas !
This is the working code :
var authContext = new Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext("https://login.microsoftonline.com/tenantId");
string tenantId = ConfigurationManager.AppSettings.Get("ida:TenantId");
string clientId = ConfigurationManager.AppSettings.Get("ida:ClientId");
String certPath = System.Web.Hosting.HostingEnvironment.MapPath("~/App_Data/cert.pfx");
X509Certificate2 cert = new X509Certificate2(
certPath,
"keyPwd",
X509KeyStorageFlags.MachineKeySet);
ClientAssertionCertificate cac = new ClientAssertionCertificate(clientId, cert);
var result = (AuthenticationResult)authContext
.AcquireTokenAsync("https://outlook.office.com", cac)
.Result;
var token = result.AccessToken;
return token;
Other required step for App-only token, you must use the Grant Permissions button in AAD application settings.

Categories