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
);
Related
created the private key with the following command
openssl genrsa -out server.key 2048
In c# console app tried the following:
string keyFile = Path to keyFile
var secret = File.ReadAllText(keyFile);
var key = new SymmetricSecurityKey(System.Text.Encoding.UTF8.GetBytes(secret));
var creds = new SigningCredentials(key, SecurityAlgorithms.RsaSsaPssSha256);
var token = new JwtSecurityToken(
claims: claims, expires:DateTime.Now.AddDays(1), signingCredentials: creds);
token.Header.Add("kid", "7fab807d-4988-4012-8f10-a77655787450");
var jwt = new JwtSecurityTokenHandler().WriteToken(token);
The WriteToken throws the following error message
System.NotSupportedException
HResult=0x80131515
Message=IDX10634: Unable to create the SignatureProvider.
Algorithm: 'PS256', SecurityKey: '[PII of type 'Microsoft.IdentityModel.Tokens.SymmetricSecurityKey' is hidden. For more details, see https://aka.ms/IdentityModel/PII.]'
is not supported. The list of supported algorithms is available here: https://aka.ms/IdentityModel/supported-algorithms
Source=Microsoft.IdentityModel.Tokens
StackTrace:
at Microsoft.IdentityModel.Tokens.CryptoProviderFactory.CreateSignatureProvider(SecurityKey key, String algorithm, Boolean willCreateSignatures, Boolean cacheProvider)
at Microsoft.IdentityModel.Tokens.CryptoProviderFactory.CreateForSigning(SecurityKey key, String algorithm, Boolean cacheProvider)
at Microsoft.IdentityModel.Tokens.CryptoProviderFactory.CreateForSigning(SecurityKey key, String algorithm)
at Microsoft.IdentityModel.JsonWebTokens.JwtTokenUtilities.CreateEncodedSignature(String input, SigningCredentials signingCredentials)
at System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.WriteToken(SecurityToken token)
at JwtConsole.Program.CreateSignedJwtToken() in C:\Projects\Jwt\dotnetcore-jwt-manager\JwtConsole\Program.cs:line 244
at JwtConsole.Program.Main(String[] args) in C:\Projects\Jwt\dotnetcore-jwt-manager\JwtConsole\Program.cs:line 34
Please note very in-experienced developer any help greatly appreciated
Thank you
The exception shows you a list of supported algorithms, see link. The chosen algorithm you used is not supported on non-Windows systems.
You should use a different algorithm, preferably one of the ECDSA algorithms.
It is the line with SigningCredentials - replace the SecurityAlgorithms.RsaSsaPssSha256 by a supported algorithm as per link - selected algorithm must be supported in both .NET Framework and .NET Standard 2.0 frameworks. The modified version of your code is below.
string keyFile = Path to keyFile
var secret = File.ReadAllText(keyFile);
var key = new SymmetricSecurityKey(System.Text.Encoding.UTF8.GetBytes(secret));
var creds = new SigningCredentials(key, SecurityAlgorithms.EcdsaSha256);
var token = new JwtSecurityToken(claims: claims, expires: DateTime.Now.AddDays(1), signingCredentials: creds);
token.Header.Add("kid", "7fab807d-4988-4012-8f10-a77655787450");
var jwt = new JwtSecurityTokenHandler().WriteToken(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.
I am newbie in JWT access Token generation. I have Public Key, Private key and ClientID. I need to generate Client_Assertion.
client_assertion: JWT (signed by client ID, public certificate and private key using
RS256 as the signature algorithm).
I have found some Node.JS code but I want to do it using .Net Framework (Not .Net Core)
Node.Js code can be seen on this link
I need to do it in C#. From where to start and how to achieve it?
I used the .NET libraries
System.IdentityModel.Tokens.Jwt
Microsoft.IdentityModel.Tokens
Microsoft.IdentityModel.JsonWebTokens
I'm also assuming that, as you said you have a private key, and that you've loaded that into an RSACryptoServiceProvider
Here's my sample code
First create your claims. This is your JWT payload
var claims = new Claim[]
{
new Claim(MicrosoftJwt.JwtRegisteredClaimNames.Sub, "your subject"),
new Claim(MicrosoftJwt.JwtRegisteredClaimNames.Iat, DateTime.Now.ToEpochSeconds().ToString(), ClaimValueTypes.Integer),
new Claim(MicrosoftJwt.JwtRegisteredClaimNames.Exp, DateTime.Now.AddMinutes(60).ToEpochSeconds().ToString(), ClaimValueTypes.Integer),
};
Then you need to configure your signing credentials using your private key.
// Assuming you already have your key loaded into an RSACryptoServiceProvider
var key = new MicrosoftTokens.RsaSecurityKey(csp)
var signingCredentials = new SigningCredentials(signingKey, SecurityAlgorithms.RsaSha256);
Now you can create your token
var jwt = new JwtSecurityToken(
issuer : issuer,
audience : audience,
claims : claims,
signingCredentials: signingCredentials
);
// You can add extra items to your Header if necessary
jwt.Header.Add("kid", deviceId);
You can then write your token to a string
var assertion = new JwtSecurityTokenHandler().WriteToken(jwt);
I think the value of assertion is what you're trying to get
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.
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.