diff --git a/.vs/ApiSecuritySolution/xs/UserPrefs.xml b/.vs/ApiSecuritySolution/xs/UserPrefs.xml new file mode 100644 index 0000000..b3fbf7b --- /dev/null +++ b/.vs/ApiSecuritySolution/xs/UserPrefs.xml @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.vs/ApiSecuritySolution/xs/sqlite3/db.lock b/.vs/ApiSecuritySolution/xs/sqlite3/db.lock new file mode 100644 index 0000000..e69de29 diff --git a/.vs/ApiSecuritySolution/xs/sqlite3/storage.ide b/.vs/ApiSecuritySolution/xs/sqlite3/storage.ide new file mode 100644 index 0000000..d15f853 Binary files /dev/null and b/.vs/ApiSecuritySolution/xs/sqlite3/storage.ide differ diff --git a/.vs/ApiSecuritySolution/xs/sqlite3/storage.ide-shm b/.vs/ApiSecuritySolution/xs/sqlite3/storage.ide-shm new file mode 100644 index 0000000..e7a9a4e Binary files /dev/null and b/.vs/ApiSecuritySolution/xs/sqlite3/storage.ide-shm differ diff --git a/.vs/ApiSecuritySolution/xs/sqlite3/storage.ide-wal b/.vs/ApiSecuritySolution/xs/sqlite3/storage.ide-wal new file mode 100644 index 0000000..ce6292b Binary files /dev/null and b/.vs/ApiSecuritySolution/xs/sqlite3/storage.ide-wal differ diff --git a/ApiUtilLib/ApiAuthorization.cs b/ApiUtilLib/ApiAuthorization.cs index 65a7fad..fefe627 100644 --- a/ApiUtilLib/ApiAuthorization.cs +++ b/ApiUtilLib/ApiAuthorization.cs @@ -4,6 +4,18 @@ using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; using System.Text; +using ApexUtilLib; +using System.Collections.Generic; +using Newtonsoft.Json.Converters; +using Newtonsoft.Json; +using System.Linq; +using Org.BouncyCastle; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.OpenSsl; +using Org.BouncyCastle.Crypto.Encodings; +using Org.BouncyCastle.Crypto.Engines; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Security; namespace ApiUtilLib { @@ -101,12 +113,11 @@ public static RSACryptoServiceProvider PrivateKeyFromP12(string certificateFileN { Logger.LogEnterExit(LoggerBase.Args(certificateFileName, "***password***")); + var privateCert = new X509Certificate2(System.IO.File.ReadAllBytes(certificateFileName), password, X509KeyStorageFlags.Exportable); var OriginalPrivateKey = (RSACryptoServiceProvider)privateCert.PrivateKey; - // Transfer the private key to overcome the following error... - // System.Security.Cryptography.CryptographicException "Invalid algorithm specified" if (Environment.OSVersion.Platform == PlatformID.MacOSX || Environment.OSVersion.Platform == PlatformID.Unix) { return OriginalPrivateKey; @@ -120,6 +131,127 @@ public static RSACryptoServiceProvider PrivateKeyFromP12(string certificateFileN } } + public static string GetL2SignatureFromPEM(string filename, string message, string passPhrase) + { + Logger.LogEnterExit(LoggerBase.Args(filename, "***password***")); + string result = null; + try + { + using (FileStream fs = File.OpenRead(filename)) + { + AsymmetricCipherKeyPair keyPair; + var obj = GetRSAProviderFromPem(File.ReadAllText(filename).Trim(), passPhrase); + byte[] bytes = Encoding.UTF8.GetBytes(message); + + using (var reader = File.OpenText(filename)) + keyPair = (AsymmetricCipherKeyPair)new PemReader(reader, new PasswordFinder(passPhrase)).ReadObject(); + var decryptEngine = new Pkcs1Encoding(new RsaEngine()); + + decryptEngine.Init(false, keyPair.Private); + var str = obj.SignData(bytes, CryptoConfig.MapNameToOID("SHA256")); + + result = System.Convert.ToBase64String(str); + } + } + catch (Exception ex) + { + throw ex; + } + return result; + } + + public static RSACryptoServiceProvider ImportPrivateKey(string pem) + { + PemReader pr = new PemReader(new StringReader(pem)); + AsymmetricCipherKeyPair KeyPair = (AsymmetricCipherKeyPair)pr.ReadObject(); + RSAParameters rsaParams = DotNetUtilities.ToRSAParameters((RsaPrivateCrtKeyParameters)KeyPair.Private); + + RSACryptoServiceProvider csp = new RSACryptoServiceProvider(); + csp.ImportParameters(rsaParams); + return csp; + } + + + public static X509Certificate2 LoadCertificateFile(string filename, string passPhrase) + { + X509Certificate2 x509 = null; + try + { + using (FileStream fs = File.OpenRead(filename)) + { + AsymmetricCipherKeyPair keyPair; + var obj = GetRSAProviderFromPem(File.ReadAllText(filename).Trim(), passPhrase); + byte[] bytes = Encoding.UTF8.GetBytes("message"); + + using (var reader = File.OpenText(filename)) + keyPair = (AsymmetricCipherKeyPair)new PemReader(reader, new PasswordFinder(passPhrase)).ReadObject(); + var decryptEngine = new Pkcs1Encoding(new RsaEngine()); + + decryptEngine.Init(false, keyPair.Private); + var str = obj.SignData(bytes, CryptoConfig.MapNameToOID("SHA256")); + + var base64 = System.Convert.ToBase64String(str); + + var privateCert = new X509Certificate2(base64, passPhrase, X509KeyStorageFlags.Exportable); + } + } + catch (Exception ex) + { + throw ex; + } + return x509; + } + + + public static RSACryptoServiceProvider GetRSAProviderFromPem(String pemstr, string password) + { + CspParameters cspParameters = new CspParameters(); + cspParameters.KeyContainerName = "MyKeyContainer"; + RSACryptoServiceProvider rsaKey = new RSACryptoServiceProvider(cspParameters); + + Func MakePublicRCSP = (RSACryptoServiceProvider rcsp, RsaKeyParameters rkp) => + { + RSAParameters rsaParameters = DotNetUtilities.ToRSAParameters(rkp); + rcsp.ImportParameters(rsaParameters); + return rsaKey; + }; + + Func MakePrivateRCSP = (RSACryptoServiceProvider rcsp, RsaPrivateCrtKeyParameters rkp) => + { + RSAParameters rsaParameters = DotNetUtilities.ToRSAParameters(rkp); + rcsp.ImportParameters(rsaParameters); + return rsaKey; + }; + IPasswordFinder pwd; + PemReader reader; + reader = new PemReader(new StringReader(pemstr), new PasswordFinder(password)); + object kp = reader.ReadObject(); + + if (kp.GetType().GetProperty("Private") != null) + { + return MakePrivateRCSP(rsaKey, (RsaPrivateCrtKeyParameters)(((AsymmetricCipherKeyPair)kp).Private)); + } + else + { + return MakePublicRCSP(rsaKey, (RsaKeyParameters)kp); + } + + + } + + + + public static byte[] PEM(string type, byte[] data) + { + string pem = Encoding.ASCII.GetString(data); + string header = String.Format("-----BEGIN {0}-----", type); + string footer = String.Format("-----END {0}-----", type); + int start = pem.IndexOf(header) + header.Length; + int end = pem.IndexOf(footer, start); + string base64 = pem.Substring(start, (end - start)); + return Convert.FromBase64String(base64); + } + public static RSACryptoServiceProvider PublicKeyFromCer(string certificateFileName) { Logger.LogEnterExit(LoggerBase.Args(certificateFileName)); @@ -159,57 +291,69 @@ string authPrefix , string timestamp , string version) { - Logger.LogEnter(LoggerBase.Args(authPrefix, signatureMethod, appId, siteUri, httpMethod, formList, nonce, timestamp)); - - authPrefix = authPrefix.ToLower(); - - // make sure that the url are valid - if (siteUri.Scheme != "http" && siteUri.Scheme != "https") + try { - throw new System.NotSupportedException("Support http and https protocol only."); - } + Logger.LogEnter(LoggerBase.Args(authPrefix, signatureMethod, appId, siteUri, httpMethod, formList, nonce, timestamp)); - // make sure that the port no and querystring are remove from url - var url = string.Format("{0}://{1}{2}", siteUri.Scheme, siteUri.Host, siteUri.AbsolutePath); - Logger.LogInformation("url:: {0}", url); + authPrefix = authPrefix.ToLower(); - // helper calss that handle parameters and form fields - ApiList paramList = new ApiList(); + // make sure that the url are valid + if (siteUri.Scheme != "http" && siteUri.Scheme != "https") + { + throw new System.NotSupportedException("Support http and https protocol only."); + } - // process QueryString from url by transfering it to paramList - if (siteUri.Query.Length > 1) - { - var queryString = siteUri.Query.Substring(1); // remove the ? from first character - Logger.LogInformation("queryString:: {0}", queryString); + // make sure that the port no and querystring are remove from url + var url = string.Format("{0}://{1}{2}", siteUri.Scheme, siteUri.Host, siteUri.AbsolutePath); + Logger.LogInformation("url:: {0}", url); - var paramArr = queryString.Split('&'); - foreach (string item in paramArr) - { - var itemArr = item.Split('='); - paramList.Add(itemArr[0], System.Net.WebUtility.UrlDecode(itemArr[1])); - } + // helper calss that handle parameters and form fields + ApiList paramList = new ApiList(); - Logger.LogInformation("paramList:: {0}", paramList); - } + // process QueryString from url by transfering it to paramList + if (siteUri.Query.Length > 1) + { + var queryString = siteUri.Query.Substring(1); // remove the ? from first character + Logger.LogInformation("queryString:: {0}", queryString); + + var paramArr = queryString.Split('&'); + foreach (string item in paramArr) + { + string key = null; + string val = null; + var itemArr = item.Split('='); + key = itemArr[0]; + if(itemArr.Length>1) + val = itemArr[1]; + paramList.Add(key, System.Net.WebUtility.UrlDecode(val)); + } + + Logger.LogInformation("paramList:: {0}", paramList); + } - // add the form fields to paramList - if (formList != null && formList.Count > 0) - { - paramList.AddRange(formList); - } + // add the form fields to paramList + if (formList != null && formList.Count > 0) + { + paramList.AddRange(formList); + } - paramList.Add(authPrefix + "_timestamp", timestamp); - paramList.Add(authPrefix + "_nonce", nonce); - paramList.Add(authPrefix + "_app_id", appId); - paramList.Add(authPrefix + "_signature_method", signatureMethod.ToString()); - paramList.Add(authPrefix + "_version", version); + paramList.Add(authPrefix + "_timestamp", timestamp); + paramList.Add(authPrefix + "_nonce", nonce); + paramList.Add(authPrefix + "_app_id", appId); + paramList.Add(authPrefix + "_signature_method", signatureMethod.ToString()); + paramList.Add(authPrefix + "_version", version); - string baseString = httpMethod.ToString() + "&" + url + "&" + paramList.ToString(); + string baseString = httpMethod.ToString() + "&" + url + "&" + paramList.ToString(); - Logger.LogDebug("BaseString:: {0}", baseString); + Logger.LogDebug("BaseString:: {0}", baseString); - Logger.LogExit(LoggerBase.Args(baseString)); - return baseString; + Logger.LogExit(LoggerBase.Args(baseString)); + return baseString; + } + catch (Exception ex) + { + throw ex; + } } public static long NewTimestamp() @@ -228,12 +372,12 @@ public static string NewNonce() { // Buffer storage. data = new byte[8]; - // Fill buffer. rng.GetBytes(data); } Logger.LogEnterExit(LoggerBase.Args(nonce.ToString())); + return System.Convert.ToBase64String(data); } @@ -283,14 +427,14 @@ string realm var tokenList = new ApiList(); tokenList.Add("realm", realm); - tokenList.Add(authPrefix + "_timestamp", timestamp); - tokenList.Add(authPrefix + "_nonce", nonce); tokenList.Add(authPrefix + "_app_id", appId); + tokenList.Add(authPrefix + "_nonce", nonce); tokenList.Add(authPrefix + "_signature_method", signatureMethod.ToString()); + tokenList.Add(authPrefix + "_timestamp", timestamp); tokenList.Add(authPrefix + "_version", version); tokenList.Add(authPrefix + "_signature", base64Token); - string authorizationToken = string.Format("{0} {1}", authPrefix.Substring(0, 1).ToUpperInvariant() + authPrefix.Substring(1), tokenList.ToString(",", false, true)); + string authorizationToken = string.Format("{0} {1}", authPrefix.Substring(0, 1).ToUpperInvariant() + authPrefix.Substring(1), tokenList.ToString(", ", false, true)); Logger.LogDebug("Token :: {0}", authorizationToken); @@ -426,5 +570,24 @@ public static void InitiateSSLTrust() Console.WriteLine("{0}", ex); } } + + private class PasswordFinder : IPasswordFinder + { + private string password; + + public PasswordFinder(string password) + { + this.password = password; + } + + + public char[] GetPassword() + { + return password.ToCharArray(); + } + } + } + + } diff --git a/ApiUtilLib/ApiList.cs b/ApiUtilLib/ApiList.cs index 253f8a4..9b5e86d 100644 --- a/ApiUtilLib/ApiList.cs +++ b/ApiUtilLib/ApiList.cs @@ -1,6 +1,8 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Collections; +using ApexUtilLib; namespace ApiUtilLib { @@ -27,13 +29,12 @@ public string ToString(string delimiter = "&", bool sort = true, bool quote = fa // sort by key, than by value var sortedList = this.OrderBy(k => k.Key,StringComparer.Ordinal).ThenBy(v => v.Value,StringComparer.Ordinal); //Fixed issue to sort by capital letter. - foreach (var item in sortedList) { format = "{0}={1}"; if (quote) format = "{0}=\"{1}\""; - if (item.Value == null && !quote) + if (item.Value.IsNullOrEmpty() && !quote) { list.Add(string.Format("{0}", item.Key, item.Value)); } @@ -45,7 +46,7 @@ public string ToString(string delimiter = "&", bool sort = true, bool quote = fa } else { - foreach (var item in this) + foreach (var item in this) { list.Add(string.Format(format, item.Key, item.Value)); } diff --git a/ApiUtilLib/ApiUtilLib.csproj b/ApiUtilLib/ApiUtilLib.csproj index c0c1f76..c821bfc 100644 --- a/ApiUtilLib/ApiUtilLib.csproj +++ b/ApiUtilLib/ApiUtilLib.csproj @@ -29,6 +29,12 @@ + + ..\packages\Newtonsoft.Json.11.0.2\lib\net45\Newtonsoft.Json.dll + + + ..\packages\BouncyCastle.1.8.3\lib\BouncyCastle.Crypto.dll + @@ -42,6 +48,10 @@ + + + + \ No newline at end of file diff --git a/ApiUtilLib/CommonExtensions.cs b/ApiUtilLib/CommonExtensions.cs new file mode 100644 index 0000000..d1af409 --- /dev/null +++ b/ApiUtilLib/CommonExtensions.cs @@ -0,0 +1,54 @@ +using System; +using System.Text; + +namespace ApexUtilLib +{ + public static class CommonExtensions + { + public static bool IsNullOrEmpty(this string value) + { + if (value == null) + return true; + + if (value == String.Empty) + return true; + + return false; + } + + public static string Unescape(this string txt) + { + if (string.IsNullOrEmpty(txt)) { return txt; } + StringBuilder retval = new StringBuilder(txt.Length); + for (int ix = 0; ix < txt.Length;) + { + int jx = txt.IndexOf('\n', ix); + if (jx < 0 || jx == txt.Length - 1) jx = txt.Length; + retval.Append(txt, ix, jx - ix); + if (jx >= txt.Length) break; + var str = txt[jx + 1]; + switch (txt[jx + 1]) + { + case 'n': retval.Append('\n'); break; // Line feed + case 'r': retval.Append('\r'); break; // Carriage return + case 't': retval.Append('\t'); break; // Tab + case '\\': retval.Append('\\'); break; // Don't escape + default: // Unrecognized, copy as-is + retval.Append('\\').Append(txt[jx + 1]); break; + } + ix = jx + 2; + } + return retval.ToString(); + } + + public static string RemoveString(this string value, string[] array) + { + foreach (var item in array) + { + value = value.Replace(item, ""); + } + + return value; + } + } +} diff --git a/ApiUtilLib/packages.config b/ApiUtilLib/packages.config new file mode 100644 index 0000000..3fe4864 --- /dev/null +++ b/ApiUtilLib/packages.config @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/ApiUtilLibTest/ApiUtilLibTest.csproj b/ApiUtilLibTest/ApiUtilLibTest.csproj index 8c0254c..08b53bc 100644 --- a/ApiUtilLibTest/ApiUtilLibTest.csproj +++ b/ApiUtilLibTest/ApiUtilLibTest.csproj @@ -1,5 +1,6 @@ + Debug AnyCPU @@ -27,8 +28,12 @@ + + ..\packages\Newtonsoft.Json.11.0.2\lib\net45\Newtonsoft.Json.dll + + - ..\packages\NUnit.3.8.1\lib\net45\nunit.framework.dll + ..\packages\NUnit.3.10.1\lib\net45\nunit.framework.dll @@ -36,6 +41,11 @@ + + + + + diff --git a/ApiUtilLibTest/BaseService.cs b/ApiUtilLibTest/BaseService.cs new file mode 100644 index 0000000..60749a2 --- /dev/null +++ b/ApiUtilLibTest/BaseService.cs @@ -0,0 +1,135 @@ +using ApexUtilLib; +using ApiUtilLib; +using System; +using System.Collections.Generic; +using System.IO; +using System.Security.Cryptography; +using System.Text; + +namespace ApexUtilLibTest +{ + public class BaseService + { + internal string testDataPath = @"/Users/nsearch/OneDrive/Projects/GovTech/testData/"; + internal string testCertPath = @"/Users/nsearch/OneDrive/Projects/GovTech/"; + + internal ApiUtilLib.SignatureMethod signatureMethod { get; set; } + internal ApiUtilLib.HttpMethod httpMethod { get; set; } + internal ApiList apiList { get; set; } + internal string timeStamp { get; set; } + internal string version { get; set; } + internal string nonce { get; set; } + internal string authPrefix { get; set; } + internal string testId { get; set; } + internal string appId { get; set; } + internal Uri signatureURL { get; set; } + internal string expectedResult { get; set; } + internal bool errorTest { get; set; } + internal string[] skipTest { get; set; } + internal string realm { get; set; } + internal Uri invokeUrl { get; set; } + internal string secret { get; set; } + internal string passphrase { get; set; } + + public BaseService() + { + } + + internal void SetDetaultParams(TestParam paramFile) + { + try + { + signatureMethod = paramFile.apiParam.signatureMethod.ParseSignatureMethod(paramFile.apiParam.secret); + httpMethod = paramFile.apiParam.httpMethod.ToEnum(); + apiList = new ApiList(); + SetApiList(paramFile.apiParam.formData); + SetApiList(paramFile.apiParam.queryString); + timeStamp = paramFile.apiParam.timestamp; + if (paramFile.apiParam.timestamp.IsNullOrEmpty()) + timeStamp = Convert.ToInt32(DateTimeOffset.UtcNow.ToUnixTimeSeconds().ToString(), 10).ToString(); + version = paramFile.apiParam.version ?? "1.0"; + nonce = paramFile.apiParam.nonce ?? ApiUtilLib.ApiAuthorization.NewNonce(); + authPrefix = paramFile.apiParam.authPrefix; + appId = paramFile.apiParam.appID; + testId = paramFile.id; + signatureURL = paramFile.apiParam.signatureURL.IsNullOrEmpty() == true ? null : new System.Uri(paramFile.apiParam.signatureURL); + expectedResult = CommonExtensions.GetCharp(paramFile.expectedResult); + errorTest = paramFile.errorTest; + skipTest = paramFile.skipTest; + invokeUrl = paramFile.apiParam.invokeURL.IsNullOrEmpty() == true ? null : new System.Uri(paramFile.apiParam.invokeURL); + secret = paramFile.apiParam.secret ?? null; + realm = paramFile.apiParam.realm ?? null; + passphrase = paramFile.apiParam.passphrase;// ?? "passwordp12"; + } + catch (Exception ex) + { + throw ex; + } + } + + internal void SetApiList(Dictionary data = null) + { + try + { + if (data != null) + { + foreach (var item in data) + { + var key = item.Key ?? ""; + var value = item.Value ?? ""; + + if (value.ToString().StartsWith("{", StringComparison.InvariantCulture) && value.ToString().EndsWith("}", StringComparison.InvariantCulture)) + value = ""; + + string[] _subArry = { "" }; + string val = null; + if (!value.ToString().IsNullOrEmpty()) + { + val = value.ToString().Trim().RemoveString(new string[] { "[", "]", "\\", "\\ ", " \\", "\"", "\\ ", "\n", " " }).Unescape(); + _subArry = val.Split(','); + } + + foreach (var subArry in _subArry) + { + var _val = subArry; + if (_val == "True") + _val = "true"; + if (_val == "False") + _val = "false"; + + if (!key.ToString().IsNullOrEmpty()) + apiList.Add(key.ToString(), _val); + } + } + } + } + catch (Exception ex) + { + throw ex; + } + } + + internal IEnumerable + GetJsonFile(string fileName) + { + string path = testDataPath + fileName; + + TestDataService service = new TestDataService(); + var jsonData = service.LoadTestFile(path); + + return jsonData; + } + + + public static byte[] PEM(string type, byte[] data) + { + string pem = Encoding.ASCII.GetString(data); + string header = String.Format("-----BEGIN {0}-----", type); + string footer = String.Format("-----END {0}-----", type); + int start = pem.IndexOf(header) + header.Length; + int end = pem.IndexOf(footer, start); + string base64 = pem.Substring(start, (end - start)); + return Convert.FromBase64String(base64); + } + } +} diff --git a/ApiUtilLibTest/BaseStringTest.cs b/ApiUtilLibTest/BaseStringTest.cs index f22c324..60e4df9 100644 --- a/ApiUtilLibTest/BaseStringTest.cs +++ b/ApiUtilLibTest/BaseStringTest.cs @@ -1,6 +1,10 @@ using NUnit.Framework; - +using System.Linq; using ApiUtilLib; +using ApexUtilLibTest; +using System.Collections.Generic; +using System.Reflection; +using System; namespace ApiUtilLibTest { @@ -12,7 +16,7 @@ public class BaseStringTest public void BaseString_Basic_Test() { var url = "https://test.example.com:443/api/v1/rest/level1/in-in/?ap=裕廊坊%20心邻坊"; - var expectedBaseString = "GET&https://test.example.com/api/v1/rest/level1/in-in/&ap=裕廊坊 心邻坊&auth_prefix_app_id=app-id-lpX54CVNltS0ye03v2mQc0b&auth_prefix_nonce=1355584618267440511&auth_prefix_signature_method=HMACSHA256&auth_prefix_timestamp=1502175057654&auth_prefix_version=1.0"; + var expectedBaseString = "GET&https://test.example.com/api/v1/rest/level1/in-in/&ap=裕廊坊 心邻坊&auth_prefix_app_id=app-id-lpX54CVNltS0ye03v2mQc0b&auth_prefix_nonce=1355584618267440511&auth_prefix_signature_method=HMACSHA256&auth_prefix_timestamp=1502175057654&auth_prefix_version=1.0"; var baseString = ApiAuthorization.BaseString( "auth_prefix", @@ -29,64 +33,98 @@ public void BaseString_Basic_Test() Assert.AreEqual(expectedBaseString, baseString); } - [Test] - public void BaseString_FormData_Test() - { - var url = "https://test.example.com:443/api/v1/rest/level1/in-in/?ap=裕廊坊%20心邻坊"; - var expectedBaseString = "POST&https://test.example.com/api/v1/rest/level1/in-in/&ap=裕廊坊 心邻坊&auth_prefix_app_id=app-id-lpX54CVNltS0ye03v2mQc0b&auth_prefix_nonce=6584351262900708156&auth_prefix_signature_method=HMACSHA256&auth_prefix_timestamp=1502184161702&auth_prefix_version=1.0¶m1=data1"; + [Test] + public void BaseString_BugTest() + { + + var formData = new ApiUtilLib.ApiList(); - var formList = new ApiList(); - formList.Add("param1", "data1"); + formData.Add("Action", "SendMessage"); + formData.Add("MessageBody", "[{}]"); - var baseString = ApiAuthorization.BaseString( - "auth_prefix", - SignatureMethod.HMACSHA256, - "app-id-lpX54CVNltS0ye03v2mQc0b", - new System.Uri(url), + var url = "https://test.example.com:443/api/v1/rest/level1/in-in/?ap=裕廊坊%20心邻坊"; + var expectedBaseString = "GET&https://test.example.com/api/v1/rest/level1/in-in/&ap=裕廊坊 心邻坊&auth_prefix_app_id=app-id-lpX54CVNltS0ye03v2mQc0b&auth_prefix_nonce=1355584618267440511&auth_prefix_signature_method=HMACSHA256&auth_prefix_timestamp=1502175057654&auth_prefix_version=1.0"; + + var baseString = ApiAuthorization.BaseString( + "auth_prefix", + SignatureMethod.HMACSHA256, + "app-id-lpX54CVNltS0ye03v2mQc0b", + new System.Uri(url), + HttpMethod.GET, + formData, + "1355584618267440511", + "1502175057654", + "1.0" + ); + + + //Console.WriteLine("\n>>>BaseString :: '{0}'<<<", baseString); + + // Console.WriteLine("\n---Lab 3.2---"); + Assert.AreEqual(expectedBaseString, baseString); + } + + [Test] + public void BaseString_FormData_Test() + { + var url = "https://test.example.com:443/api/v1/rest/level1/in-in/?ap=裕廊坊%20心邻坊"; + var expectedBaseString = "POST&https://test.example.com/api/v1/rest/level1/in-in/&ap=裕廊坊 心邻坊&auth_prefix_app_id=app-id-lpX54CVNltS0ye03v2mQc0b&auth_prefix_nonce=6584351262900708156&auth_prefix_signature_method=HMACSHA256&auth_prefix_timestamp=1502184161702&auth_prefix_version=1.0¶m1=data1"; + + var formList = new ApiList(); + formList.Add("param1", "data1"); + + var baseString = ApiAuthorization.BaseString( + "auth_prefix", + SignatureMethod.HMACSHA256, + "app-id-lpX54CVNltS0ye03v2mQc0b", + new System.Uri(url), HttpMethod.POST, - formList, - "6584351262900708156", - "1502184161702", - "1.0" - ); + formList, + "6584351262900708156", + "1502184161702", + "1.0" + ); - Assert.AreEqual(expectedBaseString, baseString); - } + Assert.AreEqual(expectedBaseString, baseString); + } - [Test] - public void BaseString_Invalid_Url_01_Test() - { - var url = "ftp://test.example.com:443/api/v1/rest/level1/in-in/?ap=裕廊坊%20心邻坊"; + [Test] + public void BaseString_Invalid_Url_01_Test() + { + var url = "ftp://test.example.com:443/api/v1/rest/level1/in-in/?ap=裕廊坊%20心邻坊"; Assert.Throws(() => ApiAuthorization.BaseString( - "auth_prefix", - SignatureMethod.HMACSHA256, - "app-id-lpX54CVNltS0ye03v2mQc0b", - new System.Uri(url), - HttpMethod.POST, - null, - "6584351262900708156", - "1502184161702", - "1.0" - )); - } - - [Test] - public void BaseString_Invalid_Url_02_Test() - { - var url = "://test.example.com:443/api/v1/rest/level1/in-in/?ap=裕廊坊%20心邻坊"; - - Assert.Throws(() => ApiAuthorization.BaseString( - "auth_prefix", - SignatureMethod.HMACSHA256, - "app-id-lpX54CVNltS0ye03v2mQc0b", - new System.Uri(url), - HttpMethod.POST, - null, - "6584351262900708156", - "1502184161702", - "1.0" + "auth_prefix", + SignatureMethod.HMACSHA256, + "app-id-lpX54CVNltS0ye03v2mQc0b", + new System.Uri(url), + HttpMethod.POST, + null, + "6584351262900708156", + "1502184161702", + "1.0" )); - } - } + } + + [Test] + public void BaseString_Invalid_Url_02_Test() + { + var url = "://test.example.com:443/api/v1/rest/level1/in-in/?ap=裕廊坊%20心邻坊"; + + Assert.Throws(() => ApiAuthorization.BaseString( + "auth_prefix", + SignatureMethod.HMACSHA256, + "app-id-lpX54CVNltS0ye03v2mQc0b", + new System.Uri(url), + HttpMethod.POST, + null, + "6584351262900708156", + "1502184161702", + "1.0" + )); + } + + + + } } diff --git a/ApiUtilLibTest/CommonExtensions.cs b/ApiUtilLibTest/CommonExtensions.cs new file mode 100644 index 0000000..aad785c --- /dev/null +++ b/ApiUtilLibTest/CommonExtensions.cs @@ -0,0 +1,124 @@ +using System; +using System.Text; +using System.Collections.Generic; +using System.Linq; + +namespace ApexUtilLibTest +{ + public static class CommonExtensions + { + /// + /// Extension method to return an enum value of type T for the given string. + /// + /// + /// + /// + public static T ToEnum(this string value) + { + if (value.IsNullOrEmpty()) + return default(T); + + return (T)Enum.Parse(typeof(T), value, true); + } + + public static string GetCharp(dynamic value) + { + try + { + return value.charp; + } + catch (Exception) + { + return Convert.ToString(value); + } + } + + public static string GetCharp(Dictionary value) + { + try + { + var result = value.Where(c => c.Key == "charp").FirstOrDefault().Value; + if (result==null) + result = value.Where(c => c.Key == "default").FirstOrDefault().Value; + + return result; + } + catch (Exception) + { + return Convert.ToString(value); + } + } + + public static ApiUtilLib.SignatureMethod ParseSignatureMethod(this string value, string secret) + { + if(value==null) + { + if(secret==null) + { + return ApiUtilLib.SignatureMethod.SHA256withRSA; + }else{ + return ApiUtilLib.SignatureMethod.HMACSHA256; + } + } + + return value.ToEnum(); + } + + public static bool IsNullOrEmpty(this string value){ + if (value == null) + return true; + + if (value == String.Empty) + return true; + + return false; + } + + public static string Unescape(this string txt) + { + if (string.IsNullOrEmpty(txt)) { return txt; } + StringBuilder retval = new StringBuilder(txt.Length); + for (int ix = 0; ix < txt.Length;) + { + int jx = txt.IndexOf('\\', ix); + if (jx < 0 || jx == txt.Length - 1) jx = txt.Length; + retval.Append(txt, ix, jx - ix); + if (jx >= txt.Length) break; + switch (txt[jx + 1]) + { + case 'n': retval.Append('\n'); break; // Line feed + case 'r': retval.Append('\r'); break; // Carriage return + case 't': retval.Append('\t'); break; // Tab + case '\\': retval.Append('\\'); break; // Don't escape + default: // Unrecognized, copy as-is + retval.Append('\\').Append(txt[jx + 1]); break; + } + ix = jx + 2; + } + return retval.ToString(); + } + + public static string RemoveString(this string value, string[] array) + { + foreach (var item in array) + { + value = value.Replace(item, ""); + } + + return value; + } + + public static bool ToBool(this string value) + { + if(!value.IsNullOrEmpty()){ + if (value.ToLower() == "true") + return true; + + if (value.ToLower() == "false") + return false; + } + + return false; + } + } +} diff --git a/ApiUtilLibTest/TestDataService.cs b/ApiUtilLibTest/TestDataService.cs new file mode 100644 index 0000000..dbf15d2 --- /dev/null +++ b/ApiUtilLibTest/TestDataService.cs @@ -0,0 +1,48 @@ +using System; +using System.IO; +using System.Reflection; +using Newtonsoft.Json.Converters; +using Newtonsoft.Json; +using System.Collections.Generic; +using System.Collections; + +namespace ApexUtilLibTest +{ + public class TestDataService + { + + public TestDataService() + { + + } + + /// + /// Loads the test file. + /// + /// The test file. + /// Path. + public IEnumerable LoadTestFile(string path) + { + try + { + ///Use below code for relative path + ///var projectPath = AppDomain.CurrentDomain.BaseDirectory.Replace("bin/Debug/", ""); + ///path = path.Replace(".json", ""); + ///path = Path.Combine(Path.GetDirectoryName(projectPath), jsonFileName + ".json"); + + using (StreamReader reader = new StreamReader(path)) + { + string _result = reader.ReadToEnd(); + + var result = JsonConvert.DeserializeObject>(_result); + + return result; + } + } + catch (Exception ex) + { + throw ex; + } + } + } +} diff --git a/ApiUtilLibTest/TestDataTest.cs b/ApiUtilLibTest/TestDataTest.cs new file mode 100644 index 0000000..2dd04d2 --- /dev/null +++ b/ApiUtilLibTest/TestDataTest.cs @@ -0,0 +1,285 @@ +using ApiUtilLib; +using NUnit.Framework; +using System; +using System.Collections.Generic; +using System.Linq; +using Newtonsoft.Json.Serialization; +using System.Security.Cryptography; + +namespace ApexUtilLibTest +{ + [TestFixture()] + public class TestDataTest : BaseService + { + [Test()] + public void TestBaseString() + { + var jsonData = GetJsonFile("getSignatureBaseString.json"); + int expectedPass = jsonData.Count(); + int actualPass = 0; + + try + { + foreach (var test in jsonData) + { + SetDetaultParams(test); + + if (skipTest == null || !skipTest.Contains("c#")) + { + + var baseString = ApiAuthorization.BaseString(authPrefix, signatureMethod, appId, signatureURL, httpMethod, apiList, nonce, timeStamp, version); + + try + { + Assert.AreEqual(expectedResult, baseString); + actualPass++; + } + catch (Exception ex) + { + ex = ex; + } + } + else + { + actualPass++; + } + } + } + catch (Exception ex) + { + throw ex; + } + + Assert.AreEqual(expectedPass, actualPass); + + } + + [Test()] + public void VerifyL1Signature() + { + var jsonData = GetJsonFile("verifyL1Signature.json"); + int expectedPass = jsonData.Count(); + int actualPass = 0; + + try + { + foreach (var test in jsonData) + { + SetDetaultParams(test); + + if (skipTest == null || !skipTest.Contains("c#")) + { + var message = test.message; + var signature = test.apiParam.signature; + var result = signature.VerifyL1Signature(secret,message); + try + { + Assert.AreEqual(expectedResult.ToBool(),result); + actualPass++; + } + catch (Exception) + { + if (expectedResult == "false") + actualPass++; + } + } + else + { + actualPass++; + } + } + } + catch (Exception) + { + throw; + } + + Assert.AreEqual(expectedPass, actualPass); + } + + [Test()] + public void VerifyL2Signature() + { + var jsonData = GetJsonFile("verifyL2Signature.json"); + int expectedPass = jsonData.Count(); + int actualPass = 0; + + try + { + foreach (var test in jsonData) + { + SetDetaultParams(test); + + if (skipTest == null || !skipTest.Contains("c#")) + { + + var message = test.message; + var signature = test.apiParam.signature; + string certPath = testCertPath + test.publicCertFileName; + + RSACryptoServiceProvider publicKey = ApiAuthorization.PublicKeyFromCer(certPath); + + var result = signature.VerifyL2Signature(publicKey, message); + + try + { + Assert.IsTrue(result); + actualPass++; + } + catch (Exception) + { + if (expectedResult == "false") + actualPass++; + } + }else{ + actualPass++; + } + } + } + catch (Exception ex) + { + throw ex; + } + + Assert.AreEqual(expectedPass, actualPass); + } + + [Test()] + public void TestTokenSignature() + { + var jsonData = GetJsonFile("getSignatureToken.json"); + int expectedPass = jsonData.Count(); + int actualPass = 0; + try + { + foreach (var test in jsonData) + { + SetDetaultParams(test); + + if (skipTest == null || !skipTest.Contains("c#")) + { + try + { + string certName = test.apiParam.privateCertFileName; + string privateCertPath = testCertPath + certName; + + RSACryptoServiceProvider privateKey = null; + if (!certName.IsNullOrEmpty()) + privateKey = ApiAuthorization.PrivateKeyFromP12(privateCertPath, passphrase); + var result = ApiAuthorization.Token(realm, authPrefix, httpMethod, signatureURL, appId, secret, apiList, privateKey, nonce, timeStamp, version); + + Assert.AreEqual(expectedResult, result); + actualPass++; + } + catch (Exception ex) + { + if (errorTest) + actualPass++; + } + } + else + { + actualPass++; + } + } + } + catch (Exception ex) + { + throw; + } + Assert.AreEqual(expectedPass, actualPass); + } + + [Test()] + public void GetL1Signature() + { + var jsonData = GetJsonFile("getL1Signature.json"); + int expectedPass = jsonData.Count(); + int actualPass = 0; + + try + { + foreach (var test in jsonData) + { + SetDetaultParams(test); + + if (skipTest == null || !skipTest.Contains("c#")) + { + var message = test.message; + try + { + var result = message.L1Signature(secret); + Assert.AreEqual(expectedResult, result); + actualPass++; + } + catch (Exception) + { + if (errorTest) + actualPass++; + } + } + else + { + actualPass++; + } + } + } + catch (Exception ex) + { + throw ex; + } + + Assert.AreEqual(expectedPass, actualPass); + + } + + [Test()] + public void GetL2Signature() + { + var jsonData = GetJsonFile("getL2Signature.json"); + int expectedPass = jsonData.Count(); + int actualPass = 0; + + try + { + foreach (var test in jsonData) + { + SetDetaultParams(test); + + if (skipTest == null || !skipTest.Contains("c#")) + { + try + { + var message = test.message; + string certName = test.apiParam.privateCertFileName; + string privateCertPath = testCertPath + certName; + + string privateKey = null; + if (!certName.IsNullOrEmpty()) + privateKey = ApiAuthorization.GetL2SignatureFromPEM(privateCertPath,message, passphrase); + + Assert.AreEqual(expectedResult, privateKey); + actualPass++; + } + catch (Exception ex) + { + if (errorTest) + actualPass++; + } + } + else + { + actualPass++; + } + } + } + catch (Exception ex) + { + throw ex; + } + + Assert.AreEqual(expectedPass, actualPass); + + } + } +} diff --git a/ApiUtilLibTest/TestParam.cs b/ApiUtilLibTest/TestParam.cs new file mode 100644 index 0000000..2b62f91 --- /dev/null +++ b/ApiUtilLibTest/TestParam.cs @@ -0,0 +1,71 @@ +using System; +using System.Collections.Generic; +namespace ApexUtilLibTest +{ + public class TestParam + { + public string id { get; set; } + + public string description { get; set; } + + public APIParam apiParam { get; set; } + + public string publicCertFileName { get; set; } + + public string[] skipTest { get; set; } + + public string message { get; set; } + + public string debug { get; set; } + + public string testTag { get; set; } + + public bool errorTest { get; set; } + + public Dictionary expectedResult { get; set; } + } + + public class APIParam + { + public string realm{ get; set; } + + public string appID { get; set; } + + public string authPrefix { get; set; } + + public string secret { get; set; } + + public string invokeURL { get; set; } + + public string signatureURL { get; set; } + + public string httpMethod { get; set; } + + public string signature { get; set; } + + public string privateCertFileName { get; set; } + + public string passphrase { get; set; } + + public string signatureMethod { get; set; } + + public string nonce { get; set; } + + public string timestamp { get; set; } + + public string version { get; set; } + + public Dictionary queryString { get; set; } + + public Dictionary formData { get; set; } + + } + + public class ExpectedResult + { + public string golang { get; set; } + public string nodejs { get; set; } + } + + +} diff --git a/ApiUtilLibTest/packages.config b/ApiUtilLibTest/packages.config index 0ee860b..00c9ff6 100644 --- a/ApiUtilLibTest/packages.config +++ b/ApiUtilLibTest/packages.config @@ -1,4 +1,5 @@  - + + \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 717f061..6de1f93 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,10 @@ # Change Log ## Added -### V1.0-SNAPSHOT -+ 2017-10-29 - Initial release. + ### V1.1-SNAPSHOT -+ 2018-09-10 - Bug fixes to return basestring nonce value as basestring64 from integer value. ++ 2018-09-10 - Bug Fixes. ### V1.2-SNAPSHOT + 2018-09-13 - Bug fixes to sort list by ordinal comparison. +### V1.3-SNAPSHOT ++ 2018-09-28 - Added functionality to handle json test cases.