Skip to content

Instantly share code, notes, and snippets.

@YannickRe
Created August 27, 2020 22:45
Show Gist options
  • Select an option

  • Save YannickRe/46a4d7b7f8c4f2ce21de7c206d307ab5 to your computer and use it in GitHub Desktop.

Select an option

Save YannickRe/46a4d7b7f8c4f2ce21de7c206d307ab5 to your computer and use it in GitHub Desktop.
using Dalion.HttpMessageSigning.Signing;
using Dalion.HttpMessageSigning;
using Microsoft.IdentityModel.Tokens;
using Newtonsoft.Json;
using System;
using System.Net.Http.Headers;
using System.Net.Http;
using System.Security.Cryptography.X509Certificates;
using System.Security.Cryptography;
using System.Threading.Tasks;
using System.Collections.Generic;
using System.Net;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
namespace Services
{
internal class MagdaAccessToken
{
internal MagdaAccessToken()
{
this.TokenDate = DateTime.Now;
}
[JsonProperty("access_token")]
public string AccessToken { get; set; }
public DateTime ExpireDate
{
get
{
return this.TokenDate.AddSeconds(this.ExpiresIn);
}
}
public DateTime TokenDate { get; private set; }
[JsonProperty("expires_in")]
public int ExpiresIn { get; set; }
[JsonProperty("token_type")]
public string TokenType { get; set; }
[JsonProperty("scope")]
public string Scope { get; set; }
}
public class MagdaTokenBasedServicesExample
{
private readonly string _keyId = "rsa-key-1";
private readonly string _clientId = "1234"; //TODO: Provide your client id
private readonly string _authUrl = "https://beta.oauth.vlaanderen.be/authorization/ws/oauth/v2/token";
private readonly string _messagesEndpointUrl = "https://iv.api.tni-vlaanderen.be/api/v1/messages/messages";
private MagdaAccessToken _accessToken;
private X509Certificate2 _clientCertificate;
private IRequestSignerFactory _requestSignerFactory;
private HttpClient _httpClient;
private JwtSecurityTokenHandler _tokenHandler;
public MagdaTokenBasedServicesExample(IRequestSignerFactory requestSignerFactory)
{
this._requestSignerFactory = requestSignerFactory;
this._clientCertificate = null; // TODO: Load your VO-DCB client certificate (pfx)
var httpClientHandler = new HttpClientHandler();
httpClientHandler.ClientCertificates.Add(this._clientCertificate);
this._httpClient = new HttpClient(httpClientHandler);
this._tokenHandler = new JwtSecurityTokenHandler();
}
public async Task<HttpResponseMessage> SendMessage()
{
if (this._accessToken == null || (this._accessToken != null && this._accessToken.ExpireDate <= DateTime.Now.AddMinutes(1)))
this._accessToken = await this.GetAccessToken();
var request = new HttpRequestMessage
{
Method = HttpMethod.Post,
RequestUri = new Uri(this._messagesEndpointUrl)
};
await SignHttpRequestMessage(request);
// Add additional payload properties here
// Like MessageToSend
return await this._httpClient.SendAsync(request);
}
private async Task SignHttpRequestMessage(HttpRequestMessage request)
{
request.Headers.Add("signature-public-key", WrapCertificateInJWK(this._clientCertificate, _keyId));
request.Headers.Date = DateTimeOffset.UtcNow;
SigningSettings settings = new SigningSettings
{
SignatureAlgorithm = SignatureAlgorithm.CreateForSigning(this._clientCertificate, HashAlgorithmName.SHA512),
DigestHashAlgorithm = HashAlgorithmName.SHA512,
EnableNonce = false,
Headers = new[]
{
new HeaderName("signature-public-key")
},
UseDeprecatedAlgorithmParameter = true
};
var requestSigner = this._requestSignerFactory.Create(_keyId, settings);
await requestSigner.Sign(request);
request.Headers.Add("Signature", request.Headers.Authorization.Parameter);
request.Headers.Add("x-correlation-id", Guid.NewGuid().ToString());
request.Headers.Add("Accept", "application/json");
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", this._accessToken.AccessToken);
}
private string WrapCertificateInJWK(X509Certificate2 certificate, string keyName)
{
var x509SigningKey = new X509SigningCredentials(certificate);
var x509Key = new X509SecurityKey(certificate);
var cert64 = Convert.ToBase64String(x509Key.Certificate.RawData);
var pubKey = x509Key.PublicKey as RSA;
var parameters = pubKey.ExportParameters(false);
var exponent = Convert.ToBase64String(parameters.Exponent);
var modulus = Convert.ToBase64String(parameters.Modulus);
var webKey = new JsonWebKey
{
Kty = "RSA",
Use = "sig",
Kid = keyName,
X5t = x509Key.Certificate.Thumbprint,
E = exponent,
N = modulus,
Alg = x509SigningKey.Algorithm
};
webKey.X5c.Add(cert64);
var simplifiedWebKey = new { kty = webKey.Kty, kid = webKey.Kid, n = webKey.N, e = webKey.E, x5c = webKey.X5c };
return JsonConvert.SerializeObject(simplifiedWebKey);
}
private async Task<MagdaAccessToken> GetAccessToken()
{
var request = new HttpRequestMessage
{
Method = HttpMethod.Post,
RequestUri = new Uri(this._authUrl)
};
request.Headers.Add("Accept", "application/json");
request.Headers.Add("Accept-Encoding", "gzip, deflate, br");
List<string> scopes = new List<string>() {
"msg_mailbox_v1_P",
"msg_msg_v1_P",
"msg_statuses_v1_P"
};
Dictionary<string, string> postParams = new Dictionary<string, string>();
postParams.Add("grant_type", "client_credentials");
postParams.Add("scope", string.Join(" ", scopes));
postParams.Add("client_assertion_type", "urn:ietf:params:oauth:client-assertion-type:jwt-bearer");
postParams.Add("client_assertion", this.GetAuthJwtToken(string.Join(" ", scopes)));
request.Content = new FormUrlEncodedContent(postParams);
var response = await this._httpClient.SendAsync(request);
var responseContent = await response.Content.ReadAsStringAsync();
switch (response.StatusCode)
{
case HttpStatusCode.OK:
return JsonConvert.DeserializeObject<MagdaAccessToken>(responseContent);
default:
throw new Exception($"HTTP call GetToken failed with status code {response.StatusCode} and message {responseContent}");
}
}
private string GetAuthJwtToken(string scopes)
{
X509SecurityKey privateKey = new X509SecurityKey(this._clientCertificate);
ClaimsIdentity claimsIdentity = new ClaimsIdentity();
claimsIdentity.AddClaim(new Claim("sub", this._clientId));
claimsIdentity.AddClaim(new Claim("scope", scopes));
claimsIdentity.AddClaim(new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()));
return this._tokenHandler.CreateEncodedJwt(this._clientId, this._authUrl, claimsIdentity, null, DateTime.UtcNow.AddMinutes(10), DateTime.UtcNow, new SigningCredentials(privateKey, SecurityAlgorithms.RsaSha256Signature));
}
}
}
@brz
Copy link
Copy Markdown

brz commented May 6, 2021

Dank voor dit stukje code! Ik zat even vast bij de message signing.

@fdervishi90
Copy link
Copy Markdown

fdervishi90 commented Jun 8, 2021

Can you please share how you instantiate the MagdaTokenBasedServicesExample class. More specifically how do you build the IRequestSignerFactory dependency on the contructor ?

@brz
Copy link
Copy Markdown

brz commented Jun 8, 2021

Can you please share how you instantiate the MagdaTokenBasedServicesExample class. More specifically how do you build the IRequestSignerFactory dependency on the contructor ?

Currently the only way of instantiating an IRequestSignerFactory object is by using dependency injection. I opened an issue on the GitHub page of Dalion.HttpMessageSigning to improve this.

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