Skip to content
This repository has been archived by the owner on Jul 11, 2021. It is now read-only.

Commit

Permalink
Merge pull request #35 from ayiemba/Dev
Browse files Browse the repository at this point in the history
Added SecurityCredential Helper (Takes in certificate and initiator password then returns sec
  • Loading branch information
ayiemba authored Oct 5, 2018
2 parents 92f9e19 + a2b82b4 commit c26e017
Show file tree
Hide file tree
Showing 6 changed files with 333 additions and 4 deletions.
2 changes: 1 addition & 1 deletion build.bat
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ if "%config%" == "" (
set config=Debug
)

set version=2.0.2
set version=2.1.1

if not "%PackageVersion%" == "" (
set version=%PackageVersion%
Expand Down
46 changes: 46 additions & 0 deletions src/MpesaLib/Clients/MpesaClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,11 @@ public async Task<string> GetAuthTokenAsync(string consumerKey, string consumerS
/// <returns></returns>
public async Task<string> MakeB2BPaymentAsync(BusinessToBusiness b2bitem, string accesstoken, string requestUri)
{

#if NET45
ServicePointManager.SecurityProtocol =
SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls;
#endif
_httpclient.DefaultRequestHeaders.Clear();
_httpclient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
_httpclient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accesstoken);
Expand Down Expand Up @@ -156,6 +161,12 @@ public async Task<string> MakeB2BPaymentAsync(BusinessToBusiness b2bitem, string
/// <returns></returns>
public async Task<string> MakeB2CPaymentAsync(BusinessToCustomer b2citem, string accesstoken, string requestUri)
{

#if NET45
ServicePointManager.SecurityProtocol =
SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls;
#endif

_httpclient.DefaultRequestHeaders.Clear();
_httpclient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
_httpclient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accesstoken);
Expand Down Expand Up @@ -197,6 +208,11 @@ public async Task<string> MakeB2CPaymentAsync(BusinessToCustomer b2citem, string
/// <returns></returns>
public async Task<string> MakeC2BPaymentAsync(CustomerToBusinessSimulate c2bsimulate, string accesstoken, string requestUri)
{

#if NET45
ServicePointManager.SecurityProtocol =
SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls;
#endif
_httpclient.DefaultRequestHeaders.Clear();
_httpclient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
_httpclient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accesstoken);
Expand Down Expand Up @@ -232,6 +248,11 @@ public async Task<string> MakeC2BPaymentAsync(CustomerToBusinessSimulate c2bsimu
/// <returns></returns>
public async Task<string> MakeLipaNaMpesaOnlinePaymentAsync(LipaNaMpesaOnline mpesaItem, string accesstoken, string requestUri)
{

#if NET45
ServicePointManager.SecurityProtocol =
SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls;
#endif
_httpclient.DefaultRequestHeaders.Clear();
_httpclient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
_httpclient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accesstoken);
Expand Down Expand Up @@ -295,6 +316,11 @@ public async Task<string> MakeLipaNaMpesaOnlinePaymentAsync(LipaNaMpesaOnline mp
/// <returns></returns>
public async Task<string> QueryAccountBalanceAsync(AccountBalance accbalance, string accesstoken, string requestUri)
{

#if NET45
ServicePointManager.SecurityProtocol =
SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls;
#endif
_httpclient.DefaultRequestHeaders.Clear();
_httpclient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
_httpclient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accesstoken);
Expand Down Expand Up @@ -332,6 +358,11 @@ public async Task<string> QueryAccountBalanceAsync(AccountBalance accbalance, st
/// <returns></returns>
public async Task<string> QueryLipaNaMpesaTransactionAsync(LipaNaMpesaQuery mpesaQuery, string accesstoken, string requestUri)
{

#if NET45
ServicePointManager.SecurityProtocol =
SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls;
#endif
_httpclient.DefaultRequestHeaders.Clear();
_httpclient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
_httpclient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accesstoken);
Expand Down Expand Up @@ -366,6 +397,11 @@ public async Task<string> QueryLipaNaMpesaTransactionAsync(LipaNaMpesaQuery mpes
/// <returns></returns>
public async Task<string> QueryMpesaTransactionStatusAsync(MpesaTransactionStatus transactionStatus, string accesstoken, string requestUri)
{

#if NET45
ServicePointManager.SecurityProtocol =
SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls;
#endif
_httpclient.DefaultRequestHeaders.Clear();
_httpclient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
_httpclient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accesstoken);
Expand Down Expand Up @@ -407,6 +443,11 @@ public async Task<string> QueryMpesaTransactionStatusAsync(MpesaTransactionStatu
/// <returns></returns>
public async Task<string> RegisterC2BUrlAsync(CustomerToBusinessRegister c2bregisterItem, string accesstoken, string requestUri)
{

#if NET45
ServicePointManager.SecurityProtocol =
SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls;
#endif
_httpclient.DefaultRequestHeaders.Clear();
_httpclient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
_httpclient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accesstoken);
Expand Down Expand Up @@ -441,6 +482,11 @@ public async Task<string> RegisterC2BUrlAsync(CustomerToBusinessRegister c2bregi
/// <returns></returns>
public async Task<string> ReverseMpesaTransactionAsync(Reversal reversal, string accesstoken, string requestUri)
{

#if NET45
ServicePointManager.SecurityProtocol =
SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls;
#endif
_httpclient.DefaultRequestHeaders.Clear();
_httpclient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
_httpclient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accesstoken);
Expand Down
89 changes: 89 additions & 0 deletions src/MpesaLib/Helpers/Credentials.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
using System;
using System.IO;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Text;

namespace MpesaLib.Helpers
{

/*
M-Pesa Core authenticates a transaction by decrypting the security credentials.
Security credentials are generated by encrypting the base64 encoded initiator password with M-Pesa’s public key,
a X509 certificate.
The algorithm for generating security credentials is as follows:
Write the unencrypted password into a byte array.
Encrypt the array with the M-Pesa public key certificate. Use the RSA algorithm,
and use PKCS #1.5 padding (not OAEP), and add the result to the encrypted stream.
Convert the resulting encrypted byte array into a string using base64 encoding.
The resulting base64 encoded string is the security credential.
*/


/// <summary>
/// Encrypt password helper class for MpesaLib
/// </summary>
public static class Credentials
{
/// <summary>
/// Encrypts Mpesa API Security Credential password
/// </summary>
/// <param name="certificatePath"></param>
/// <param name="password"></param>
/// <returns></returns>
public static string EncryptPassword(string certificatePath, string password)
{

//get certificate data in bytes
var data = ReadFile(certificatePath);

//var rsa3 = new RSACryptoServiceProvider();

X509Certificate2 x509 = new X509Certificate2(data);

RSA publicprovider = (RSA)x509.PublicKey.Key;

#if !NETSTANDARD2_0
var key1 = publicprovider.ToXmlString(false);
#else
var key1 = publicprovider.ToXmlString2(false);
#endif

var data2 = Encoding.UTF8.GetBytes(password);

using (var rsa2 = new RSACryptoServiceProvider())
{
try
{
rsa2.FromXmlString2(key1);
var encryptedData = rsa2.Encrypt(data2, false);
var base64Encrypted = Convert.ToBase64String(encryptedData);
return base64Encrypted;
}
finally
{
rsa2.PersistKeyInCsp = false;
}
}
}

//Reads a certificate file file.
private static byte[] ReadFile(string fileName)
{
FileStream f = new FileStream(fileName, FileMode.Open, FileAccess.Read);
int size = (int)f.Length;
byte[] data = new byte[size];
size = f.Read(data, 0, size);
f.Close();
return data;
}



}
}
149 changes: 149 additions & 0 deletions src/MpesaLib/Helpers/RSACryptoServiceProviderExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
using System;
using System.Security.Cryptography;
using System.Xml;

namespace MpesaLib.Helpers
{
/// <summary>
/// RSACryptoServiceProvider Extensions
/// </summary>
public static class RSACryptoServiceProviderExtensions
{
/// <summary>
/// Imports the specified XML String into the crypto service provider
/// </summary>
/// <remarks>
/// .NET Core 2.0 doesn't provide an implementation of RSACryptoServiceProvider.FromXmlString/ToXmlString, so we have to do it ourselves.
/// Source: https://gist.github.com/Jargon64/5b172c452827e15b21882f1d76a94be4/
/// </remarks>
public static void FromXmlString2(this RSACryptoServiceProvider rsa, string xmlString)
{
#if !NETSTANDARD2_0
rsa.FromXmlString(xmlString);
#else
FromXmlStringImpl(rsa, xmlString);
#endif
}

internal static void FromXmlStringImpl(RSACryptoServiceProvider rsa, string xmlString)
{
var parameters = new RSAParameters();

var xmlDoc = new XmlDocument();

xmlDoc.LoadXml(xmlString);

if (!xmlDoc.DocumentElement.Name.Equals("RSAKeyValue"))
{
throw new InvalidOperationException("Invalid XML RSA key.");
}


foreach (XmlNode node in xmlDoc.DocumentElement.ChildNodes)
{
switch (node.Name)
{
case "Modulus":
parameters.Modulus = Convert.FromBase64String(node.InnerText);
break;
case "Exponent":
parameters.Exponent = Convert.FromBase64String(node.InnerText);
break;
case "P":
parameters.P = Convert.FromBase64String(node.InnerText);
break;
case "Q":
parameters.Q = Convert.FromBase64String(node.InnerText);
break;
case "DP":
parameters.DP = Convert.FromBase64String(node.InnerText);
break;
case "DQ":
parameters.DQ = Convert.FromBase64String(node.InnerText);
break;
case "InverseQ":
parameters.InverseQ = Convert.FromBase64String(node.InnerText);
break;
case "D":
parameters.D = Convert.FromBase64String(node.InnerText);
break;
default:
throw new InvalidOperationException("Unknown node name: " + node.Name);
}
}

rsa.ImportParameters(parameters);
}



/// <summary>
/// ToXmlString extention method for .net standard, netcoreapp
/// </summary>
/// <param name="rsa"></param>
/// <param name="includePrivateParameters"></param>
/// <returns></returns>
public static void ToXmlString3(this RSA rsa, bool includePrivateParameters = false)
{
#if !NETSTANDARD2_0
rsa.ToXmlString(false);
#else
ToXmlStringImpl(rsa, false);
#endif
}


internal static string ToXmlStringImpl(this RSA rsa, bool includePrivateParameters = false)
{
RSAParameters parameters = rsa.ExportParameters(includePrivateParameters);

if (includePrivateParameters)
{
return string.Format("<RSAKeyValue><Modulus>{0}</Modulus><Exponent>{1}</Exponent><P>{2}</P><Q>{3}</Q><DP>{4}</DP><DQ>{5}</DQ><InverseQ>{6}</InverseQ><D>{7}</D></RSAKeyValue>",
Convert.ToBase64String(parameters.Modulus),
Convert.ToBase64String(parameters.Exponent),
Convert.ToBase64String(parameters.P),
Convert.ToBase64String(parameters.Q),
Convert.ToBase64String(parameters.DP),
Convert.ToBase64String(parameters.DQ),
Convert.ToBase64String(parameters.InverseQ),
Convert.ToBase64String(parameters.D));
}

return string.Format("<RSAKeyValue><Modulus>{0}</Modulus><Exponent>{1}</Exponent></RSAKeyValue>",
Convert.ToBase64String(parameters.Modulus),
Convert.ToBase64String(parameters.Exponent));
}


/// <summary>
/// ToXmlString extention method for .net standard, netcoreapp
/// </summary>
/// <param name="rsa"></param>
/// <param name="includePrivateParameters"></param>
/// <returns></returns>
public static string ToXmlString2(this RSA rsa, bool includePrivateParameters = false)
{
RSAParameters parameters = rsa.ExportParameters(includePrivateParameters);

if (includePrivateParameters)
{
return string.Format("<RSAKeyValue><Modulus>{0}</Modulus><Exponent>{1}</Exponent><P>{2}</P><Q>{3}</Q><DP>{4}</DP><DQ>{5}</DQ><InverseQ>{6}</InverseQ><D>{7}</D></RSAKeyValue>",
Convert.ToBase64String(parameters.Modulus),
Convert.ToBase64String(parameters.Exponent),
Convert.ToBase64String(parameters.P),
Convert.ToBase64String(parameters.Q),
Convert.ToBase64String(parameters.DP),
Convert.ToBase64String(parameters.DQ),
Convert.ToBase64String(parameters.InverseQ),
Convert.ToBase64String(parameters.D));
}

return string.Format("<RSAKeyValue><Modulus>{0}</Modulus><Exponent>{1}</Exponent></RSAKeyValue>",
Convert.ToBase64String(parameters.Modulus),
Convert.ToBase64String(parameters.Exponent));
}

}

}
8 changes: 5 additions & 3 deletions src/MpesaLib/MpesaLib.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<PropertyGroup>
<TargetFrameworks>netstandard2.0;net45;net40;</TargetFrameworks>

<Version>2.0.5</Version>
<Version>2.1.1</Version>

<GeneratePackageOnBuild>false</GeneratePackageOnBuild>

Expand All @@ -15,7 +15,7 @@
<Copyright>Copyright © Geospartan Tech. All rights reserved.</Copyright>
<PackageLicenseUrl>https://github.com/ayiemba/MpesaLib/blob/master/LICENSE</PackageLicenseUrl>
<PackageProjectUrl>https://ayiemba.github.io/MpesaLib</PackageProjectUrl>
<PackageReleaseNotes>MInor Enhancements</PackageReleaseNotes>
<PackageReleaseNotes>Added Security Credential Password Encryption for B2B, B2C, Reversal, Transaction, Account Balance APIs</PackageReleaseNotes>
<IncludeBuildOutput>true</IncludeBuildOutput>
</PropertyGroup>

Expand All @@ -25,7 +25,7 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="11.0.2" />
<PackageReference Include="Newtonsoft.Json" Version="11.0.2" />
</ItemGroup>

<ItemGroup Condition=" '$(TargetFramework)' == 'net45' ">
Expand All @@ -37,6 +37,8 @@
<PackageReference Include="Microsoft.Bcl.Async" Version="1.0.168" />
</ItemGroup>



<PropertyGroup Condition=" '$(TargetFramework)' == 'netstandard2.0'">
<DefineConstants>NETCORE;NETSTANDARD;NETSTANDARD2_0</DefineConstants>
</PropertyGroup>
Expand Down
Loading

0 comments on commit c26e017

Please sign in to comment.