first commit

This commit is contained in:
Zeeshaun
2026-03-11 11:18:25 -05:00
commit f3cb730f3b
472 changed files with 53651 additions and 0 deletions
@@ -0,0 +1,84 @@
using System.Text;
using System.Xml.Linq;
using Microsoft.Extensions.Options;
using Landmark.OLA.Models.OnlineBanking;
using OnlineBankingAuth.Configuration;
using OnlineBankingAuth.Parsers;
namespace OnlineBankingAuth.Services
{
public class DigitalInsightService : IDigitalInsightService
{
private readonly SecuritySettings _settings;
private readonly IAuthTokenParser _parser;
public DigitalInsightService(
IAuthTokenParser parser,
IOptions<SecuritySettings> settings)
{
_parser = parser;
_settings = settings.Value;
}
public OnlineBankingFormData PopulateOnlineBankingDataFromConfig(OnlineBankingFormData data)
{
data.Authorization = "Basic " +
Convert.ToBase64String(
Encoding.UTF8.GetBytes(
_settings.DigitalInsightConsumerKey + ":" + _settings.DigitalInsightConsumerSecret
)
);
data.DI_FIID = _settings.DigitalInsightFIID;
data.DI_TID = (Guid.NewGuid()).ToString();
return data;
}
private IEnumerable<KeyValuePair<string, string>> BuildKeyValuePair(OnlineBankingFormData data)
{
return new Dictionary<string, string>
{
{"grant_type", data.GrantType },
{"username", data.Username },
{"password", data.Password }
};
}
public async Task<AuthTokenResponse> LoginUser(OnlineBankingFormData data)
{
try
{
using (var hc = new HttpClient())
{
var formData = BuildKeyValuePair(data);
using (var content = new FormUrlEncodedContent(formData))
{
content.Headers.Clear();
content.Headers.Add("Content-Type", "application/x-www-form-urlencoded");
HttpContent httpContent = new StringContent(await content.ReadAsStringAsync(), Encoding.UTF8, "application/x-www-form-urlencoded");
var httpRequest = new HttpRequestMessage(HttpMethod.Post, _settings.DigitalInsightPostTokenURI);
httpRequest.Content = httpContent;
httpRequest.Headers.Add("Authorization", data.Authorization);
httpRequest.Headers.Add("di_fiid", data.DI_FIID);
httpRequest.Headers.Add("di_tid", data.DI_TID);
var response = hc.SendAsync(httpRequest);
var xdoc = XDocument.Parse(await response.Result.Content.ReadAsStringAsync());
var xel = XElement.Parse(xdoc.ToString());
var authTokenResponse = _parser.ParseAuthTokenResponse(xel);
return authTokenResponse;
}
}
}
catch (Exception)
{
throw;
}
}
}
}
@@ -0,0 +1,143 @@
using System.Security.Cryptography;
using System.Security.Authentication;
using System.Xml;
using System.Text;
using System.Security.Cryptography.X509Certificates;
using Microsoft.Extensions.Options;
using OnlineBankingAuth.Configuration;
using OnlineBankingAuth.Services;
using OnlineBankingAuth.Parsers;
using Landmark.OLA.Models.OnlineBanking;
namespace OnlineBankingAuth.Communication
{
public class DigitalInsightVendorClient : IDigitalInsightVendorClient
{
private readonly ISAMLParser<DISAMLAssertion> _samlParser;
private readonly SecuritySettings _settings;
private readonly IDigitalInsightService _serivce;
public DigitalInsightVendorClient(
ISAMLParser<DISAMLAssertion> samlParser,
IOptions<SecuritySettings> settings,
IDigitalInsightService service)
{
_samlParser = samlParser;
_settings = settings.Value;
_serivce = service;
}
public bool ValidateDIPublicKey(XmlElement x509)
{
var vendorTrustedCert = GetPublicKeyFromStore();
var innerText = x509.InnerText;
byte[] payloadCertBytes = Encoding.Default.GetBytes(innerText);
var payloadCert = new X509Certificate2(payloadCertBytes);
return vendorTrustedCert.GetPublicKeyString().Equals(payloadCert.GetPublicKeyString());
// @TODO: Assert other equalities besides the "publicKeyString"?
}
public DISAMLAssertion GetSAMLPayload(XmlDocument xmlDocument)
{
var lmcuPrivateCertificate = GetPrivateKeyFromStore();
// The "EncryptedAsserion" block is the only acceptable block we can use to construct SAML2.Saml20EncryptedAssertion
var encryptedXml = (XmlElement)xmlDocument.GetElementsByTagName("saml:EncryptedAssertion")[0];
var encryptedXmlDoc = new XmlDocument();
encryptedXmlDoc.LoadXml(encryptedXml.OuterXml);
// SAML2 library does the decrypting work
var privateKeyAlgorithm = (RSA)lmcuPrivateCertificate.GetRSAPrivateKey();
var encryptedAssertion = new SAML2.Saml20EncryptedAssertion(privateKeyAlgorithm, encryptedXmlDoc);
encryptedAssertion.Decrypt();
// The decrypted document should be signed by DI. The Vendor Public Key is used to assert that this is the case.
var vendorPublicKey = GetPublicKeyFromStore().GetRSAPublicKey();
var checkSignature = SAML2.Utils.XmlSignatureUtils.CheckSignature(encryptedAssertion.Assertion, vendorPublicKey);
if (!checkSignature)
{
throw new AuthenticationException("Signature does not match therefore the decrypted payload cannot be accepted.");
}
var decryptedAssertion = _samlParser.ParseAssertion(encryptedAssertion.Assertion.DocumentElement);
return decryptedAssertion;
}
public bool ValidateTimeStamp(DISAMLAssertion assertion)
{
return 0 <= DateTime.Compare(assertion.Conditions.NotAfter, DateTime.Now);
}
public string GetSuccessRedirectUrl()
{
return _settings.SAMLSuccessRedirectRoute;
}
public string GetFailureRedirectUrl(string message)
{
var plainTextBytes = System.Text.Encoding.UTF8.GetBytes(message);
var encodedMessage = System.Convert.ToBase64String(plainTextBytes);
return _settings.SAMLFailureRedirectRoute + "&msg=" + encodedMessage;
}
private X509Certificate2 GetPrivateKeyFromStore()
{
// FOR TESTING LOCAL #1345. Note: the second argument is the password for the secure cert file
//return new X509Certificate2("Controllers" + Path.DirectorySeparatorChar + "dev.pfx", "Nvisia", X509KeyStorageFlags.Exportable);
///*
var certStore = new X509Store(StoreName.My, StoreLocation.CurrentUser);
certStore.Open(OpenFlags.ReadOnly);
var certCollection = certStore.Certificates.Find(X509FindType.FindByThumbprint, _settings.PrivateCertAzureThumbprint, false);
X509Certificate2 lmcuPrivateCertificate;
if (certCollection.Count == 0)
{
throw new AuthenticationException("Could not GET private key from PrivateCertAzureThumbprint");
}
// Should only have 1 cert with that thumbprint
lmcuPrivateCertificate = certCollection[0];
return lmcuPrivateCertificate;
//*/
}
private X509Certificate2 GetPublicKeyFromStore()
{
// FOR TESTING LOCAL #1345
//return new X509Certificate2("Controllers" + Path.DirectorySeparatorChar + "pfd-sign.qal1signingcert2018");
///*
var certStore = new X509Store(StoreName.My, StoreLocation.CurrentUser);
certStore.Open(OpenFlags.ReadOnly);
var certCollection = certStore.Certificates.Find(X509FindType.FindByThumbprint, _settings.PublicCertAzureThumbprint, false);
X509Certificate2 vendorTrustedCert;
if (certCollection.Count == 0)
{
throw new AuthenticationException("Could not GET public key from PublicCertAzureThumbprint");
}
// Should only have 1 cert with that thumbprint
vendorTrustedCert = certCollection[0];
return vendorTrustedCert;
//*/
}
public async Task<AuthTokenResponse> AuthenticateOnlineBankingAccount(OnlineBankingFormData data)
{
var onlineBankingData = _serivce.PopulateOnlineBankingDataFromConfig(data);
return await _serivce.LoginUser(onlineBankingData);
}
}
}
@@ -0,0 +1,10 @@
using Landmark.OLA.Models.OnlineBanking;
namespace OnlineBankingAuth.Services
{
public interface IDigitalInsightService
{
Task<AuthTokenResponse> LoginUser(OnlineBankingFormData data);
OnlineBankingFormData PopulateOnlineBankingDataFromConfig(OnlineBankingFormData data);
}
}
@@ -0,0 +1,16 @@
using System.Xml;
using Landmark.OLA.Models.OnlineBanking;
namespace OnlineBankingAuth.Services
{
public interface IDigitalInsightVendorClient
{
bool ValidateDIPublicKey(XmlElement x509);
DISAMLAssertion GetSAMLPayload(XmlDocument xmlDocument);
bool ValidateTimeStamp(DISAMLAssertion assertion);
string GetSuccessRedirectUrl();
string GetFailureRedirectUrl(string message);
Task<AuthTokenResponse> AuthenticateOnlineBankingAccount(OnlineBankingFormData data);
};
}