diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..9f27cdb
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,10 @@
+[*.cs]
+
+# IDE0090: Use 'new(...)'
+csharp_style_implicit_object_creation_when_type_is_apparent = true
+
+# IDE0090: Use 'new(...)'
+dotnet_diagnostic.IDE0090.severity = none
+
+# IDE0054: Use compound assignment
+dotnet_diagnostic.IDE0054.severity = none
diff --git a/.gitignore b/.gitignore
index 29e7dc5..8997407 100644
--- a/.gitignore
+++ b/.gitignore
@@ -52,3 +52,5 @@ NUnit3TestAdapter-3.8.0.vsix
# dotCover
*.dotCover
+
+TestHarness/
\ No newline at end of file
diff --git a/.vs/ApiSecuritySolution/xs/UserPrefs.xml b/.vs/ApiSecuritySolution/xs/UserPrefs.xml
deleted file mode 100644
index a0fb447..0000000
--- a/.vs/ApiSecuritySolution/xs/UserPrefs.xml
+++ /dev/null
@@ -1,32 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.vs/ApiSecuritySolution/xs/sqlite3/db.lock b/.vs/ApiSecuritySolution/xs/sqlite3/db.lock
deleted file mode 100644
index e69de29..0000000
diff --git a/.vs/ApiSecuritySolution/xs/sqlite3/storage.ide b/.vs/ApiSecuritySolution/xs/sqlite3/storage.ide
deleted file mode 100644
index d15f853..0000000
Binary files a/.vs/ApiSecuritySolution/xs/sqlite3/storage.ide and /dev/null differ
diff --git a/.vs/ApiSecuritySolution/xs/sqlite3/storage.ide-shm b/.vs/ApiSecuritySolution/xs/sqlite3/storage.ide-shm
deleted file mode 100644
index e7a9a4e..0000000
Binary files a/.vs/ApiSecuritySolution/xs/sqlite3/storage.ide-shm and /dev/null differ
diff --git a/.vs/ApiSecuritySolution/xs/sqlite3/storage.ide-wal b/.vs/ApiSecuritySolution/xs/sqlite3/storage.ide-wal
deleted file mode 100644
index ce6292b..0000000
Binary files a/.vs/ApiSecuritySolution/xs/sqlite3/storage.ide-wal and /dev/null differ
diff --git a/ApiSecuritySolution.sln b/ApiSecuritySolution.sln
index d917907..1b3b41c 100644
--- a/ApiSecuritySolution.sln
+++ b/ApiSecuritySolution.sln
@@ -10,6 +10,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
LICENSE = LICENSE
README.md = README.md
CHANGELOG.md = CHANGELOG.md
+ .editorconfig = .editorconfig
EndProjectSection
EndProject
Global
@@ -29,7 +30,7 @@ Global
EndGlobalSection
GlobalSection(MonoDevelopProperties) = preSolution
description = Internal Update
- version = 0.1.2
+ version = 2.0.1
Policies = $0
$0.DotNetNamingPolicy = $1
$1.DirectoryNamespaceAssociation = PrefixedHierarchical
diff --git a/ApiUtilLib/ApiAuthorization.cs b/ApiUtilLib/ApiAuthorization.cs
index 7fe3519..27eae54 100644
--- a/ApiUtilLib/ApiAuthorization.cs
+++ b/ApiUtilLib/ApiAuthorization.cs
@@ -4,16 +4,10 @@
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;
@@ -24,26 +18,20 @@ public static class ApiAuthorization
// PKI - https://www.lyquidity.com/devblog/?p=70
// hmac - https://www.jokecamp.com/blog/examples-of-creating-base64-hashes-using-hmac-sha256-in-different-languages/#csharp
- public static LoggerBase Logger
- {
- get
- {
- return LoggerManager.Logger;
- }
- }
+ public static LoggerBase Logger => LoggerManager.Logger;
public static string L1Signature(this string message, string secret)
{
Logger.LogEnter(LoggerBase.Args(message, "***secret***"));
- if (String.IsNullOrEmpty(message))
+ if (string.IsNullOrEmpty(message))
{
Logger.LogError("{0} must not be null or empty.", nameof(message));
throw new ArgumentNullException(nameof(message));
}
- if (String.IsNullOrEmpty(secret))
+ if (string.IsNullOrEmpty(secret))
{
Logger.LogError("{0} must not be null or empty.", nameof(secret));
@@ -54,7 +42,7 @@ public static string L1Signature(this string message, string secret)
byte[] secretBytes = Encoding.UTF8.GetBytes(secret);
string base64Token = null;
- using (var hmacsha256 = new HMACSHA256(secretBytes))
+ using (HMACSHA256 hmacsha256 = new HMACSHA256(secretBytes))
{
byte[] messageHash = hmacsha256.ComputeHash(messageBytes);
@@ -71,7 +59,7 @@ public static bool VerifyL1Signature(this string signature, string secret, strin
{
Logger.LogEnter(LoggerBase.Args(signature, message, "***secret***"));
- bool signatureValid = ApiAuthorization.L1Signature(message, secret) == signature;
+ bool signatureValid = L1Signature(message, secret) == signature;
Logger.LogExit(LoggerBase.Args(signatureValid));
return signatureValid;
@@ -81,7 +69,7 @@ public static string L2Signature(this string message, RSACryptoServiceProvider p
{
Logger.LogEnter(LoggerBase.Args(message, privateKey));
- if (String.IsNullOrEmpty(message))
+ if (string.IsNullOrEmpty(message))
{
Logger.LogError("{0} must not be null or empty.", nameof(message));
@@ -109,255 +97,209 @@ public static string L2Signature(this string message, RSACryptoServiceProvider p
return base64Token;
}
- public static RSACryptoServiceProvider PrivateKeyFromP12(string certificateFileName, string password)
+ public static bool VerifyL2Signature(this string signature, RSACryptoServiceProvider publicKey, string message)
{
- Logger.LogEnterExit(LoggerBase.Args(certificateFileName, "***password***"));
-
-
- var privateCert = new X509Certificate2(System.IO.File.ReadAllBytes(certificateFileName), password, X509KeyStorageFlags.Exportable);
+ Logger.LogEnter(LoggerBase.Args(signature, message, publicKey));
- var OriginalPrivateKey = (RSACryptoServiceProvider)privateCert.PrivateKey;
+ byte[] messageBytes = Encoding.UTF8.GetBytes(message);
+ byte[] signatureBytes = Convert.FromBase64String(signature);
- if (Environment.OSVersion.Platform == PlatformID.MacOSX || Environment.OSVersion.Platform == PlatformID.Unix)
- {
- return OriginalPrivateKey;
- }
- else
+ bool signatureValid = false;
+ using (SHA256Managed sha256 = new SHA256Managed())
{
- var privateKey = new RSACryptoServiceProvider();
- privateKey.ImportParameters(OriginalPrivateKey.ExportParameters(true));
+ byte[] messageHash = sha256.ComputeHash(messageBytes);
- return privateKey;
+ signatureValid = publicKey.VerifyHash(messageHash, CryptoConfig.MapNameToOID("SHA256"), signatureBytes);
}
+
+ Logger.LogExit(LoggerBase.Args(signatureValid));
+ return signatureValid;
}
- public static string GetL2SignatureFromPEM(string filename, string message, string passPhrase)
+ public static RSACryptoServiceProvider GetPrivateKey(string keyFileName, string passPhrase = null)
{
- Logger.LogEnterExit(LoggerBase.Args(filename, "***password***"));
- if (String.IsNullOrEmpty(message))
- {
- Logger.LogError("{0} must not be null or empty.", nameof(message));
-
- throw new ArgumentNullException(nameof(message));
- }
+ Logger.LogEnterExit(LoggerBase.Args(keyFileName, "***password***"));
- if (String.IsNullOrEmpty(filename))
- {
- Logger.LogError("{0} must not be null or empty.", nameof(filename));
-
- throw new ArgumentNullException(nameof(filename));
- }
- 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);
+ RSACryptoServiceProvider privateKey = null;
- using (var reader = File.OpenText(filename))
- keyPair = (AsymmetricCipherKeyPair)new PemReader(reader, new PasswordFinder(passPhrase)).ReadObject();
- var decryptEngine = new Pkcs1Encoding(new RsaEngine());
+ string fileExtension = Path.GetExtension(keyFileName).ToLower();
- decryptEngine.Init(false, keyPair.Private);
- var str = obj.SignData(bytes, CryptoConfig.MapNameToOID("SHA256"));
-
- result = System.Convert.ToBase64String(str);
- }
- }
- catch (Exception ex)
+ //if (fileType == PrivateKeyFileType.P12_OR_PFX)
+ switch (fileExtension)
{
- throw ex;
+ case ".p12":
+ case ".pfx":
+ {
+ X509Certificate2 privateCert = new X509Certificate2(File.ReadAllBytes(keyFileName), passPhrase, X509KeyStorageFlags.Exportable);
+ RSACryptoServiceProvider OriginalPrivateKey = (RSACryptoServiceProvider)privateCert.PrivateKey;
+
+ if (Environment.OSVersion.Platform == PlatformID.MacOSX || Environment.OSVersion.Platform == PlatformID.Unix)
+ {
+ privateKey = OriginalPrivateKey;
+ }
+ else
+ {
+ privateKey = new RSACryptoServiceProvider();
+ privateKey.ImportParameters(OriginalPrivateKey.ExportParameters(true));
+ }
+
+ break;
+ }
+ //if (fileType == PrivateKeyFileType.PEM_PKCS1 || fileType == PrivateKeyFileType.PEM_PKCS8)
+ case ".pem":
+ case ".key":
+ {
+ RSAParameters rsaParams;
+
+ bool isPkcs1 = false;
+ string pemString = File.ReadAllText(keyFileName);
+ if (pemString.StartsWith("-----BEGIN RSA PRIVATE KEY-----"))
+ {
+ isPkcs1 = true;
+ }
+
+ using (StreamReader reader = File.OpenText(keyFileName)) // file containing RSA PKCS8 private key
+ {
+ if (isPkcs1)
+ {
+ AsymmetricCipherKeyPair keyPair_pkcs1;
+
+ keyPair_pkcs1 = (AsymmetricCipherKeyPair)new PemReader(reader, new PasswordFinder(passPhrase)).ReadObject();
+ rsaParams = DotNetUtilities.ToRSAParameters((RsaPrivateCrtKeyParameters)keyPair_pkcs1.Private);
+ }
+ else
+ {
+ RsaPrivateCrtKeyParameters keyPair_pkcs8;
+
+ keyPair_pkcs8 = (RsaPrivateCrtKeyParameters)new PemReader(reader, new PasswordFinder(passPhrase)).ReadObject();
+ rsaParams = DotNetUtilities.ToRSAParameters(keyPair_pkcs8);
+ }
+ }
+
+ privateKey = new RSACryptoServiceProvider();
+ privateKey.ImportParameters(rsaParams);
+
+ break;
+ }
+ default:
+ throw new CryptographicException("No supported key formats were found. Check that the file format are supported.");
}
- return result;
+
+ return privateKey;
}
- public static RSACryptoServiceProvider ImportPrivateKey(string pem)
+ public static RSACryptoServiceProvider GetPublicKey(string keyFileName, string passPhrase = null)
{
- PemReader pr = new PemReader(new StringReader(pem));
- AsymmetricCipherKeyPair KeyPair = (AsymmetricCipherKeyPair)pr.ReadObject();
- RSAParameters rsaParams = DotNetUtilities.ToRSAParameters((RsaPrivateCrtKeyParameters)KeyPair.Private);
+ Logger.LogEnterExit(LoggerBase.Args(keyFileName));
- RSACryptoServiceProvider csp = new RSACryptoServiceProvider();
- csp.ImportParameters(rsaParams);
- return csp;
- }
+ RSACryptoServiceProvider key = null;
+ string fileExtension = Path.GetExtension(keyFileName).ToLower();
- public static X509Certificate2 LoadCertificateFile(string filename, string passPhrase)
- {
- X509Certificate2 x509 = null;
- try
+ // check if the file contain certificate?
+ // by default .pem file conatain key
+ if (fileExtension == ".pem")
{
- using (FileStream fs = File.OpenRead(filename))
+ string pemString = File.ReadAllText(keyFileName);
+ if (pemString.StartsWith("-----BEGIN CERTIFICATE-----"))
{
- 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);
+ // assume .pem as .cer
+ fileExtension = ".cer";
}
}
- 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
+ switch (fileExtension)
{
- 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));
-
- var privateCert = new X509Certificate2(System.IO.File.ReadAllBytes(certificateFileName));
-
- return (RSACryptoServiceProvider)privateCert.PublicKey.Key;
- }
-
- public static bool VerifyL2Signature(this string signature, RSACryptoServiceProvider publicKey, string message)
- {
- Logger.LogEnter(LoggerBase.Args(signature, message, publicKey));
-
- byte[] messageBytes = Encoding.UTF8.GetBytes(message);
- byte[] signatureBytes = Convert.FromBase64String(signature);
+ case ".p12":
+ case ".pfx":
+ //if (fileType == PublicKeyFileType.P12_OR_PFX)
+ {
+ X509Certificate2 cert = new X509Certificate2(File.ReadAllBytes(keyFileName), passPhrase, X509KeyStorageFlags.Exportable);
+ key = (RSACryptoServiceProvider)cert.PublicKey.Key;
+ break;
+ }
- bool signatureValid = false;
- using (SHA256Managed sha256 = new SHA256Managed())
- {
- byte[] messageHash = sha256.ComputeHash(messageBytes);
+ //if (fileType == PublicKeyFileType.CERTIFICATE)
+ case ".cer":
+ {
+ X509Certificate2 cert = new X509Certificate2(File.ReadAllBytes(keyFileName));
+ key = (RSACryptoServiceProvider)cert.PublicKey.Key;
+ break;
+ }
- signatureValid = publicKey.VerifyHash(messageHash, CryptoConfig.MapNameToOID("SHA256"), signatureBytes);
+ //if (fileType == PublicKeyFileType.PUBLIC_KEY)
+ case ".pem":
+ case ".key":
+ {
+ using (StreamReader reader = File.OpenText(keyFileName))
+ {
+ PemReader pr = new PemReader(reader);
+ AsymmetricKeyParameter publicKey = (AsymmetricKeyParameter)pr.ReadObject();
+
+ RSAParameters rsaParams = DotNetUtilities.ToRSAParameters((RsaKeyParameters)publicKey);
+ key = new RSACryptoServiceProvider();
+ key.ImportParameters(rsaParams);
+ }
+ break;
+ }
+ default:
+ throw new CryptographicException("No supported key formats were found. Check that the file format are supported.");
}
- Logger.LogExit(LoggerBase.Args(signatureValid));
- return signatureValid;
+ return key;
}
- public static string BaseString(
- string authPrefix
- , SignatureMethod signatureMethod
- , string appId
- , Uri siteUri
- , HttpMethod httpMethod
- , ApiList formList
- , string nonce
- , string timestamp
- , string version)
- {
+ public static string BaseString(string authPrefix, SignatureMethod signatureMethod, string appId, Uri siteUri, FormData formData, HttpMethod httpMethod, string nonce, string timestamp, string version)
+ {
try
{
- Logger.LogEnter(LoggerBase.Args(authPrefix, signatureMethod, appId, siteUri, httpMethod, formList, nonce, timestamp));
+ Logger.LogEnter(LoggerBase.Args(authPrefix, signatureMethod, appId, siteUri, httpMethod, formData, nonce, timestamp));
authPrefix = authPrefix.ToLower();
// make sure that the url are valid
if (siteUri.Scheme != "http" && siteUri.Scheme != "https")
{
- throw new System.NotSupportedException("Support http and https protocol only.");
+ throw new NotSupportedException("Support http and https protocol only.");
}
// make sure that the port no and querystring are remove from url
- string url;
-
- if(siteUri.Port==-1 || siteUri.Port==80 || siteUri.Port == 443)
- {
- url = string.Format("{0}://{1}{2}", siteUri.Scheme, siteUri.Host, siteUri.AbsolutePath);
- }
- else
- {
- url = string.Format("{0}://{1}:{2}{3}", siteUri.Scheme, siteUri.Host, siteUri.Port, siteUri.AbsolutePath);
- }
-
+ string url = siteUri.Port == (-1) || siteUri.Port == 80 || siteUri.Port == 443
+ ? string.Format("{0}://{1}{2}", siteUri.Scheme, siteUri.Host, siteUri.AbsolutePath)
+ : string.Format("{0}://{1}:{2}{3}", siteUri.Scheme, siteUri.Host, siteUri.Port, siteUri.AbsolutePath);
Logger.LogInformation("url:: {0}", url);
// helper calss that handle parameters and form fields
- ApiList paramList = new ApiList();
+ ApiList paramList = new ApiList();
// 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
+ string queryString = siteUri.Query.Substring(1); // remove the ? from first character
Logger.LogInformation("queryString:: {0}", queryString);
- var paramArr = queryString.Split('&');
+ string[] paramArr = queryString.Split('&');
foreach (string item in paramArr)
{
string key = null;
string val = null;
- var itemArr = item.Split('=');
+ string[] itemArr = item.Split('=');
key = itemArr[0];
- if(itemArr.Length>1)
+ if (itemArr.Length > 1)
+ {
val = itemArr[1];
- paramList.Add(key, System.Net.WebUtility.UrlDecode(val));
+ }
+
+ paramList.Add(WebUtility.UrlDecode(key), WebUtility.UrlDecode(val));
}
Logger.LogInformation("paramList:: {0}", paramList);
}
// add the form fields to paramList
- if (formList != null && formList.Count > 0)
+ if (formData != null && formData.Count > 0)
{
- paramList.AddRange(formList);
+ paramList.AddRange(formData.GetBaseStringList());
}
paramList.Add(authPrefix + "_timestamp", timestamp);
@@ -373,7 +315,7 @@ string authPrefix
Logger.LogExit(LoggerBase.Args(baseString));
return baseString;
}
- catch (Exception ex)
+ catch (Exception ex)
{
throw ex;
}
@@ -394,98 +336,25 @@ public static string NewNonce()
using (RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider())
{
// Buffer storage.
- data = new byte[8];
+ data = new byte[32];
// Fill buffer.
rng.GetBytes(data);
}
Logger.LogEnterExit(LoggerBase.Args(nonce.ToString()));
-
- return System.Convert.ToBase64String(data);
+ return Convert.ToBase64String(data);
}
- public static String Token(
- string realm
- , string authPrefix
- , HttpMethod httpMethod
- , Uri urlPath
- , string appId
- , string secret = null
- , ApiList formList = null
- , RSACryptoServiceProvider privateKey = null
- , string nonce = null
- , string timestamp = null
- , string version = "1.0")
+ public static int HttpRequest(Uri url, string token, FormData formData, HttpMethod httpMethod = HttpMethod.GET, bool ignoreServerCert = false)
{
- String nullValueErrMsg = "One or more required parameters are missing!";
- try
- {
- Logger.LogEnter(LoggerBase.Args(realm, authPrefix, httpMethod, urlPath, appId, secret, formList == null ? null : formList.ToFormData(), privateKey, nonce, timestamp, version));
-
- Logger.LogDebug("URL:: {0}", urlPath);
- if (String.IsNullOrEmpty(authPrefix))
- {
- Logger.LogError(nullValueErrMsg);
- throw new ArgumentNullException(nameof(authPrefix));
- }
-
-
- authPrefix = authPrefix.ToLower();
-
- // Generate the nonce value
- nonce = nonce ?? ApiAuthorization.NewNonce().ToString();
- timestamp = timestamp ?? ApiAuthorization.NewTimestamp().ToString();
-
- SignatureMethod signatureMethod = SignatureMethod.HMACSHA256;
- if (secret == null)
- {
- signatureMethod = SignatureMethod.SHA256withRSA;
- }
-
- String baseString = BaseString(authPrefix, signatureMethod
- , appId, urlPath, httpMethod
- , formList, nonce, timestamp, version);
-
- String base64Token = "";
- if (secret != null)
- {
- base64Token = baseString.L1Signature(secret);
- }
- else
- {
- base64Token = baseString.L2Signature(privateKey);
- }
-
- var tokenList = new ApiList();
-
- tokenList.Add("realm", realm);
- 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);
+ Logger.LogEnter(LoggerBase.Args(url, token, formData == null ? "null" : formData.ToString(), httpMethod));
- string authorizationToken = string.Format("{0} {1}", authPrefix.Substring(0, 1).ToUpperInvariant() + authPrefix.Substring(1), tokenList.ToString(", ", false, true));
-
- Logger.LogDebug("Token :: {0}", authorizationToken);
+ int returnCode = 0;
- Logger.LogExit(LoggerBase.Args(authorizationToken));
- return authorizationToken;
- }
- catch (Exception ex)
+ if (ignoreServerCert)
{
- throw ex;
+ InitiateSSLTrust();
}
- }
-
- public static int HttpRequest(Uri url, string token = null, ApiList postData = null, HttpMethod httpMethod = HttpMethod.GET, bool ignoreServerCert = false)
- {
- Logger.LogEnter(LoggerBase.Args(url, token, postData == null ? "null" : postData.ToFormData(), httpMethod));
-
- int returnCode = 0;
-
- if (ignoreServerCert) InitiateSSLTrust();
WebResponse response = null;
StreamReader reader = null;
@@ -498,16 +367,19 @@ public static int HttpRequest(Uri url, string token = null, ApiList postData = n
request.Method = httpMethod.ToString();
- if (!string.IsNullOrEmpty(token)) request.Headers.Add("Authorization", token);
+ if (!string.IsNullOrEmpty(token))
+ {
+ request.Headers.Add("Authorization", token);
+ }
- if (postData != null)
+ if (formData != null)
{
// Set the Method property of the request to POST.
request.Method = HttpMethod.POST.ToString();
// Create POST data and convert it to a byte array.
//string postData = "This is a test that posts this string to a Web server.";
- byte[] byteArray = Encoding.UTF8.GetBytes(postData.ToFormData());
+ byte[] byteArray = Encoding.UTF8.GetBytes(formData.ToString());
// Set the ContentType property of the WebRequest.
request.ContentType = "application/x-www-form-urlencoded";
@@ -529,9 +401,9 @@ public static int HttpRequest(Uri url, string token = null, ApiList postData = n
response = request.GetResponse();
// Display the status.
- Logger.LogDebug("Response Code:: {0} - {1}", ((HttpWebResponse)response).StatusDescription, (int)(((HttpWebResponse)response).StatusCode));
+ Logger.LogDebug("Response Code:: {0} - {1}", ((HttpWebResponse)response).StatusDescription, (int)((HttpWebResponse)response).StatusCode);
- returnCode = (int)(((HttpWebResponse)response).StatusCode);
+ returnCode = (int)((HttpWebResponse)response).StatusCode;
// Get the stream containing content returned by the server.
dataStream = response.GetResponseStream();
@@ -551,9 +423,9 @@ public static int HttpRequest(Uri url, string token = null, ApiList postData = n
{
response = (HttpWebResponse)e.Response;
- returnCode = (int)(((HttpWebResponse)response).StatusCode);
+ returnCode = (int)((HttpWebResponse)response).StatusCode;
- Logger.LogWarning("Error Code: {0} - {1}", returnCode, (((HttpWebResponse)response).StatusDescription));
+ Logger.LogWarning("Error Code: {0} - {1}", returnCode, ((HttpWebResponse)response).StatusDescription);
// Get the stream containing content returned by the server.
dataStream = response.GetResponseStream();
@@ -576,20 +448,34 @@ public static int HttpRequest(Uri url, string token = null, ApiList postData = n
finally
{
// Clean up the streams and the response.
- if (response != null) response.Close();
- if (reader != null) reader.Close();
- if (dataStream != null) dataStream.Close();
+ if (response != null)
+ {
+ response.Close();
+ }
+
+ if (reader != null)
+ {
+ reader.Close();
+ }
+
+ if (dataStream != null)
+ {
+ dataStream.Close();
+ }
}
Logger.LogExit(LoggerBase.Args(returnCode));
return returnCode;
}
- static bool iniSslTrust;
+ private static bool iniSslTrust;
- public static void InitiateSSLTrust()
+ private static void InitiateSSLTrust()
{
- if (iniSslTrust) return;
+ if (iniSslTrust)
+ {
+ return;
+ }
try
{
@@ -610,7 +496,7 @@ public static void InitiateSSLTrust()
private class PasswordFinder : IPasswordFinder
{
- private string password;
+ private readonly string password;
public PasswordFinder(string password)
{
@@ -624,7 +510,130 @@ public char[] GetPassword()
}
}
- }
+ private const string APEX1_DOMAIN = "api.gov.sg";
+
+ public static AuthToken TokenV2(AuthParam authParam) {
+ Logger.LogEnter(LoggerBase.Args(authParam));
+
+ string nullValueErrMsg = "One or more required parameters are missing!";
+
+ if (authParam.url == null)
+ {
+ Logger.LogError(nullValueErrMsg);
+ throw new ArgumentNullException(nameof(authParam.url));
+ }
+ // split url into hostname and domain
+ string gatewayName = authParam.url.Host.Split('.')[0];
+ string originalDomain = string.Join(".", authParam.url.Host.Split('.').Skip(1).ToArray());
-}
+ // default api domain to external zone
+ string apiDomain = string.Format(".{0}", originalDomain);
+ string authSuffix = "eg";
+
+ // for backward compatible with apex1
+ if (authParam.url.Host.EndsWith(APEX1_DOMAIN))
+ {
+ apiDomain = string.Format(".e.{0}", originalDomain);
+ }
+
+ // switch to internal zone based on hostname suffix
+ if (gatewayName.EndsWith("-pvt"))
+ {
+ authSuffix = "ig";
+
+ // for backward compatible with apex1
+ if (authParam.url.Host.EndsWith(APEX1_DOMAIN))
+ {
+ apiDomain = string.Format(".i.{0}", originalDomain);
+ }
+ }
+
+ // default auth level to l2, switch to l1 if appSecret provided
+ string authLevel = "l2";
+ if (authParam.appSecret != null)
+ {
+ authLevel = "l1";
+ }
+
+ // for backward compatible with apex1, update the signature url
+ string signatureUrl = authParam.url.ToString().Replace(string.Format(".{0}", originalDomain), apiDomain);
+
+ // Generate the nonce value
+ authParam.nonce = authParam.nonce ?? NewNonce().ToString();
+ authParam.timestamp = authParam.timestamp ?? NewTimestamp().ToString();
+
+ authParam.signatureMethod = SignatureMethod.HMACSHA256;
+ if (authParam.appSecret == null)
+ {
+ authParam.signatureMethod = SignatureMethod.SHA256withRSA;
+ }
+
+ string apexToken = "";
+ List baseStringList = new List();
+
+ if (authParam.appName != null)
+ {
+ string realm = string.Format("https://{0}", authParam.url.Host);
+ string authPrefix = string.Format("apex_{0}_{1}", authLevel, authSuffix);
+ if (authParam.version == null)
+ {
+ authParam.version = "1.0";
+ }
+
+ string baseString = BaseString(
+ authPrefix,
+ authParam.signatureMethod,
+ authParam.appName,
+ new Uri(signatureUrl),
+ authParam.formData,
+ authParam.httpMethod,
+ authParam.nonce,
+ authParam.timestamp,
+ authParam.version
+ );
+ baseStringList.Add(baseString);
+
+ string base64Token = authLevel == "l1" ? baseString.L1Signature(authParam.appSecret) : baseString.L2Signature(authParam.privateKey);
+ ApiList tokenList = new ApiList
+ {
+ { "realm", realm },
+ { authPrefix + "_app_id", authParam.appName },
+ { authPrefix + "_nonce", authParam.nonce },
+ { authPrefix + "_signature_method", authParam.signatureMethod.ToString() },
+ { authPrefix + "_timestamp", authParam.timestamp },
+ { authPrefix + "_version", authParam.version },
+ { authPrefix + "_signature", base64Token }
+ };
+
+ apexToken = string.Format("{0} {1}", authPrefix.Substring(0, 1).ToUpperInvariant() + authPrefix.Substring(1), tokenList.ToString(", ", false, true));
+ }
+ string authToken = string.Format("{0}", apexToken);
+
+ if (authParam.nextHop != null) {
+ // propagate the information from root param to nextHop
+ authParam.nextHop.httpMethod = authParam.httpMethod;
+ //authParam.nextHop.queryString = authParam.queryString;
+ authParam.nextHop.formData = authParam.formData;
+
+ // get the apexToken for nextHop
+ AuthToken nextHopResult = TokenV2(authParam.nextHop);
+
+ // save the baseString
+ baseStringList.Add(nextHopResult.BaseString);
+
+ string netxHopToken = nextHopResult.Token;
+
+ // combine the apexToken if required
+ if (authToken == "") {
+ authToken = string.Format("{0}", netxHopToken);
+ } else {
+ authToken += string.Format(", {0}", netxHopToken);
+ }
+ }
+
+ Logger.LogExit(LoggerBase.Args(authToken, baseStringList));
+ return new AuthToken(authToken, baseStringList);
+ }
+ }
+}
\ No newline at end of file
diff --git a/ApiUtilLib/ApiList.cs b/ApiUtilLib/ApiList.cs
index 9b5e86d..6f2f585 100644
--- a/ApiUtilLib/ApiList.cs
+++ b/ApiUtilLib/ApiList.cs
@@ -1,38 +1,42 @@
using System;
using System.Collections.Generic;
using System.Linq;
-using System.Collections;
-using ApexUtilLib;
namespace ApiUtilLib
{
- public class ApiList : List>
+ internal class ApiList : List>
{
public void Add(string key, string value)
{
- KeyValuePair item = new KeyValuePair(key, value);
+ KeyValuePair item = new KeyValuePair(key, value);
- this.Add(item);
+ Add(item);
}
// for BaseString
public string ToString(string delimiter = "&", bool sort = true, bool quote = false)
{
- var list = new List();
+ List list = new List();
string format = "{0}={1}";
- if (quote) format = "{0}=\"{1}\"";
+ if (quote)
+ {
+ format = "{0}=\"{1}\"";
+ }
if (sort)
{
// 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.
+ IOrderedEnumerable> 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)
+ foreach (KeyValuePair item in sortedList)
{
format = "{0}={1}";
- if (quote) format = "{0}=\"{1}\"";
+ if (quote)
+ {
+ format = "{0}=\"{1}\"";
+ }
if (item.Value.IsNullOrEmpty() && !quote)
{
@@ -46,34 +50,13 @@ public string ToString(string delimiter = "&", bool sort = true, bool quote = fa
}
else
{
- foreach (var item in this)
+ foreach (KeyValuePair item in this)
{
list.Add(string.Format(format, item.Key, item.Value));
}
}
- return String.Join(delimiter, list.ToArray());
- }
-
- public string ToFormData()
- {
- string delimiter = "&";
-
- var list = new List();
-
- string format = "{0}={1}";
-
- foreach (var item in this)
- {
- list.Add(string.Format(format, System.Net.WebUtility.UrlEncode(item.Key), System.Net.WebUtility.UrlEncode(item.Value)));
- }
-
- return String.Join(delimiter, list.ToArray());
- }
-
- public string ToQueryString()
- {
- return ToFormData();
+ return string.Join(delimiter, list.ToArray());
}
}
}
diff --git a/ApiUtilLib/ApiUtilLib.csproj b/ApiUtilLib/ApiUtilLib.csproj
index c821bfc..17abe13 100644
--- a/ApiUtilLib/ApiUtilLib.csproj
+++ b/ApiUtilLib/ApiUtilLib.csproj
@@ -8,7 +8,7 @@
ApexUtilLib
ApexUtilLib
v4.6.1
- 0.1.2
+ 2.0.1
true
@@ -30,10 +30,10 @@
- ..\packages\Newtonsoft.Json.11.0.2\lib\net45\Newtonsoft.Json.dll
+ ..\packages\Newtonsoft.Json.13.0.1\lib\net45\Newtonsoft.Json.dll
- ..\packages\BouncyCastle.1.8.3\lib\BouncyCastle.Crypto.dll
+ ..\packages\BouncyCastle.1.8.9\lib\BouncyCastle.Crypto.dll
@@ -49,6 +49,10 @@
+
+
+
+
diff --git a/ApiUtilLib/AuthParam.cs b/ApiUtilLib/AuthParam.cs
new file mode 100644
index 0000000..2011356
--- /dev/null
+++ b/ApiUtilLib/AuthParam.cs
@@ -0,0 +1,25 @@
+using System;
+using System.Security.Cryptography;
+
+namespace ApiUtilLib
+{
+ public class AuthParam
+ {
+ public Uri url;
+ public HttpMethod httpMethod;
+
+ public string appName;
+ public string appSecret;
+ public RSACryptoServiceProvider privateKey = null;
+
+ public FormData formData;
+
+ public string nonce;
+ public string timestamp;
+
+ internal SignatureMethod signatureMethod;
+ public string version = "1.0";
+
+ public AuthParam nextHop;
+ }
+}
diff --git a/ApiUtilLib/AuthToken.cs b/ApiUtilLib/AuthToken.cs
new file mode 100644
index 0000000..f4e9d85
--- /dev/null
+++ b/ApiUtilLib/AuthToken.cs
@@ -0,0 +1,19 @@
+using System.Collections.Generic;
+
+namespace ApiUtilLib
+{
+ public class AuthToken
+ {
+ internal AuthToken(string token, List baseStringList)
+ {
+ Token = token;
+ BaseStringList = baseStringList;
+ }
+
+ public string Token { get; }
+
+ public List BaseStringList { get; }
+
+ public string BaseString => string.Join(", ", BaseStringList.ToArray());
+ }
+}
diff --git a/ApiUtilLib/CommonExtensions.cs b/ApiUtilLib/CommonExtensions.cs
index d1af409..342b5eb 100644
--- a/ApiUtilLib/CommonExtensions.cs
+++ b/ApiUtilLib/CommonExtensions.cs
@@ -1,19 +1,12 @@
-using System;
-using System.Text;
+using System.Text;
-namespace ApexUtilLib
+namespace ApiUtilLib
{
public static class CommonExtensions
{
public static bool IsNullOrEmpty(this string value)
{
- if (value == null)
- return true;
-
- if (value == String.Empty)
- return true;
-
- return false;
+ return value == null || value == string.Empty;
}
public static string Unescape(this string txt)
@@ -23,10 +16,18 @@ public static string Unescape(this string txt)
for (int ix = 0; ix < txt.Length;)
{
int jx = txt.IndexOf('\n', ix);
- if (jx < 0 || jx == txt.Length - 1) jx = txt.Length;
+ 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];
+ if (jx >= txt.Length)
+ {
+ break;
+ }
+
+ //char str = txt[jx + 1];
switch (txt[jx + 1])
{
case 'n': retval.Append('\n'); break; // Line feed
@@ -43,7 +44,7 @@ public static string Unescape(this string txt)
public static string RemoveString(this string value, string[] array)
{
- foreach (var item in array)
+ foreach (string item in array)
{
value = value.Replace(item, "");
}
diff --git a/ApiUtilLib/ConsoleLogger.cs b/ApiUtilLib/ConsoleLogger.cs
index 1d2ed8b..ebcdb2d 100644
--- a/ApiUtilLib/ConsoleLogger.cs
+++ b/ApiUtilLib/ConsoleLogger.cs
@@ -4,17 +4,17 @@ namespace ApiUtilLib
{
public class ConsoleLogger : LoggerBase
{
- readonly string _messageFormat = "{0:yyyy-MM-dd HH:mm:ss.fff %K} : {1} : {2}";
+ private readonly string _messageFormat = "{0:yyyy-MM-dd HH:mm:ss.fff %K} : {1} : {2}";
- public ConsoleLogger()
- {
- this.LogLevel = LogLevel.None;
- }
+ public ConsoleLogger()
+ {
+ LogLevel = LogLevel.None;
+ }
public ConsoleLogger(LogLevel logLevel)
- {
- this.LogLevel = logLevel;
- }
+ {
+ LogLevel = logLevel;
+ }
public override void LogMessage(LogLevel messageLogLevel, string message)
{
diff --git a/ApiUtilLib/FileLogger.cs b/ApiUtilLib/FileLogger.cs
index 5078848..849ca94 100644
--- a/ApiUtilLib/FileLogger.cs
+++ b/ApiUtilLib/FileLogger.cs
@@ -5,16 +5,16 @@ namespace ApiUtilLib
{
public class FileLogger : LoggerBase
{
- readonly string _messageFormat = "{0:yyyy-MM-dd HH:mm:ss.fff %K} : {1} : {2}";
+ private readonly string _messageFormat = "{0:yyyy-MM-dd HH:mm:ss.fff %K} : {1} : {2}";
public FileLogger()
{
- this.LogLevel = LogLevel.None;
+ LogLevel = LogLevel.None;
}
public FileLogger(LogLevel logLevel)
{
- this.LogLevel = logLevel;
+ LogLevel = logLevel;
}
~FileLogger()
@@ -24,31 +24,30 @@ public FileLogger(LogLevel logLevel)
public override void LogMessage(LogLevel messageLogLevel, string message)
{
- //Console.WriteLine(_messageFormat, DateTime.Now, messageLogLevel.ToString(), message);
WriteLog(string.Format(_messageFormat, DateTime.Now, messageLogLevel.ToString(), message));
}
- static bool _firstTime = true;
- static string _logFilePath = "Logs";
+ private static bool _firstTime = true;
+ private static string _logFilePath = "Logs";
private static void SetupLog()
{
_firstTime = false;
+ FileInfo logFileInfo;
- FileStream fileStream = null;
- DirectoryInfo logDirInfo = null;
- FileInfo logFileInfo;
-
- _logFilePath = Path.Combine(_logFilePath, System.DateTime.Today.ToString("yyyy-MM-dd") + "." + "log");
+ _logFilePath = Path.Combine(_logFilePath, DateTime.Today.ToString("yyyy-MM-dd") + "." + "log");
logFileInfo = new FileInfo(_logFilePath);
- logDirInfo = new DirectoryInfo(logFileInfo.DirectoryName);
- if (!logDirInfo.Exists) logDirInfo.Create();
+ DirectoryInfo logDirInfo = new DirectoryInfo(logFileInfo.DirectoryName);
+ if (!logDirInfo.Exists)
+ {
+ logDirInfo.Create();
+ }
- if (!logFileInfo.Exists)
+ if (!logFileInfo.Exists)
{
- fileStream = logFileInfo.Create();
- fileStream.Close();
+ FileStream fileStream = logFileInfo.Create();
+ fileStream.Close();
}
WriteLog(string.Format("\n\n{0:yyyy-MM-dd HH:mm:ss.fff %K} : {1}", DateTime.Now, new string('-', 128)));
@@ -56,17 +55,18 @@ private static void SetupLog()
private static void WriteLog(string strLog)
{
- if (_firstTime) SetupLog();
-
- StreamWriter log;
- FileStream fileStream = null;
+ if (_firstTime)
+ {
+ SetupLog();
+ }
- fileStream = new FileStream(_logFilePath, FileMode.Append);
+ StreamWriter log;
+ FileStream fileStream = new FileStream(_logFilePath, FileMode.Append);
- log = new StreamWriter(fileStream);
+ log = new StreamWriter(fileStream);
log.WriteLine(strLog);
log.Close();
}
}
-}
+}
\ No newline at end of file
diff --git a/ApiUtilLib/FormList.cs b/ApiUtilLib/FormList.cs
new file mode 100644
index 0000000..0acb9fb
--- /dev/null
+++ b/ApiUtilLib/FormList.cs
@@ -0,0 +1,305 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace ApiUtilLib
+{
+ public abstract class BaseList : List>
+ {
+ public void Add(string key, string[] value)
+ {
+ FormField newValue = new FormField(value);
+ Add(key, newValue);
+ }
+
+ public void Add(string key, string value)
+ {
+ FormField newValue = new FormField(value);
+ Add(key, newValue);
+ }
+
+ private void Add(string key, FormField value)
+ {
+ KeyValuePair formField = this.FirstOrDefault(x => x.Key == key);
+
+ if (formField.Key == key)
+ {
+ formField.Value.Add(value.RawValue);
+ }
+ else
+ {
+ KeyValuePair item = new KeyValuePair(key, value);
+
+ value.Key = key;
+
+ Add(item);
+ }
+ }
+
+ public virtual string ToString(bool urlSafeString = true)
+ {
+ string delimiter = "&";
+
+ List list = new List();
+
+ string format = "{0}={1}";
+
+ foreach (KeyValuePair item in this)
+ {
+ if (urlSafeString)
+ {
+ list.Add(string.Format(format, System.Net.WebUtility.UrlEncode(item.Key), item.Value.FormValue));
+ }
+ else
+ {
+ list.Add(string.Format(format, item.Key, item.Value.Value));
+ }
+ }
+
+ return string.Join(delimiter, list.ToArray());
+ }
+
+ internal static T SetupList(Dictionary
-
-
-
-
-
@@ -66,6 +65,7 @@
PreserveNewest
+
diff --git a/ApiUtilLibTest/AuthorizationTokenTest.cs b/ApiUtilLibTest/AuthorizationTokenTest.cs
deleted file mode 100644
index 8b35266..0000000
--- a/ApiUtilLibTest/AuthorizationTokenTest.cs
+++ /dev/null
@@ -1,141 +0,0 @@
-using NUnit.Framework;
-using System;
-
-using ApiUtilLib;
-using System.IO;
-using System.Reflection;
-using System.Security.Cryptography;
-
-namespace ApiUtilLibTest
-{
- [TestFixture]
- public class AuthorizationToken
- {
-
- static string GetLocalPath(string relativeFileName)
- {
- var localPath = Path.Combine(Path.GetDirectoryName(new Uri(Assembly.GetExecutingAssembly().CodeBase).LocalPath), relativeFileName.Replace('/', Path.DirectorySeparatorChar));
-
- return localPath;
- }
-
- // file name follow unix convention...
- static readonly string privateCertNameP12 = GetLocalPath("Certificates/ssc.alpha.example.com.p12");
-
- const string passphrase = "passwordp12";
-
- static RSACryptoServiceProvider privateKey = ApiAuthorization.PrivateKeyFromP12(privateCertNameP12, passphrase);
-
- const string realm = "http://example.api.test/token";
- const string authPrefixL1 = "api_prefix_l1";
- const string authPrefixL2 = "api_prefix_l2";
-
- const HttpMethod httpMethod = HttpMethod.GET;
- private Uri url = new Uri("https://test.example.com:443/api/v1/rest/level1/in-in/?ap=裕廊坊%20心邻坊");
-
- const string appId = "app-id-lpX54CVNltS0ye03v2mQc0b";
- const string secret = "5aes9wG4mQgWJBfKMuYLtrEtNslm1enWG2XpGaMk";
-
- const string nonce = "-5816789581922453013";
- const string timestamp = "1502199514462";
-
- [Test]
- public void Test_L1_Basic_Test()
- {
- var expectedTokenL1 = "Api_prefix_l1 realm=\"http://example.api.test/token\", api_prefix_l1_app_id=\"app-id-lpX54CVNltS0ye03v2mQc0b\", api_prefix_l1_nonce=\"-5816789581922453013\", api_prefix_l1_signature_method=\"HMACSHA256\", api_prefix_l1_timestamp=\"1502199514462\", api_prefix_l1_version=\"1.0\", api_prefix_l1_signature=\"loz2Hp2wqiK8RxWjkI6Y6Y4OzmOS/QVPevT8Z43TRM4=\"";
-
- var authorizationToken = ApiAuthorization.Token(
- realm
- , authPrefixL1
- , httpMethod
- , url
- , appId
- , secret
- , timestamp: timestamp
- , nonce: nonce
- );
-
- Assert.AreEqual(expectedTokenL1, authorizationToken);
- }
-
- [Test]
- public void Test_L2_Basic_Test()
- {
- var expectedTokenL2 = "Api_prefix_l2 realm=\"http://example.api.test/token\", api_prefix_l2_app_id=\"app-id-lpX54CVNltS0ye03v2mQc0b\", api_prefix_l2_nonce=\"-5816789581922453013\", api_prefix_l2_signature_method=\"SHA256withRSA\", api_prefix_l2_timestamp=\"1502199514462\", api_prefix_l2_version=\"1.0\", api_prefix_l2_signature=\"EZuFn/n3dxJ4OA9nkdM3yvw76azvyx/HKptQoWzTNWHxMB/2FyurbbpsSb16yNU4bOzRgHlFTZZzbJeZd211M7tLfRC/YQ1Mc2aIxufG7c7H3/3IZ0WdfHIJlF+XwHOR4U5sjRhbCBwSOZzHp6V2a/nmm+CYTjW2LBHxG7aB1wNI6V1PGDp+ePVr8uoyd4MD9nJj5IqLlljtpWCBUJsa7ZZdXgwbStxAdVA3j2lk3FAH9BzaKTQV0msB50Ou/itAw95pqH4RGrWjcuUETUN82JG154SrT/+hqXlmgsgl+6vui7kyCIGnQjhH+3ZSIp/91nJKW8/1hDcNKWQzuoIS9G23rJzPIuStc1f8y/YvXjUSxNTItb4DcSGwqOs1W8+ejLofW/HDBENhhL66ZZaO0EbJmMWJDp+r7w+RtrlRa2QLsuocuAYAsc8FbhW8SBowIHt/BpuIE21SCfXhbbqYmi0WY+YjJxJ79bNsf7OzH57wQln2Ri6jUtRsCez3rP+714aSAJMLKzJPrsUsiefQDuDjl+g7Fs+Ge5eCv3EOu36qmBEAwvS8oNU8eKa0ZnuXTZrvVEyAAgqQXjv7V4tklKImHMhBv3CqWHGtmxCIqFJuJ71ss81kOJ9pc1otyMzKvSZtVyxaOFgE1hTPfsA6Y5pQayhVikeCMfX8u/uFSmM=\"";
-
- var authorizationToken = ApiAuthorization.Token(
- realm
- , authPrefixL2
- , httpMethod
- , url
- , appId
- , privateKey: privateKey
- , timestamp: timestamp
- , nonce: nonce
- );
-
- Assert.AreEqual(expectedTokenL2, authorizationToken);
- }
-
- [Test]
- public void Test_L2_Wrong_Password_Test()
- {
-
- Assert.Throws(() =>
- {
- var myPrivateKey = ApiAuthorization.PrivateKeyFromP12(privateCertNameP12, passphrase + "x");
-
- ApiAuthorization.Token(
- realm
- , authPrefixL2
- , httpMethod
- , url
- , appId
- , privateKey: myPrivateKey
- );
- });
- }
-
- [Test]
- public void Test_L2_Not_Supported_Cert_Test()
- {
- var fileName = GetLocalPath("Certificates/ssc.alpha.example.com.pem");
-
- Assert.Throws(() =>
- {
- var myPrivateKey = ApiAuthorization.PrivateKeyFromP12(fileName, passphrase);
-
- ApiAuthorization.Token(
- realm
- , authPrefixL2
- , httpMethod
- , url
- , appId
- , privateKey: myPrivateKey
- );
- });
- }
-
- [Test]
- public void Test_L2_Invalid_FileName_Test()
- {
- var fileName = "Xssc.alpha.example.com.p12";
-
- Assert.Throws(() =>
- {
- var myPrivateKey = ApiAuthorization.PrivateKeyFromP12(fileName, passphrase);
-
- ApiAuthorization.Token(
- realm
- , authPrefixL2
- , httpMethod
- , url
- , appId
- , privateKey: myPrivateKey
- );
- });
- }
-
-
- }
-}
diff --git a/ApiUtilLibTest/BaseService.cs b/ApiUtilLibTest/BaseService.cs
index 6836455..0f720f2 100644
--- a/ApiUtilLibTest/BaseService.cs
+++ b/ApiUtilLibTest/BaseService.cs
@@ -1,52 +1,64 @@
-using ApexUtilLib;
-using ApiUtilLib;
+using ApiUtilLib;
using System;
using System.Collections.Generic;
using System.IO;
-using System.Security.Cryptography;
-using System.Text;
using System.IO.Compression;
using System.Reflection;
+using System.Linq;
+using Newtonsoft.Json;
+using NUnit.Framework;
namespace ApexUtilLibTest
{
public class BaseService
{
- internal string apexTestSuitePath = "https://github.com/GovTechSG/test-suites-apex-api-security/archive/master.zip";
- internal string testDataPath = GetLocalPath("temp/test-suites-apex-api-security-master/testData/");
- internal string testCertPath = GetLocalPath("temp/test-suites-apex-api-security-master/");
-
- 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; }
+ // APEX 1
+ //internal static string apexTestSuitePath = "https://github.com/GovTechSG/test-suites-apex-api-security/archive/master.zip";
+
+ // for APEX2
+ //internal static string apexTestSuitePath = "https://github.com/GovTechSG/test-suites-apex-api-security/zipball/master/";
+ internal static string apexTestSuitePath = "https://github.com/GovTechSG/test-suites-apex-api-security/zipball/development/";
+
+ internal static bool IsDebug = false;
+
+ internal static bool IsDataFileDownloaded = false;
+ internal static string testDataPath = GetLocalPath("temp/GovTechSG-test-suites-apex-api-security-2b397cc/testData/");
+ internal static string testCertPath = GetLocalPath("temp/GovTechSG-test-suites-apex-api-security-2b397cc/");
+
+ internal SignatureMethod SignatureMethod { get; set; }
+ internal HttpMethod HttpMethod { get; set; }
+
+ internal FormData FormData { 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; }
+ public bool ResultBool => ExpectedResult == "true";
+
+ internal bool ErrorTest { get; set; }
+
+ internal bool 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()
{
- downloadFile(apexTestSuitePath, GetLocalPath("testSuite.zip"));
}
-
-
internal static string GetLocalPath(string relativeFileName)
{
- var localPath = Path.Combine(Path.GetDirectoryName(new Uri(Assembly.GetExecutingAssembly().CodeBase).LocalPath), relativeFileName.Replace('/', Path.DirectorySeparatorChar));
- return localPath;
+ return Path.Combine(Path.GetDirectoryName(new Uri(Assembly.GetExecutingAssembly().CodeBase).LocalPath), relativeFileName.Replace('/', Path.DirectorySeparatorChar));
}
- internal void downloadFile(string sourceURL, string downloadPath)
+
+ internal static string DownloadFile(string sourceURL, string downloadPath)
{
try
{
@@ -54,17 +66,17 @@ internal void downloadFile(string sourceURL, string downloadPath)
int bufferSize = 1024;
bufferSize *= 1000;
long existLen = 0;
- System.IO.FileStream saveFileStream;
- saveFileStream = new System.IO.FileStream(downloadPath,
- System.IO.FileMode.Create,
- System.IO.FileAccess.Write,
- System.IO.FileShare.ReadWrite);
+ FileStream saveFileStream;
+ saveFileStream = new FileStream(downloadPath,
+ FileMode.Create,
+ FileAccess.Write,
+ FileShare.ReadWrite);
System.Net.HttpWebRequest httpReq;
System.Net.HttpWebResponse httpRes;
httpReq = (System.Net.HttpWebRequest)System.Net.WebRequest.Create(sourceURL);
httpReq.AddRange((int)existLen);
- System.IO.Stream resStream;
+ Stream resStream;
httpRes = (System.Net.HttpWebResponse)httpReq.GetResponse();
resStream = httpRes.GetResponseStream();
@@ -78,11 +90,19 @@ internal void downloadFile(string sourceURL, string downloadPath)
}
saveFileStream.Close();
- if (System.IO.Directory.Exists(GetLocalPath("temp/")))
+ if (Directory.Exists(GetLocalPath("temp/")))
{
Directory.Delete(GetLocalPath("temp/"), true);
}
ZipFile.ExtractToDirectory(downloadPath, GetLocalPath("temp/"));
+
+ // determine the folder for the json files
+ string path = GetLocalPath("temp/");
+ DirectoryInfo dictiontory = new DirectoryInfo(path);
+ DirectoryInfo[] dir = dictiontory.GetDirectories();// this get all subfolder //name in folder NetOffice.
+ string dirName = dir[0].Name; //var dirName get name from array Dir;
+
+ return dirName;
}
catch (Exception ex)
{
@@ -94,25 +114,54 @@ 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 ?? "%s";
- version = paramFile.apiParam.version ?? "1.0";
- nonce = paramFile.apiParam.nonce ?? "%s";
- 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";
+ if (paramFile.ApiParam != null)
+ {
+ SignatureMethod = paramFile.ApiParam.SignatureMethod.ParseSignatureMethod(paramFile.ApiParam.Secret);
+ HttpMethod = paramFile.ApiParam.HttpMethod.ToEnum();
+
+ // queryString and formData must be saperated
+ FormData = FormData.SetupList(paramFile.ApiParam.FormData);
+ QueryData queryData = QueryData.SetupList(paramFile.ApiParam.QueryString);
+
+ //TimeStamp = paramFile.ApiParam.Timestamp ?? "%s";
+ TimeStamp = paramFile.ApiParam.Timestamp;
+
+ Version = paramFile.ApiParam.Version ?? "1.0";
+
+ //Nonce = paramFile.ApiParam.Nonce ?? "%s";
+ Nonce = paramFile.ApiParam.Nonce;
+
+ AuthPrefix = paramFile.ApiParam.AuthPrefix;
+ AppId = paramFile.ApiParam.AppID;
+
+ // combine queryString with URL
+ string queryString = "";
+ if (!paramFile.ApiParam.SignatureURL.IsNullOrEmpty())
+ {
+ queryString = queryData.ToString();
+ if (!queryString.IsNullOrEmpty())
+ {
+ // query start with ?, replace ? with & when url already contain queryString
+ if (paramFile.ApiParam.SignatureURL.IndexOf('?') > -1)
+ {
+ queryString = queryString.Replace("?", "&");
+ }
+ }
+ }
+ SignatureURL = paramFile.ApiParam.SignatureURL.IsNullOrEmpty() ? null : new Uri(string.Format("{0}{1}", paramFile.ApiParam.SignatureURL, queryString));
+
+ InvokeUrl = paramFile.ApiParam.InvokeURL.IsNullOrEmpty() ? null : new Uri(paramFile.ApiParam.InvokeURL);
+ Secret = paramFile.ApiParam.Secret ?? null;
+ Realm = paramFile.ApiParam.Realm ?? null;
+ Passphrase = paramFile.ApiParam.Passphrase ?? paramFile.Passphrase;// ?? "passwordp12";
+ }
+
+ TestId = paramFile.Id;
+ ExpectedResult = CommonExtensions.GetCharp(paramFile.ExpectedResult);
+ ErrorTest = paramFile.ErrorTest;
+
+ SkipTest = paramFile.SkipTest == null ? false : paramFile.SkipTest.Contains("c#");
+
}
catch (Exception ex)
{
@@ -120,86 +169,54 @@ internal void SetDetaultParams(TestParam paramFile)
}
}
- internal void SetApiList(Dictionary data = null)
+ internal static IEnumerable GetJsonFile(string fileName)
{
- try
+ if (!IsDataFileDownloaded)
{
- if (data != null)
+ if (IsDebug)
{
- foreach (var item in data)
- {
- var key = item.Key ?? "";
- var value = item.Value ?? "";
+ var folderName = "linkFolder";
- String value_s = value.ToString().Trim();
-
- if (!key.ToString().IsNullOrEmpty())
- {
- string[] _queryParams = { "" };
- string val = null;
-
- if (!value_s.IsNullOrEmpty() && !(value_s.StartsWith("{", StringComparison.InvariantCulture) && value_s.EndsWith("}", StringComparison.InvariantCulture)))
- {
-
- val = value_s.RemoveString(new string[] { "\\", "\\ ", " \\", "\"", "\\ ", "\n" }).Unescape();
-
- if (val == "True")
- val = "true";
- if (val == "False")
- val = "false";
- if (val.StartsWith("[", StringComparison.InvariantCulture) && val.EndsWith("]", StringComparison.InvariantCulture))
- {
-
- string[] _paramValues = { "" };
- val = val.RemoveString(new string[] { "[", "]", " " });
- _paramValues = val.Split(',');
- foreach (var paramvalue in _paramValues)
- {
- var _paramvalue = paramvalue;
- apiList.Add(key.ToString(), _paramvalue.Unescape());
- }
-
- }
- else
- {
- apiList.Add(key.ToString(), val);
- }
- }
- else
- {
- apiList.Add(key.ToString(), val);
- }
+ // set the path to test data files
+ testDataPath = GetLocalPath($"{folderName}/testData/");
+ testCertPath = GetLocalPath($"{folderName}/");
+ }
+ else
+ {
+ var folderName = DownloadFile(apexTestSuitePath, GetLocalPath("testSuite.zip"));
- }
- }
+ // set the path to test data files
+ testDataPath = GetLocalPath($"temp/{folderName}/testData/");
+ testCertPath = GetLocalPath($"temp/{folderName}/");
}
- }catch (Exception ex)
- {
- throw ex;
+ IsDataFileDownloaded = true;
}
- }
- internal IEnumerable
- GetJsonFile(string fileName)
- {
string path = testDataPath + fileName;
- TestDataService service = new TestDataService();
- var jsonData = service.LoadTestFile(path);
+ try
+ {
+ using (StreamReader reader = new StreamReader(path))
+ {
+ string _result = reader.ReadToEnd();
- return jsonData;
- }
+ IEnumerable result = JsonConvert.DeserializeObject>(_result);
+ return result;
+ }
+ }
+ catch (Exception ex)
+ {
+ throw ex;
+ }
+ }
- public static byte[] PEM(string type, byte[] data)
+ internal static void ValidateErrorMessage(TestParam testCase, Exception ex)
{
- 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);
+ // remove the file path that is machine dependent...
+ string err = ex.Message.Replace(testCertPath, "");
+
+ Assert.AreEqual(testCase.Result, err, "{0} - {1}", testCase.Id, testCase.Description);
}
}
}
diff --git a/ApiUtilLibTest/BaseStringTest.cs b/ApiUtilLibTest/BaseStringTest.cs
deleted file mode 100644
index 8d60b81..0000000
--- a/ApiUtilLibTest/BaseStringTest.cs
+++ /dev/null
@@ -1,126 +0,0 @@
-using NUnit.Framework;
-using System.Linq;
-using ApiUtilLib;
-using ApexUtilLibTest;
-using System.Collections.Generic;
-using System.Reflection;
-using System;
-
-namespace ApiUtilLibTest
-{
- [TestFixture]
- public class BaseStringTest
- {
-
- [Test]
- 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 baseString = ApiAuthorization.BaseString(
- "auth_prefix",
- SignatureMethod.HMACSHA256,
- "app-id-lpX54CVNltS0ye03v2mQc0b",
- new System.Uri(url),
- HttpMethod.GET,
- null,
- "1355584618267440511",
- "1502175057654",
- "1.0"
- );
-
- Assert.AreEqual(expectedBaseString, baseString);
- }
-
- [Test]
- public void BaseString_BugTest()
- {
-
- var formData = new ApiUtilLib.ApiList();
-
- formData.Add("Action", "SendMessage");
- formData.Add("MessageBody", "[{}]");
-
- 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/&Action=SendMessage&MessageBody=[{}]&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"
- );
-
- 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"
- );
-
- 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心邻坊";
-
- 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"
- ));
- }
-
-
-
- }
-}
diff --git a/ApiUtilLibTest/CommonExtensions.cs b/ApiUtilLibTest/CommonExtensions.cs
index 97cef13..92ec10c 100644
--- a/ApiUtilLibTest/CommonExtensions.cs
+++ b/ApiUtilLibTest/CommonExtensions.cs
@@ -1,5 +1,4 @@
using System;
-using System.Text;
using System.Collections.Generic;
using System.Linq;
@@ -15,31 +14,18 @@ public static class CommonExtensions
///
public static T ToEnum(this string value)
{
- if (value.IsNullOrEmpty())
- return default(T);
-
- return (T)Enum.Parse(typeof(T), value, true);
+ return value.IsNullOrEmpty() ? default : (T)Enum.Parse(typeof(T), value, true);
}
- public static string GetCharp(dynamic value)
+ public static string GetCharp(Dictionary 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 == "c#").FirstOrDefault().Value;
- if (result==null)
- result = value.Where(c => c.Key == "default").FirstOrDefault().Value;
+ string result = value.FirstOrDefault(c => c.Key == "c#").Value;
+ if (result == null)
+ {
+ result = value.FirstOrDefault(c => c.Key == "default").Value;
+ }
return result;
}
@@ -51,71 +37,27 @@ public static string GetCharp(Dictionary 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();
+ return value == null
+ ? secret == null ? ApiUtilLib.SignatureMethod.SHA256withRSA : ApiUtilLib.SignatureMethod.HMACSHA256
+ : 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;
+ return value == null || value == string.Empty;
}
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/L1SignatureTest.cs b/ApiUtilLibTest/L1SignatureTest.cs
deleted file mode 100644
index 63f8e0a..0000000
--- a/ApiUtilLibTest/L1SignatureTest.cs
+++ /dev/null
@@ -1,143 +0,0 @@
-using System;
-using System.Collections.Generic;
-using ApiUtilLib;
-using NUnit.Framework;
-
-namespace ApiUtilLibTest
-{
- [TestFixture]
- public class L1SignatureTest
- {
- const string baseString = "message";
-
- const string secret = "secret";
- const string secret2 = "5aes9wG4mQgWJBfKMuYLtrEtNslm1enWG2XpGaMk";
-
- const string expectedResult = "i19IcCmVwVmMVz2x4hhmqbgl1KeU0WnXBgoDYFeWNgs=";
-
- [Test]
- public void L1_BaseString_IsNullOrEmpty_Test()
- {
- string testBaseString = null;
-
- Assert.Throws(() => testBaseString.L1Signature(secret));
- Assert.Throws(() => "".L1Signature(secret));
-
- Assert.Throws(() => ApiAuthorization.L1Signature(null, secret));
- Assert.Throws(() => ApiAuthorization.L1Signature("", secret));
- }
-
- [Test]
- public void L1_Secret_IsNullOrEmpty_Test()
- {
- Assert.Throws(() => baseString.L1Signature(null));
- Assert.Throws(() => baseString.L1Signature(""));
-
- Assert.Throws(() => ApiAuthorization.L1Signature(baseString, null));
- Assert.Throws(() => ApiAuthorization.L1Signature(baseString, ""));
- }
-
- [Test]
- public void L1_Verify_Signature_Test()
- {
- Assert.IsTrue(expectedResult.VerifyL1Signature(secret, baseString));
-
- Assert.IsTrue(ApiAuthorization.VerifyL1Signature(expectedResult, secret, baseString));
- }
-
- [Test]
- public void L1_Verify_Signature_With_Wrong_Secret_Test()
- {
- Assert.IsFalse(expectedResult.VerifyL1Signature(secret + 'x', baseString));
-
- Assert.IsFalse(ApiAuthorization.VerifyL1Signature(expectedResult, secret + 'x', baseString));
- }
-
- [Test]
- public void L1_Verify_Signature_With_Wrong_BaseString_Test()
- {
- Assert.IsFalse(expectedResult.VerifyL1Signature(secret, baseString + 'x'));
-
- Assert.IsFalse(ApiAuthorization.VerifyL1Signature(expectedResult, secret, baseString + 'x'));
- }
-
- [Test]
- public void L1_BaseString_Encoding_Test()
- {
- var dataList = new List();
-
- dataList.Add(new string[]
- {
- "Lorem ipsum dolor sit amet, vel nihil senserit ei. Ne quo erat feugait disputationi.",
- secret,
- "cL3lY5/rhmkxMw/dCHCa4b9Lpp/soPPACnIxtQwRQI8=",
- "Basic Test"
- });
-
- // Chinese Traditional
- dataList.Add(new string[]
- {
- "道続万汁国圭絶題手事足物目族月会済。",
- secret,
- "wOHv68zuoiIjfJHW0hZcOk4lORyiAL/IGK8WSkBUnuk=",
- "UTF8 (Chinese Traditional) Test"
- });
-
- // Japanese
- dataList.Add(new string[]
- {
- "員ちぞど移点お告周ひょ球独狙チウソノ法保断フヒシハ東5広みぶめい質創ごぴ採8踊表述因仁らトつ。",
- secret,
- "L0ft4O8R2hxpupJVkLbgQpW0+HRw3KDgNUNf9DAEY7Y=",
- "UTF8 (Japanese) Test"
- });
-
- // Korean
- dataList.Add(new string[]
- {
- "대통령은 즉시 이를 공포하여야 한다, 그 자율적 활동과 발전을 보장한다.",
- secret,
- "a6qt0t/nQ3GQFAEVTH+LMvEi0D41ZaKqC7LWJcVmHlE=",
- "UTF8 (Korean) Test"
- });
-
- // Greek
- dataList.Add(new string[]
- {
- "Λορεμ ιπσθμ δολορ σιτ αμετ, τατιον ινιμιcθσ τε ηασ, ιν εαμ μοδο ποσσιμ ινvιδθντ.",
- secret,
- "WUGjbeO8Jy8Rvs5tD2biLHPR0+qtAmXeZKqX6acYL/4=",
- "UTF8 (Greek) Test"
- });
-
- dataList.Add(new string[]
- {
- "GET&https://test.example.com/api/v1/rest/level1/in-in/&auth_prefix_app_id=app-id-lpX54CVNltS0ye03v2mQc0b&auth_prefix_nonce=-4985265956715077053&auth_prefix_signature_method=HMACSHA256&auth_prefix_timestamp=1502159855341&auth_prefix_version=1.0¶m1=def+123",
- secret2,
- "8NxfLG0pFWEq1gZEttCW4lgrb92MFaqQpeUPRDx4CAE=",
- "L1 BaseString Happy Path Test"
- });
-
- dataList.Add(new string[]
- {
- "GET&https://test.example.com/api/v1/rest/level1/in-in/&ap=裕廊坊 心邻坊&auth_prefix_app_id=app-id-lpX54CVNltS0ye03v2mQc0b&auth_prefix_nonce=2851111144329605674&auth_prefix_signature_method=HMACSHA256&auth_prefix_timestamp=1502163903712&auth_prefix_version=1.0",
- secret2,
- "0fE74Vf/Q7nktxeezzrYcvOeq36Pd4CJ7Ez9I00cdJk=",
- "L1 BaseString with UTF8 Parameters Test"
- });
-
- // excute test
- foreach (var item in dataList)
- {
- L1Test(item[0], item[1], item[2], item[3]);
- }
- }
-
- void L1Test(string testBaseString, string testSecret, string expectedSignature, string messageTag)
- {
- var signature = testBaseString.L1Signature(testSecret);
-
- Assert.AreEqual(expectedSignature, signature, messageTag);
- }
- }
-}
diff --git a/ApiUtilLibTest/L2SignatureTest.cs b/ApiUtilLibTest/L2SignatureTest.cs
deleted file mode 100644
index b200933..0000000
--- a/ApiUtilLibTest/L2SignatureTest.cs
+++ /dev/null
@@ -1,134 +0,0 @@
-using NUnit.Framework;
-using System;
-using System.Security.Cryptography.X509Certificates;
-using ApiUtilLib;
-using System.Collections.Generic;
-using System.Security.Cryptography;
-using System.IO;
-using System.Reflection;
-
-namespace ApiUtilLibTest
-{
- [TestFixture]
- public class L2SignatureTest
- {
- // file name follow unix convention...
- static readonly string privateCertName = GetLocalPath("Certificates/ssc.alpha.example.com.p12");
- static readonly string publicCertName = GetLocalPath("Certificates/ssc.alpha.example.com.cer");
-
- const string baseString = "message";
- const string password = "passwordp12";
-
- static readonly RSACryptoServiceProvider privateKey = ApiAuthorization.PrivateKeyFromP12(privateCertName, password);
- static readonly RSACryptoServiceProvider publicKey = ApiAuthorization.PublicKeyFromCer(publicCertName);
-
- static string GetLocalPath(string relativeFileName)
- {
- var localPath = Path.Combine(Path.GetDirectoryName(new Uri(Assembly.GetExecutingAssembly().CodeBase).LocalPath), relativeFileName.Replace('/', Path.DirectorySeparatorChar));
-
- return localPath;
- }
-
- [Test]
- public void L2_BaseString_IsNullOrEmpty_Test()
- {
- const string testBaseString = null;
-
- Assert.Throws(() => testBaseString.L2Signature(privateKey));
- Assert.Throws(() => "".L2Signature(privateKey));
-
- Assert.Throws(() => ApiAuthorization.L2Signature(null, privateKey));
- Assert.Throws(() => ApiAuthorization.L2Signature("", privateKey));
- }
-
- [Test]
- public void L2_PrivateKey_IsNull_Test()
- {
- Assert.Throws(() => baseString.L2Signature(null));
-
- Assert.Throws(() => ApiAuthorization.L2Signature(baseString, null));
- }
-
- const string message = "Lorem ipsum dolor sit amet, vel nihil senserit ei. Ne quo erat feugait disputationi.";
-
- const string expectedSignature = "OsOqG/6hJfGmpCDkqBSZ4netNJDex1lzBYTzGjvjShSFEhJEzAD1zNHKg8Zf9Dve7o9lx3+Yrhrn68nMocgUSOvinhUNF3ttLWw36GzXG7BFJRSIbeUfY3C1vAhkjxmE8oiYoIWctT9qBOL/3GY5QD1H3DiWrb3OLUjy52dsAPmK2P5ofdo8Erd5/0mTxgX+OLMADLJUXq/Aajp1ZIF+djQipPHg0Ms1sNkSHCURxyCjRMKOHNe8DH15lKcApBBjd3XPlb+PGlFl/ffc5Q1ALnAOmsqN6hi8mW+R6Eb0QZsvoRMFSA7kQdWvkCrlWtP5ux+A2Ji/b48SWFSJurVz7yRBhJFDYlvTTCGcgLfwn3TJXa/YbCK05qy307i6X9jnfYaqSYhKC61ExTZYE2SyfagAcWVlSlq3bEovZXllKAwq8Yqyez2EqkOoSzJdj5gmJ1Pb4wN/ss7yYybRSvFShQunj/t6TiQDCJuhghXOfV5Scs/wqjDMWViqrA65YOQHROqAku81NiWFmciVHjk6bNAGsp7iE0p5XnA4z9B41ZVPsxsSXUg4tZvpUrZSpNzlGFBi/uEa1UYcrUd8APzBCvUa75RhZsfxRsCOkpyOEmqoFzg4ngCfegJzBpU5La9e0SOlRvW29p9CK7fS/FZC5YJtP1kucaBN5pX/mxaYeUQ=";
-
- [Test]
- public void L2_Verify_Signature_Test()
- {
- Assert.IsTrue(expectedSignature.VerifyL2Signature(publicKey, message));
- }
-
- [Test]
- public static void L2_Basic_Test()
- {
- var dataList = new List();
-
- dataList.Add(new string[]
- {
- message,
- expectedSignature,
- "Basic Test"
- });
-
- // Chinese Traditional
- dataList.Add(new string[]
- {
- "道続万汁国圭絶題手事足物目族月会済。",
- "BcgiwVRV5NPf2D15NMA7PjfheHY+jYeODlODuaAahd5dU/fuGanMcFpFuKJtxuCQLOE3veZMCC7V+hb/LEaBfkvXw+7gl8WtLu+T927Xs+3517AZm9vZ3nU34FIMAQpTJ8QbciFcd5FAybDiMuCfzvVE59yTSL/JmzSH4188/K6Z1uZ29VizrC2BwtVA/SHaWN1SMUGX6u0tQN5nE4dGZ9lRKm1Jd2rsUNDmqsmUZDJTbgoZbTJjNQklRv48GunXYBt/cfi9T5bryIVilqUphTIe6GrjLXZ1NVVCcMCJaCzAesX2dWUwLCEULcM4Vqw+7SWN20k4zcori5+QkwNH/eyViHwKiYY+neIusUU4HcafIXNHlYQjj1OVEXqPn2P7TzH9y+7TXheNrQ03P6NnRBjEW/bAgoCplbhYWnlNtu+BBNLn9+6rN/ePJz265Wetb16ZjG+ZwbV72PUkGxeFoT7cGBNvcC5zK4bFZV4AOr7TqE9Nt/xm9Xi7/gM0oU7zgYm+32LJaAxG2vax9EFdi3yBKrGRBYLaMH/6KEreZV+iZgLsqK/7tWEQom843iTmeRaxA4/Xeg3MLPyyxrWtQBqu2O/lv6pEf+scnc2Mg6gyc5uRm0luxJUBkqI6i/BAHGZRN1cDkMhWywAcWs3yxxV6qptFYxl6ubLCbCXtiw0=",
- "UTF8 (Chinese Traditional) Test"
- });
-
- // Japanese
- dataList.Add(new string[]
- {
- "員ちぞど移点お告周ひょ球独狙チウソノ法保断フヒシハ東5広みぶめい質創ごぴ採8踊表述因仁らトつ。",
- "RtNtUoRXhNFrFPMy5aJjPTB8yI9AyvLqIKmgjmarxZhB/aOLXSJtHHJMgufOLDsUzEyDenlPuRp4ju2Dp870P19H/IxLktTqkU3DZU35tqk21TWNQDmdl/P9YjY3BNJqU4YBV3A83KRDRhJh235Hjy20dbJqZAe/oL/8GboRd0W941Oj2VfC53SmVAYWQV1aJb4qV3cvoQG2OtcBMNA+ayG+0oTB9AtGZ3CqCUPqbfbb36oc81jYQj0nElHRew7QdclfpAUQaDgCF6svduji2rdXrU+fRYaiRPtm4F1zv9JVuIjKOZRqVQeQ3Nb/X8zUMEBNeWToQPmzoHz6hAEfzYUif2IJ1KqYooV29AwOvwu1itAeUwLtqlHK3QGJYaJVrw05EyAg1IsicAQ+szP+6t6Er3GjhRSXwIcpKdxLUHVtwFoK7E1L4FqxCW+Pokm97h0/rqWREt7DJvoIofQ8rtfEfao5CTaJOQyMRUx+Ds1Kytzpzd1T7aWFvdzFxo9YLfsZ/DzIy2F7iMi9c1b8WYfStlBvfUeEEeByZj+7FrvLMo9Ys5K/UweBfTcBHdPfCmW5RTJhmfK0p+EVsntLqkCbWMoQ6JdNZoASSB7E+NPGJuk3kuVo4sPnPy9vQlHsYJWktXjwTmBp4EZzfcia6U5TSWG0Wdn4ohCYQU2Y/sg=",
- "UTF8 (Japanese) Test"
- });
-
- // Korean
- dataList.Add(new string[]
- {
- "대통령은 즉시 이를 공포하여야 한다, 그 자율적 활동과 발전을 보장한다.",
- "GW0UWsS/bdP22Zd8D+WCZtz4LhyHF/8QemS7xTDPzhSlN+yjPtu7O0f/GGl3s+U1Cm3gUjMIRKbSKyi441Z57MD/9Ju8swtAJkHh9K/LPf/fFfm3UMN0EU7jeoEUkFG3AM8rR24ih16HFpK8RcDHDRL5+tAoU6au/JRLAnuRnhcOjunSC91OhTZJqSGYukoarLYVFxnLFyZPviZPe+aaFW4ZUrD+Kc6K2C/htHS1S/7NJedDsD8If31+dh/wdkIbvhQRDgWBJlSAoqOqmeFSRIIXW/VeufOjXZ9fxa/pmsBDN5BB5Fb3MguxebD61c0MN4F+gnRQ/5arKQL5oIn/QAGan6Ll7s7nUGpa88sdVKRqw/TVcqmYeIFgWBUhnk2p54tvWbCXski63z4QRC+4TZ/ITPgn1sDqsD5Qf9/Ly1RPpJPODNgIYb5i6vh94gchqrF1g3EphbJ3riWCqREoBuCD+yqS2DSE7QWg1gjaHtT8kzcxkt3KpJoLPlZPKt92y03/av8a0AXpc2H7pw2mJ4i13xDsiRKavE4R7pwrfUJxSxYD2jBPZgNTo3XxaboHZgFbvyyw3xHreSo9CmM0mL94qha4jv2TqGuURooiBfizxzuHeMub1t8VIAXOiTk/iQtBPvGLtsQzFW3TeAeZtiYSGBeKOmb6O1vtetBurQk=",
- "UTF8 (Korean) Test"
- });
-
- // Greek
- dataList.Add(new string[]
- {
- "Λορεμ ιπσθμ δολορ σιτ αμετ, τατιον ινιμιcθσ τε ηασ, ιν εαμ μοδο ποσσιμ ινvιδθντ.",
- "G6FezmgEqrnZNxqWfIE8Rcb49L3WQRcAQxQ0xX2sibejHHiOXPXU811OIsL7hsYmyLSSoY3IXTtu271MwfR1TTiODBnIqpgZ0jwmyKK7YoHUDqRgKmVscBnwotw2ntDn1eA2BAU2yKi+UOeUbDcY8dCK/qxdoKdvQg99zjmm1P4EG0dFlmh07oa2ByH4pgioaxI0sKQdDL14qbjrKOiFtfgdv5NEd1Q3kP240p9vLOoScPsRvRZlpWGPCUa0R9wQMtXZAKB3TVs+p8hu5ZHmG9JP2Jo5FRt8EkCG6V3Fg8qlbDO5m9B49atynVBsNSQkYKpCylokJI/mcESNciliQmOwkLmqh6YeELX82PSvnErIPRSAzrqkKYed/HI5gL2Z8pCOwohSfuMeoOrba3JeD98kMQHGwhw+pxSP6lnTCxLwLREhqgSrcXfymhc2TCbA/w/1gT3MjTIDjIF1HgtT2bPpjco62iuKPyrjejb4ARGcty5mlUjbPNUCD/DB4qgghnhbtvWJFJxF7Egs/BeDk5swyyvFBrlXPd/yhCpMJRAOZ0bK3Adj1ij0tVH/kHtDzRYZnF0ZQXZBlHyP2DMvlnJQbIDrTBuojRYFb8W7CPWc/P4RQIGwRv6ZvT+LLl+uuNpvNoVFc/EB0gKII819nINmCjcmuYhsboBLkJ9XHyE=",
- "UTF8 (Greek) Test"
- });
-
- dataList.Add(new string[]
- {
- "GET&https://test.example.com/api/v1/rest/level2/in-in/&auth_prefix_app_id=app-id-lpX54CVNltS0ye03v2mQc0b&auth_prefix_nonce=7798278298637796436&auth_prefix_signature_method=SHA256withRSA&auth_prefix_timestamp=1502163142423&auth_prefix_version=1.0",
- "hmAlq47s1yfjVjmgdMFK4OXk1Gm380Gy+cfhWrhuD2Dux523D8ywA4dn1UVsxukGYT6HnXm83tHwFiD0WWyL1nyHfViD7HDNo95pZrHFOEgrkmXzUUJ5g8aVO1EPxbMuJSlRECeLjbHSpkfyMljSVX/8KH9wt4S50Zs36f9kMcMpYZvK8Z1C7SUleoM+yIvCJPGj5mRIbZyieFXGJ0Zt3aJBsWwZeV4BcrSL/T1GQQLigOY5M7lmkmDmc/Wz7Ky82rTImbJB8HfclNkD0l4wy49vSLXh9IbIvUpErbzipjG1MmA4t1cPMA3PoyAXWAcrHHSSBBwcpD6yOr5HOXyYAKDHz5IPZLRtAOft4BPwKygzOhDR881rfFbOT8IOOSI20uXtlTa6KTIUq9Opvc0g2gnUgai0qfm6uCOi8vmEBKj5z5mOrG+39FhXbB5FEpv6ZuFPP3ATtkIg9nSWStifbkq4fJCTSAsXRF4ragNWQRgc46B4zUwWpnRZ7s83oi808A2vQXLFExPTjW4DBUTtBt+O9Z1Gp7Z5yRHekut4vLaNqrIvRcLqmn92G9QqfkFJmQGrOH7SpAtgDhGHzaNc5C+OTmCkxhFKtoblO4ItwIh5X4i3gePDcHrBDEDTwjHPjXEFbDOL0JKrA6qlgA8evXVZXLugHAs6bO5zJhacXTU=",
- "L2 BaseString Happy Path Test"
- });
-
- dataList.Add(new string[]
- {
- "GET&https://test.example.com/api/v1/rest/level2/in-in/&ap=裕廊坊 心邻坊&auth_prefix_app_id=app-id-lpX54CVNltS0ye03v2mQc0b&auth_prefix_nonce=7231415196459608363&auth_prefix_signature_method=SHA256withRSA&auth_prefix_timestamp=1502164219425&auth_prefix_version=1.0&oq=c# nunit mac&q=c# nunit mac",
- "q7gaCiA1/oTYtCwrj3ZMumSwuIsF++NnjDzoexpU1OunABGrVxmQrJIcDXnVkKA1gaDZf0JXJMCfyfu9r/F2S8raQUyyxt0edjEjzCPFnF+1N6Hhj5KYMxT2lggv6mSblqi0tWUXD/tSgw0ssRfNM0+v0xo4G2kzbOlZe/JzIGUX/FtkanW5xJGS0wrZIfoAO3p1OVwghN6FZkKZ3o9gH3VGZItJKEa6agPqlaH2XVbF3636nKlGRKbcc+fNDd2/EFqqxDxS3gx21a1cRJT6SfOZ7lqbnAkxvC9QW/mw42hObUx/RjyGI1QqMdJ6aRkLHQkHZ9LT41kJKg+AKsLlo/ogTdwxyekok9jt2UgVm7EFA9lkAB2T8T8q1eDalpby6BHCSBiQn9j7FhcVw6EFgWPLzlXCZl/sDzbrqX90AR38VAntTS+2f+Uc5vKxrJ41m2zD2HIYLw0oiXEjODPfvRPtCt478CCz1Y6xnMgD4hJwhTN8pyuMvdoxwWKWcCsdZhXPZrzNm/GZljT3jdRekUgAt3fBOwbji+OVt7TUqUKpYMadExrhiTRKV+dh8EUtdnJoM056YC5Ta2w/Iuxai6dT1QRW7bZOrUfOljD3Miq6Xyx3nnUW2sVQbeJkxFBJVbFAsxtpMRICcTg63VRrcGmhqxTX0QyqsxFqSrbj+eQ=",
- "L2 BaseString with UTF8 Parameters Test"
- });
-
- // excute test
- foreach (var item in dataList)
- {
- L2Test(item[0], privateKey, item[1], item[2]);
- }
- }
-
- public static void L2Test(string baseString, RSACryptoServiceProvider privateKey, string expectedSignature, string message)
- {
- var signature = baseString.L2Signature(privateKey);
-
- Assert.AreEqual(expectedSignature, signature, message);
- }
- }
-}
diff --git a/ApiUtilLibTest/TestDataService.cs b/ApiUtilLibTest/TestDataService.cs
deleted file mode 100644
index dbf15d2..0000000
--- a/ApiUtilLibTest/TestDataService.cs
+++ /dev/null
@@ -1,48 +0,0 @@
-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
index bd5cbdf..91f9c58 100644
--- a/ApiUtilLibTest/TestDataTest.cs
+++ b/ApiUtilLibTest/TestDataTest.cs
@@ -1,296 +1,378 @@
using ApiUtilLib;
-using NUnit.Framework;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using Newtonsoft.Json.Serialization;
+using NUnit.Framework;
+using System;
+using System.IO;
using System.Security.Cryptography;
-using System.Text.RegularExpressions;
-
-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);
+using System.Text.RegularExpressions;
- if (skipTest == null || !skipTest.Contains("c#"))
- {
+namespace ApexUtilLibTest
+{
+ [TestFixture()]
+ public class TestDataTest : BaseService
+ {
+ [Test(), TestCaseSource(nameof(GetJsonFile), new object[] { "getSignatureBaseString.json" })]
+ public void TestBaseString(TestParam testCase)
+ {
+ SetDetaultParams(testCase);
- try
- {
- var baseString = ApiAuthorization.BaseString(authPrefix, signatureMethod, appId, signatureURL, httpMethod, apiList, nonce, timeStamp, version);
- Assert.AreEqual(expectedResult, baseString);
- actualPass++;
- }
- catch (Exception ex)
- {
- ex = ex;
- }
- }
- else
- {
- actualPass++;
- }
- }
+ if (SkipTest)
+ {
+ Assert.Ignore();
}
- catch (Exception ex)
+ else
{
- throw ex;
- }
-
- Assert.AreEqual(expectedPass, actualPass);
-
+ if (testCase.ErrorTest)
+ {
+ Assert.Fail("No exception defined for test case...");
+ }
+ else
+ {
+ string baseString = ApiAuthorization.BaseString(AuthPrefix, SignatureMethod, AppId, SignatureURL, FormData, HttpMethod, Nonce, TimeStamp, Version);
+ Assert.AreEqual(ExpectedResult, baseString, testCase.ToString());
+ }
+ }
}
- [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++;
- }
- }
+ [Test(), TestCaseSource(nameof(GetJsonFile), new object[] { "verifyL1Signature.json" })]
+ public void VerifyL1Signature(TestParam testCase)
+ {
+ SetDetaultParams(testCase);
+ if (SkipTest)
+ {
+ Assert.Ignore();
}
- catch (Exception)
+ else
{
- 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);
+ if (testCase.ErrorTest)
+ {
+ Assert.Fail("No exception defined for test case...");
+ }
+ else
+ {
+ string message = testCase.Message;
+ string signature = testCase.ApiParam.Signature;
+ bool result = signature.VerifyL1Signature(Secret, message);
+
+ Assert.AreEqual(ExpectedResult.ToBool(), result, testCase.ToString());
+ }
+ }
+ }
+
+ [Test(), TestCaseSource(nameof(GetJsonFile), new object[] { "verifyL2Signature.json" })]
+ public void VerifyL2Signature(TestParam testCase)
+ {
+ SetDetaultParams(testCase);
+
+ if (SkipTest)
+ {
+ Assert.Ignore();
+ }
+ else
+ {
+ if (testCase.ErrorTest)
+ {
+ Assert.Fail("No exception defined for test case...");
+ }
+ else
+ {
+ string message = testCase.Message;
+ string signature = testCase.ApiParam.Signature;
+ string certPath = testCertPath + testCase.PublicKeyFileName;
+
+ RSACryptoServiceProvider publicKey = ApiAuthorization.GetPublicKey(certPath, Passphrase);
+ bool result = signature.VerifyL2Signature(publicKey, message);
+
+ Assert.AreEqual(ExpectedResult.ToBool(), result, testCase.ToString());
+ }
+ }
}
+
+ private Exception GetTokenException(AuthParam authParam) where T : Exception
+ {
+ return Assert.Throws(() => { string result = ApiAuthorization.TokenV2(authParam).Token; });
+ }
- [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.privateCertFileNameP12;
- string privateCertPath = testCertPath + certName;
- RSACryptoServiceProvider privateKey = null;
- if (!certName.IsNullOrEmpty())
- privateKey = ApiAuthorization.PrivateKeyFromP12(GetLocalPath(privateCertPath), passphrase);
- var result = ApiAuthorization.Token(realm, authPrefix, httpMethod, signatureURL, appId, secret, apiList, privateKey, nonce, timeStamp, version);
- if(timeStamp.Equals("%s") || nonce.Equals("%s"))
+ [Test(), TestCaseSource(nameof(GetJsonFile), new object[] { "getSignatureToken.json" })]
+ public void GetToken(TestParam testCase)
+ {
+ SetDetaultParams(testCase);
+
+ if (SkipTest)
+ {
+ Assert.Ignore();
+ }
+ else
+ {
+ RSACryptoServiceProvider privateKey = null;
+ if (!testCase.ApiParam.PrivateKeyFileName.IsNullOrEmpty())
+ {
+ privateKey = ApiAuthorization.GetPrivateKey(Path.Combine(testCertPath, testCase.ApiParam.PrivateKeyFileName), Passphrase);
+ }
+
+ AuthParam authParam = new AuthParam
+ {
+ url = SignatureURL,
+ httpMethod = HttpMethod,
+ appName = AppId,
+ appSecret = Secret,
+ formData = FormData,
+ privateKey = privateKey,
+ timestamp = TimeStamp,
+ nonce = Nonce
+ };
+
+ if (testCase.ErrorTest)
+ {
+ //Assert.Fail("No exception defined for test case...");
+
+ Exception ex;
+ switch (testCase.Exception)
+ {
+ case "ArgumentNullException":
+ {
+ ex = GetTokenException(authParam);
+ break;
+ }
+ default:
{
- Match m = Regex.Match(result, "apex_(l[12])_([ei]g)_signature=(\\S+)", System.Text.RegularExpressions.RegexOptions.IgnoreCase);
- String signature_value = "";
- if (m.Success)
- signature_value = m.Groups[3].Value;
-
- expectedResult = expectedResult.Replace("signature=\"%s\"", "signature=" + signature_value);
- }
- Assert.AreEqual(expectedResult, result);
- actualPass++;
- }
- catch (Exception ex)
- {
- Assert.AreEqual(expectedResult, ex.Message);
- if (errorTest)
- actualPass++;
- }
- }
- else
- {
- actualPass++;
- }
- }
+ throw new Exception($"Exception ({testCase.Exception}) not defined in json, please update the test case.");
+ }
+ };
+ ValidateErrorMessage(testCase, ex);
+ }
+ else
+ {
+ string result = ApiAuthorization.TokenV2(authParam).Token;
+
+ var expectedResult = ExpectedResult;
+ if (string.IsNullOrEmpty(TimeStamp) || string.IsNullOrEmpty(Nonce))
+ {
+ if (string.IsNullOrEmpty(TimeStamp))
+ {
+ expectedResult = expectedResult.Replace("timestamp=\"%s\"", "timestamp=\"" + authParam.timestamp + "\"");
+ }
+
+ if (string.IsNullOrEmpty(Nonce))
+ {
+ expectedResult = expectedResult.Replace("nonce=\"%s\"", "nonce=\"" + authParam.nonce + "\"");
+ }
+
+ Match m = Regex.Match(result, "apex_(l[12])_([ei]g)_signature=(\\S+)", RegexOptions.IgnoreCase);
+ string signature_value = "";
+ if (m.Success)
+ {
+ signature_value = m.Groups[3].Value;
+ }
+
+ expectedResult = expectedResult.Replace("signature=\"%s\"", "signature=" + signature_value);
+ }
+
+ Assert.AreEqual(expectedResult, result, testCase.ToString());
+ }
}
- catch (Exception ex)
- {
- throw ex;
- }
-
- Assert.AreEqual(expectedPass, actualPass);
- }
-
- [Test()]
- public void GetL1Signature()
- {
- var jsonData = GetJsonFile("getL1Signature.json");
- int expectedPass = jsonData.Count();
- int actualPass = 0;
+ }
+
+ private Exception GetL1SignatureException(TestParam testCase) where T : Exception
+ {
+ return Assert.Throws(() => testCase.Message.L1Signature(testCase.ApiParam.Secret));
+ }
- try
- {
- foreach (var test in jsonData)
- {
- SetDetaultParams(test);
+ [Test(), TestCaseSource(nameof(GetJsonFile), new object[] { "getL1Signature.json" })]
+ public void GetL1Signature(TestParam testCase)
+ {
+ SetDetaultParams(testCase);
+ if (SkipTest)
+ {
+ Assert.Ignore();
+ }
+ else
+ {
+ if (testCase.ErrorTest)
+ {
+ //Assert.Fail("No exception defined for test case...");
+
+ Exception ex;
+ switch(testCase.Exception)
+ {
+ case "ArgumentException":
+ {
+ ex = GetL1SignatureException(testCase);
+ break;
+ }
+ case "ArgumentNullException":
+ {
+ ex = GetL1SignatureException(testCase);
+ break;
+ }
+ default:
+ {
+ throw new Exception($"Exception ({testCase.Exception}) not defined in json, please update the test case.");
+ }
+ };
+ ValidateErrorMessage(testCase, ex);
+ }
+ else
+ {
+ string message = testCase.Message;
+ string result = message.L1Signature(Secret);
+ Assert.AreEqual(ExpectedResult, result, testCase.ToString());
+ }
+ }
+ }
+
+ private Exception GetL2SignatureException(TestParam testCase, RSACryptoServiceProvider privateKey) where T : Exception
+ {
+ return Assert.Throws(() => testCase.Message.L2Signature(privateKey));
+ }
- if (skipTest == null || !skipTest.Contains("c#"))
- {
- var message = test.message;
- try
- {
- var result = message.L1Signature(secret);
- Assert.AreEqual(expectedResult, result);
- actualPass++;
- }
- catch (Exception ex)
- {
- Assert.AreEqual(expectedResult, ex.Message);
- if (errorTest)
- actualPass++;
- }
+ [Test(), TestCaseSource(nameof(GetJsonFile), new object[] { "getL2Signature.json" })]
+ public void GetL2Signature(TestParam testCase)
+ {
+ SetDetaultParams(testCase);
+
+ if (SkipTest)
+ {
+ Assert.Ignore();
+ }
+ else
+ {
+ RSACryptoServiceProvider privateKey = null;
+ if (!testCase.ApiParam.PrivateKeyFileName.IsNullOrEmpty())
+ {
+ privateKey = ApiAuthorization.GetPrivateKey(Path.Combine(testCertPath, testCase.ApiParam.PrivateKeyFileName), Passphrase);
+ }
+
+ if (testCase.ErrorTest)
+ {
+ //Assert.Fail("No exception defined for test case...");
+
+ Exception ex;
+ switch (testCase.Exception)
+ {
+ case "ArgumentException":
+ {
+ ex = GetL2SignatureException(testCase, privateKey);
+ break;
+ }
+ case "ArgumentNullException":
+ {
+ ex = GetL2SignatureException(testCase, privateKey);
+ break;
+ }
+ default:
+ {
+ throw new Exception($"Exception ({testCase.Exception}) not defined in json, please update the test case.");
+ }
+ };
+ ValidateErrorMessage(testCase, ex);
+ }
+ else
+ {
+ string result = testCase.Message.L2Signature(privateKey);
+
+ Assert.AreEqual(ExpectedResult, result, testCase.ToString());
+ }
+ }
+ }
+
+ private Exception GetPrivateKeyException(string keyFileName, string passphrase) where T : Exception
+ {
+ return Assert.Throws(() => ApiAuthorization.GetPrivateKey(keyFileName, passphrase));
+ }
+
+ private Exception GetPublicKeyException(string keyFileName, string passphrase) where T : Exception
+ {
+ return Assert.Throws(() => ApiAuthorization.GetPublicKey(keyFileName, passphrase));
+ }
+
+ [Test, TestCaseSource(nameof(GetJsonFile), new object[] { "verifySupportedKeyFileType.json" })]
+ public void VerifySupportedKeyFileType(TestParam testCase)
+ {
+ SetDetaultParams(testCase);
+
+ if (SkipTest)
+ {
+ Assert.Ignore();
+ }
+ else
+ {
+ if (testCase.ErrorTest)
+ {
+ Exception ex = null;
+ if (testCase.ApiParam != null && testCase.ApiParam.PrivateKeyFileName != null)
+ {
+ string keyFileName = $"{testCertPath}/{testCase.ApiParam.PrivateKeyFileName}";
+ string passphrase = testCase.ApiParam.Passphrase;
+
+ switch (testCase.Exception)
+ {
+ case "ArgumentException":
+ {
+ ex = GetPrivateKeyException(keyFileName, passphrase);
+ break;
+ }
+ case "CryptographicException":
+ {
+ ex = GetPrivateKeyException(keyFileName, passphrase);
+ break;
+ }
+ case "FileNotFoundException":
+ {
+ ex = GetPrivateKeyException(keyFileName, passphrase);
+ break;
+ }
+ case "PemException":
+ {
+ ex = GetPrivateKeyException(keyFileName, passphrase);
+ break;
+ }
+ case "InvalidCastException":
+ {
+ ex = GetPrivateKeyException(keyFileName, passphrase);
+ break;
+ }
+ default:
+ throw new Exception($"Exception ({testCase.Exception}) not defined in json, please update the test case.");
+ }
+ ValidateErrorMessage(testCase, ex);
}
- else
- {
- actualPass++;
- }
- }
- }
- catch (Exception ex)
- {
- throw ex;
- }
-
- Assert.AreEqual(expectedPass, actualPass);
-
+
+ if (testCase.PublicKeyFileName != null)
+ {
+ string keyFileName = $"{testCertPath}/{testCase.PublicKeyFileName}";
+ string passphrase = testCase.Passphrase;
+
+ switch (testCase.Exception)
+ {
+ case "FileNotFoundException":
+ {
+ ex = GetPublicKeyException(keyFileName, passphrase);
+ break;
+ }
+ case "CryptographicException":
+ {
+ ex = GetPublicKeyException(keyFileName, passphrase);
+ break;
+ }
+ default:
+ throw new Exception($"Exception ({testCase.Exception}) not defined in json, please update the test case.");
+ };
+ }
+
+ ValidateErrorMessage(testCase, ex);
+ }
+ else
+ {
+ RSACryptoServiceProvider privateKey = ApiAuthorization.GetPrivateKey($"{testCertPath}/{testCase.ApiParam.PrivateKeyFileName}", testCase.ApiParam.Passphrase);
+ string signature = testCase.Message.L2Signature(privateKey);
+
+ RSACryptoServiceProvider publicKey = ApiAuthorization.GetPublicKey($"{testCertPath}/{testCase.PublicKeyFileName}", testCase.Passphrase);
+ bool result = signature.VerifyL2Signature(publicKey, testCase.Message);
+
+ Assert.AreEqual(testCase.ApiParam.Signature, signature, "Signature - {0} - {1}", testCase.Id, testCase.Description);
+ Assert.IsTrue(result, "{0} - {1}", testCase.Id, testCase.Description);
+ }
+ }
}
-
- [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 result = null;
- if (!certName.IsNullOrEmpty())
- result = ApiAuthorization.GetL2SignatureFromPEM(privateCertPath,message, passphrase);
-
- Assert.AreEqual(expectedResult, result);
- actualPass++;
- }
- catch (Exception ex)
- {
- Assert.AreEqual(expectedResult, ex.Message);
- if (errorTest)
- actualPass++;
- }
- }
- else
- {
- actualPass++;
- }
- }
- }
- catch (Exception ex)
- {
- throw ex;
- }
-
- Assert.AreEqual(expectedPass, actualPass);
-
- }
- }
+ }
}
diff --git a/ApiUtilLibTest/TestParam.cs b/ApiUtilLibTest/TestParam.cs
index 4b3ec19..75d32c4 100644
--- a/ApiUtilLibTest/TestParam.cs
+++ b/ApiUtilLibTest/TestParam.cs
@@ -1,73 +1,87 @@
-using System;
-using System.Collections.Generic;
+using System.Collections.Generic;
+using System.Linq;
+
namespace ApexUtilLibTest
{
public class TestParam
{
- public string id { get; set; }
-
- public string description { get; set; }
+ public string Id { get; set; }
- public APIParam apiParam { get; set; }
+ public string Description { get; set; }
- public string publicCertFileName { get; set; }
+ public APIParam ApiParam { get; set; }
- public string[] skipTest { get; set; }
+ public string PublicKeyFileName { get; set; }
+ public string Passphrase { get; set; }
- public string message { get; set; }
+ public string[] SkipTest { get; set; }
+ internal bool Skip => SkipTest != null && SkipTest.Contains("c#");
- public string debug { get; set; }
- public string testTag { get; set; }
+ public string Message { get; set; }
- public bool errorTest { get; set; }
+ public string Debug { get; set; }
- public Dictionary expectedResult { get; set; }
- }
+ public string TestTag { get; set; }
- public class APIParam
- {
- public string realm{ get; set; }
+ public bool ErrorTest { get; set; }
- public string appID { get; set; }
+ public Dictionary ExceptionType { get; set; }
+ public string Exception => ExceptionType != null ? ExceptionType.FirstOrDefault(c => c.Key == "c#").Value ?? ExceptionType.FirstOrDefault(c => c.Key == "default").Value : "";
- public string authPrefix { get; set; }
+ public Dictionary ExpectedResult { get; set; }
+ public string Result
+ {
+ get
+ {
+ string result = ExpectedResult.FirstOrDefault(c => c.Key == "c#").Value;
+ if (result == null)
+ {
+ result = ExpectedResult.FirstOrDefault(c => c.Key == "default").Value;
+ }
- public string secret { get; set; }
+ return result;
+ }
+ }
- public string invokeURL { get; set; }
+ public override string ToString()
+ {
+ return Skip ? $"{Id} - {Description} >>> {Result} <<<" : $"{Id} - {Description}";
+ }
+ }
- public string signatureURL { get; set; }
+ public class APIParam
+ {
+ public string Realm{ get; set; }
- public string httpMethod { get; set; }
+ public string AppID { get; set; }
- public string signature { get; set; }
+ public string AuthPrefix { get; set; }
- public string privateCertFileName { get; set; }
+ public string Secret { get; set; }
- public string privateCertFileNameP12 { get; set; }
+ public string InvokeURL { get; set; }
- public string passphrase { get; set; }
+ public string SignatureURL { get; set; }
- public string signatureMethod { get; set; }
+ public string HttpMethod { get; set; }
- public string nonce { get; set; }
+ public string Signature { get; set; }
- public string timestamp { get; set; }
+ public string PrivateKeyFileName { get; set; }
+ public string Passphrase { get; set; }
- public string version { get; set; }
+ public string SignatureMethod { get; set; }
- public Dictionary queryString { get; set; }
+ public string Nonce { get; set; }
- public Dictionary formData { get; set; }
+ public string Timestamp { get; set; }
- }
+ public string Version { get; set; }
- public class ExpectedResult
- {
- public string golang { get; set; }
- public string nodejs { get; set; }
- }
+ public Dictionary QueryString { get; set; }
+ public Dictionary FormData { get; set; }
+ }
}
diff --git a/ApiUtilLibTest/addLinks.sh b/ApiUtilLibTest/addLinks.sh
new file mode 100644
index 0000000..186fbb0
--- /dev/null
+++ b/ApiUtilLibTest/addLinks.sh
@@ -0,0 +1,23 @@
+#!/bin/bash
+# Proper header for a Bash script.
+
+# dependency: must clone test-suites-apex-api-security
+# at the save level as csharp-apex2-net5-skd
+
+CREATE_LINK=false
+
+cd bin/Debug
+mkdir -p linkFolder
+cd linkFolder
+
+FOLDER=../../../../../test-suites-apex-api-security
+
+FOLDER_NAME=testData
+[ -L "${FOLDER_NAME}" ] && unlink ${FOLDER_NAME} && echo "Unlink folder - ${FOLDER_NAME}"
+if [ ${CREATE_LINK} = "true" ]; then ln -s ${FOLDER}/${FOLDER_NAME} ${FOLDER_NAME} && echo ">>>Add link folder - ${FOLDER_NAME}"; fi
+
+FOLDER_NAME=cert
+[ -L "${FOLDER_NAME}" ] && unlink ${FOLDER_NAME} && echo "Unlink folder - ${FOLDER_NAME}"
+if [ ${CREATE_LINK} = "true" ]; then ln -s ${FOLDER}/${FOLDER_NAME} ${FOLDER_NAME} && echo ">>>Add link folder - ${FOLDER_NAME}"; fi
+
+cd ../../../
\ No newline at end of file
diff --git a/ApiUtilLibTest/packages.config b/ApiUtilLibTest/packages.config
index 00c9ff6..8ff1025 100644
--- a/ApiUtilLibTest/packages.config
+++ b/ApiUtilLibTest/packages.config
@@ -1,5 +1,6 @@
-
-
+
+
+
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 96c05df..5bc7ed1 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,6 +3,18 @@ All notable changes to this project will be documented in this file.
This project adheres to Semantic Versioning.
+### V2.0.1-SNAPSHOT
++ 2021-11-22 - Remove Test Harness
+
+
+### V2.0.1-SNAPSHOT
++ 2021-08-08 - Restructure PrivateKey and PublicKey API.
+ - Update Test Cases
+
+### V2.0-SNAPSHOT
++ 2021-07-16 - Fix bugs related to form post with array.
+ - Add APEX2 supports and code refactors
+
### V1.6-SNAPSHOT
+ 2019-03-22 - Update ApiAuthorization class to support non-standard http port
diff --git a/README.md b/README.md
index 063dab4..83e4d68 100644
--- a/README.md
+++ b/README.md
@@ -3,16 +3,19 @@
[![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](http://makeapullrequest.com)
[![MIT License](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/GovTechSG/csharp-apex-api-security/blob/master/LICENSE)
-A C# helper utility that construct and sign HTTP Authorization header scheme for API authentication and verification
+A C# helper utility that construct and sign HTTP Authorization header scheme for API authentication and verification.
-## Table of Contents
+## Table of Contents (version 2.0 - beta (2021-08-08 v2.0.1))
- [Getting Started](#getting-started)
* [Prerequisites](#prerequisites)
- * [APIList Interface](#using-the-apilist-class)
+ * [Query String and FormData Class](#using-the-querydata-and-formdata-class)
+ [Generate QueryString](#generate-querystring)
+ [Generate FormData](#generate-formdata)
- * [Constructing HMAC256 L1 Authorization Header](#how-to-generate-hmac256-l1-authorization-header)
- * [Constructing RSA256 L2 Authorization Header](#how-to-generate-rsa256-l2-authorization-header)
+ * [Constructing L1 Authorization Header](#how-to-generate-l1-authorization-header)
+ * [Supported Private Key File Type](#supported-private-key-file-type)
+ * [Constructing L2 Authorization Header](#how-to-generate-l2-authorization-header)
+ * [Cross Zone API from Internet to Intranet](#how-to-generate-l21-authorization-header)
+ * [Cross Zone API from Intranet to Internet](#how-to-generate-l12-authorization-header)
- [Release](#release)
- [Contributing](#contributing)
- [License](#license)
@@ -22,8 +25,8 @@ A C# helper utility that construct and sign HTTP Authorization header scheme for
### Prerequisites
+ .NET Framework 4.6.1
-+ Visual Studio IDE 2015/2017 Community+
-+ NUnit Framework 3.8+ (for Windows Platform)
++ Visual Studio 2019 Community
++ NUnit Framework 3.13+
Make sure that all unit test cases are passed before using the library.
@@ -40,32 +43,32 @@ For windows users , NUnitTestAdapter have to be installed before you can run the
4. Click install, and select existing project ApiSecuritySolution to add the adapter.
-### Using the ApiList Class
-The ApiUtilLib Library provide the utility class ApiList to construct request Query String and Form Data.
+### Using the QueryData and FormData Class
+The ApiUtilLib Library provide the utility class QueryData to construct request Query String and Form Data.
#### Generate QueryString
```
- var queryParam = new ApiUtilLib.ApiList();
+ var queryData = new QueryData();
- queryParam.Add("clientId", "1256-1231-4598");
- queryParam.Add("accountStatus", "active");
- queryParam.Add("txnDate", "2017-09-29");
+ queryData.Add("clientId", "1256-1231-4598");
+ queryData.Add("accountStatus", "active");
+ queryData.Add("txnDate", "2017-09-29");
- string queryString = queryParam.ToQueryString();
+ string queryString = queryData.ToString();
- string baseUrl = string.Format("https://example.com/resource/?{0}", queryString);
- // https://example.com/resource/?clientId=1256-1231-4598&accountStatus=active&txnDate=2017-09-29
+ string baseUrl = string.Format("https://example.com/resource{0}", queryString);
+ // https://example.com/resource?clientId=1256-1231-4598&accountStatus=active&txnDate=2017-09-29
```
#### Generate FormData
```
- var formData = new ApiUtilLib.ApiList();
+ var formData = new FormData();
formData.Add("phoneNo", "+1 1234 4567 890");
formData.Add("street", "Hellowood Street");
formData.Add("state", "AP");
- string formData = formData.ToFormData();
+ string formData = formData.ToString();
// phoneNo=%2B1+1234+4567+890&street=Hellowood+Street&state=AP
```
@@ -74,162 +77,203 @@ The ApiUtilLib Library provide the utility class ApiList to construct request Qu
For **formData** parameter used for Signature generation, the key value parameters **do not** need to be URL encoded,
When you use this client library method **ApiAuthorization.HttpRequest**, it will do the url-encoding during the HTTP call
-### How to Generate HMAC256 L1 Authorization Header
+### How to Generate L1 Authorization Header
```
-public static void L1Sample()
+public void L1Sample()
{
- // application realm
- string realm = "<>";
+ var URL = "https://{gatewayName}.api.gov.sg/api/v1/resource";
+ var APP_NAME = "{appName}";
+ var APP_SECRET = "{appSecret}";
- // authorization prefix (i.e 'Apex_l1_eg' )
- string authPrefix = "<>";
+ // prepare form data
+ var formData = new FormData();
+ formData.Add("q", "how to validate signature in pdf");
+ formData.Add("ei", "yAr8YLmwCM_Fz7sPsKmLoAU");
- // app id and app secret assign to the application
- string appId = "<>";
- string appSecret = "<>";
+ var authParam = new AuthParam()
+ {
+ url = new Uri($"{URL}"),
+ httpMethod = HttpMethod.POST,
- // api signing Internet gateway name and path (for Intranet i.e -pvt.i.api.gov.sg)
- string signingGateway = ".e.api.gov.sg";
- string apiPath = "api/v1/l1/";
+ appName = APP_NAME,
+ appSecret = APP_SECRET,
- // query string (optional)
- var queryParam = new ApiUtilLib.ApiList();
+ formData = formData
+ };
- queryParam.Add("clientId", "1256-1231-4598");
- queryParam.Add("accountStatus", "active");
- queryParam.Add("txnDate", "2017-09-29");
+ // get the authorization token for L1
+ var authToken = ApiAuthorization.TokenV2(authParam);
- string queryString = queryParam.ToQueryString();
+ Console.WriteLine($"\n>>> BaseString :: '{authToken.BaseString}'<<<");
+ Console.WriteLine($"\n>>> Authorization Token :: '{authToken.Token}'<<<");
- string baseUrl = string.Format("https://{0}/{1}?{2}", signingGateway, apiPath, queryString);
+ // make api call with authToken.Token
+}
+```
- // form data (optional)
- var formData = new ApiUtilLib.ApiList();
+### Supported Private Key File Type
+1. .pem/.key - pkcs#1 base64 encoded text file
+2. .pem/.key - pkcs#8 base64 encoded text file
+3. .p12/.pfx - pkcs#12 key store
- formData.Add("phoneNo", "+1 1234 4567 890");
- formData.Add("street", "Hellowood Street");
- formData.Add("state", "AP");
+### How to Generate L2 Authorization Header
+```
+public void L2Sample()
+{
+ var URL = "https://{gatewayName}.api.gov.sg/api/v1/resource";
+ var APP_NAME = "{appName}";
+ var PRIVATE_KEY_FILE_NAME = "privateKey.key";
+ var PRIVATE_KEY_PASSPHRASE = "{passphrase}";
+
+ // get the private key from pem file (in pkcs1 format)
+ var privateKey = ApiAuthorization.GetPrivateKey(PRIVATE_KEY_FILE_NAME, PRIVATE_KEY_PASSPHRASE);
+
+ // prepare queryString
+ var queryData = new QueryData();
+ queryData.Add("view", "net-5.0");
+ queryData.Add("system", "C# sample code");
+
+ // get url safe querystring from ToString()
+ Console.WriteLine($">>> Query String >>>{queryData.ToString()}<<<");
+
+ // prepare form data
+ var formData = new FormData();
+ formData.Add("name", "peter pan");
+ formData.Add("age", "12");
+
+ var authParam = new AuthParam()
+ {
+ url = new Uri($"{URL}{queryData.ToString()}"),
+ httpMethod = HttpMethod.POST,
- // authorization header
- var authorizationHeader = ApiAuthorization.Token(realm, authPrefix, HttpMethod.POST, new Uri(baseUrl), appId, appSecret, formData);
+ appName = APP_NAME,
+ privateKey = privateKey,
- Console.WriteLine("\n>>>Authorization Header :: '{0}'<<<", authorizationHeader);
+ formData = formData
+ };
- // no need append .e on the target Internet gateway name (for Intranet i.e -pvt.api.gov.sg)
- string targetGatewayName = ".api.gov.sg";
- string targetBaseUrl = string.Format("https://{0}/{1}?{2}", targetGatewayName, apiPath, queryString);
+ // get the authorization token for L1
+ var authToken = ApiAuthorization.TokenV2(authParam);
- // this method only for verification only
- // expecting result to be 200
- var result = ApiAuthorization.HttpRequest(new Uri(targetBaseUrl), authorizationHeader, formData, HttpMethod.POST, ignoreServerCert: true);
+ Console.WriteLine($"\n>>> BaseString :: '{authToken.BaseString}'<<<");
+ Console.WriteLine($"\n>>> Authorization Token :: '{authToken.Token}'<<<");
+
+ // make api call with authToken.Token
}
```
-### How to Generate RSA256 L2 Authorization Header
+### How to Generate L21 Authorization Header
+(for cross zone api from internet to intranet)
```
-public static void L2Sample()
+public void L21Sample()
{
- // application realm
- string realm = "<>";
-
- // authorization prefix (i.e 'Apex_l2_eg' )
- string authPrefix = "<>";
+ var URL_WWW = "https://{www_gatewayName}.api.gov.sg/api/v1/resource";
+ var APP_NAME_WWW = "www_appName";
+ var PRIVATE_KEY_FILE_NAME = "www_privateKey.key");
+ var PRIVATE_KEY_PASSPHRASE = "{password}";
- // app id i.e 'Apex_l2_eg' assign to the application
- string appId = "<>";
+ var URL_WOG = "https://{wog_gatewayName}.api.gov.sg/api/v1/resource";
+ var APP_NAME_WOG = "{wog_AppName}";
+ var APP_SECRET_WOG = "{wog_appSecret}";
- // api signing gateway name and path (for Intranet i.e -pvt.i.api.gov.sg)
- string signingGateway = ".e.api.gov.sg";
- string apiPath = "api/v1/l2/";
+ // get the private key from pem file (in pkcs1 format)
+ var privateKey = ApiAuthorization.GetPrivateKey(PRIVATE_KEY_FILE_NAME, PRIVATE_KEY_PASSPHRASE);
- // query string (optional)
- var queryParam = new ApiUtilLib.ApiList();
+ // prepare queryString
+ var queryData = new QueryData();
+ queryData.Add("view", "net-5.0");
+ queryData.Add("system", "C# sample code");
- queryParam.Add("clientId", "1256-1231-4598");
- queryParam.Add("accountStatus", "active");
- queryParam.Add("txnDate", "2017-09-29");
+ // prepare form data
+ var formData = new FormData();
+ formData.Add("name", "peter pan");
+ formData.Add("age", "12");
- string queryString = queryParam.ToQueryString();
+ // prepare the parameters
+ var authParam = new AuthParam()
+ {
+ url = new Uri($"{URL_WWW}{queryData.ToString()}"),
+ httpMethod = HttpMethod.POST,
- string baseUrl = string.Format("https://{0}/{1}?{2}", signingGateway, apiPath, queryString);
+ appName = APP_NAME_WWW,
+ privateKey = privateKey,
- // form data (optional)
- var formData = new ApiUtilLib.ApiList();
+ formData = formData,
- formData.Add("phoneNo", "+1 1234 4567 890");
- formData.Add("street", "Hellowood Street");
- formData.Add("state", "AP");
+ nextHop = new AuthParam()
+ {
+ url = new Uri($"{URL_WOG}{queryData.ToString()}"),
- // private cert file and password
- string privateCertName = GetLocalPath("Certificates/alpha.example.api.com.p12");
- string password = "password";
+ appName = APP_NAME_WOG,
+ appSecret = APP_SECRET_WOG
+ }
+ };
- // get the private key from cert
- var privateKey = ApiAuthorization.PrivateKeyFromP12(privateCertName, password);
-
- // authorization header
- var authorizationHeader = ApiAuthorization.Token(realm, authPrefix, HttpMethod.POST, new Uri(baseUrl), appId, null, formData, privateKey);
+ // get the authorization token for L21
+ var authToken = ApiAuthorization.TokenV2(authParam);
- // no need append .e on the target gateway name (for Intranet i.e -pvt.api.gov.sg)
- string targetGatewayName = ".api.gov.sg";
- string targetBaseUrl = string.Format("https://{0}/{1}?{2}", targetGatewayName, apiPath, queryString);
+ Console.WriteLine($"\n>>>{tag}<<< BaseString :: '{authToken.BaseString}'<<<");
+ Console.WriteLine($"\n>>>{tag}<<< Authorization Token :: '{authToken.Token}'<<<");
- // this method only for verification only
- // expecting result to be 200
- var result = ApiAuthorization.HttpRequest(new Uri(targetBaseUrl), authorizationHeader, formData, HttpMethod.POST, ignoreServerCert: true);
+ // make api call with authToken.Token
}
-static string GetLocalPath(string relativeFileName)
-{
- var localPath = Path.Combine(Path.GetDirectoryName(new Uri(Assembly.GetExecutingAssembly().CodeBase).LocalPath), relativeFileName.Replace('/', Path.DirectorySeparatorChar));
-
- return localPath;
-}
```
-#### Sample HTTP POST Call for x-www-form-urlencoded with APEX L1 Security (for reference only)
+### How to Generate L12 Authorization Header
+(for cross zone api from intranet to internet)
```
-[Test]
-public static void Http_Call_Test()
+public void L12Sample()
{
- // application realm
- string realm = "http://example.api.test/token";
+ var URL_WOG = "https://{wog_gatewayName}.api.gov.sg/api/v1/reslource";
+ var APP_NAME_WOG = "{wog_appName}";
+ var APP_SECRET_WOG = "{wog_AppSecret}";
+
+ var URL_WWW = "https://{www_appName}.api.gov.sg/api/v1/resource";
+ var APP_NAME_WWW = "{www_AppName}";
+ var PRIVATE_KEY_FILE_NAME = "Certificates/www_privateKey.pkcs8");
+ var PRIVATE_KEY_PASSPHRASE = "{passphrase}";
+
+ // get the private key from pem file (in pkcs8 format)
+ var privateKey = ApiAuthorization.GetPrivateKey(PRIVATE_KEY_FILE_NAME, PRIVATE_KEY_PASSPHRASE);
+
+ // prepare queryString
+ var queryData = new QueryData();
+ queryData.Add("view", "net-5.0");
+ queryData.Add("system", "C# sample code");
- // authorization prefix
- string authPrefix = "Apex_l1_eg";
+ // prepare form data
+ var formData = new FormData();
+ formData.Add("name", "peter pan");
+ formData.Add("age", "12");
- // app id and app secret assign to the application
- string appId = "tenant-1X2w7NQPzjO2azDu904XI5AE";
- string appSecret = "s0m3s3cr3t";
- var formData = new ApiUtilLib.ApiList();
+ // prepare the token parameters
+ var authParam = new AuthParam()
+ {
+ url = new Uri($"{URL_WOG}{queryData.ToString()}"),
+ httpMethod = HttpMethod.POST,
- formData.Add("key1", "value1);
- formData.Add("key2", "value2");
-
- // api signing gateway name and path
- string gatewayName = "https://tenant.e.api.gov.sg";
- string apiPath = "api14021live/resource";
- string baseUrl = string.Format("{0}/{1}", gatewayName, apiPath);
- Console.WriteLine("\n>>>baseUrl :: '{0}'<<<", baseUrl);
- Console.WriteLine("\n>>>appId :: '{0}'<<<", appId);
- Console.WriteLine("\n>>>appSecret :: '{0}'<<<", appSecret);
- // authorization header
- var authorizationHeader = ApiAuthorization.Token(realm, authPrefix, HttpMethod.POST, new Uri(baseUrl), appId, appSecret, formData);
+ appName = APP_NAME_WOG,
+ appSecret = APP_SECRET_WOG,
- Console.WriteLine("\n>>>Authorization Header :: '{0}'<<<", authorizationHeader);
+ formData = formData,
- // if the target gateway name is different from signing gateway name
- string targetBaseUrl = "https://tenant.api.gov.sg/api14021live/resource";
+ nextHop = new AuthParam()
+ {
+ url = new Uri($"{URL_WWW}{queryData.ToString()}"),
+ appName = APP_NAME_WWW,
+ privateKey = privateKey,
+ }
+ };
- // this method only for verification only
- // expecting result to be 200
+ // get the authorization token
+ var authToken = ApiAuthorization.TokenV2(authParam);
- var result = ApiAuthorization.HttpRequest(new Uri(targetBaseUrl), authorizationHeader, formData, HttpMethod.POST, ignoreServerCert: true);
- Console.WriteLine(result);
- Console.ReadLine();
+ Console.WriteLine($"\n>>> BaseString :: '{authToken.BaseString}'<<<");
+ Console.WriteLine($"\n>>> Authorization Token :: '{authToken.Token}'<<<");
- Assert.True(true);
+ // make api call with authToken.Token
}
```