I need help to convert this code to Java for password comparison and it must run on Android.
I am specially confused in how to add the salt given in this C# Code here:
Code C#
using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;
namespace CMS.Core.Utility
{
public sealed class CMSHashManager
{
private static readonly string _salt = "3D5900AE-111A-45BE-96B3-D9E4606CA793";
private static readonly int _hashIterationsMax = 10;
private CMSHashManager()
{
}
#region Public Methods
//Gets the salted hash value with predetermined iterations.
public static string GetPasswordHash(string plaintextPassword)
{
string hashData = plaintextPassword;
for (int hashLimit = 0; hashLimit < _hashIterationsMax; hashLimit++)
hashData = GetHash(_salt + hashData);
return hashData;
}
//Verifies the hash
public static bool VerifyHashedPassword(string plaintextPassword, string encryptedPassword)
{
string hashData = GetPasswordHash(plaintextPassword);
return encryptedPassword.Equals(hashData);
}
#endregion Public Methods
#region Private Methods
//Gets the hash value of the data using SHA512Managed
private static string GetHash(string unhashedData)
{
byte[] hashData = Encoding.UTF8.GetBytes(unhashedData);
// on server 2003 or higher, can use SHA512CryptoServiceProvider
//SHA512CryptoServiceProvider sha512CryptoServiceProvider = new SHA512CryptoServiceProvider();
SHA512Managed sha512CryptoServiceProvider = new SHA512Managed();
hashData = sha512CryptoServiceProvider.ComputeHash(hashData);
sha512CryptoServiceProvider.Clear();
return Convert.ToBase64String(hashData);
}
#endregion Private Methods
}
}
I have already written this java method which creates a MD5 hash:
Code Java
public String getMD5Password(String password) throws NoSuchAlgorithmException, UnsupportedEncodingException{
MessageDigest digest = java.security.MessageDigest.getInstance("SHA-512");
digest.update(password.getBytes("UTF-16LE"));
byte messageDigest[] = digest.digest();
// Create Hex String
StringBuffer hexString = new StringBuffer();
for (int i = 0; i < messageDigest.length; i++) {
String h = Integer.toHexString(0xFF & messageDigest[i]);
while (h.length() < 2)
h = "0" + h;
hexString.append(h);
}
return hexString.toString();
}
Test
For testing purposes you can use the following case:
plaintext:12345
Encrypted:NgkuakH7UsCQwGHMQOhVXI3nW6M+1AtREY4Qx35osQo87p/whZIzy8cZU7+R7XnmyzgMzLWSvX+rTiWzfGTPsA==
I tried to reproduce your code.
For the password test it produces the following BASE64 output
Q0Y2QkI0MTBFRUJFOTAyNkU1OUZGMUNGMzU0NkYzMkI3NDZFMzE5RjQzNTc0MDM5QjU2MUI2NEQxOTQzNzRGMDRENDM0QzMyQjg3MjMwQkM1N0I0ODFDRDlEODlBNjMxQjMyNjRGQjNBQjAwMEYwNjk5Rjc0NUNEQjgzMzY1RkM=
I used the following code:
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
//import javax.xml.bind.DatatypeConverter;
import android.util.Base64;
public class Support {
private static final String SALT = "3D5900AE-111A-45BE-96B3-D9E4606CA793";
private static final int MAX_HASH_ITERATIONS = 10;
public static void main(String[] args) throws NoSuchAlgorithmException, UnsupportedEncodingException {
String result = Support.GetPasswordHash("test");
System.out.println(result);
}
public static String GetPasswordHash(String plaintextPassword) throws NoSuchAlgorithmException, UnsupportedEncodingException {
String hashData = plaintextPassword;
for (int hashLimit = 0; hashLimit < MAX_HASH_ITERATIONS; hashLimit++) {
hashData = GetHash(SALT + hashData);
}
return hashData;
}
//Gets the hash value of the data using SHA512Managed
private static String GetHash(String unhashedData) throws NoSuchAlgorithmException, UnsupportedEncodingException {
return getMD5Password(unhashedData);
}
//Verifies the hash
public static boolean VerifyHashedPassword(String plaintextPassword, String encryptedPassword) throws NoSuchAlgorithmException, UnsupportedEncodingException {
String hashData = GetPasswordHash(plaintextPassword);
return encryptedPassword.equals(hashData);
}
public static String getMD5Password(String password) throws NoSuchAlgorithmException, UnsupportedEncodingException{
MessageDigest digest = java.security.MessageDigest.getInstance("SHA-512");
digest.update(password.getBytes("UTF-16LE"));
byte messageDigest[] = digest.digest();
StringBuilder sb = new StringBuilder();
for(int iPos = 0; iPos < messageDigest.length; iPos++) {
String h = Integer.toHexString(0xFF & messageDigest[iPos]);
while (h.length() < 2) {
h = "0" + h;
}
sb.append(h);
}
String md5String = sb.toString().toUpperCase();
String res = Base64.encodeToString(md5String.getBytes(), Base64.DEFAULT);
return res;
}
}
Related
I'm trying to port the following Java code to C#, but so far it still says that the signature is invalid.
private static String generateSignStr(Map<String, String> params, String key) {
StringBuilder sb = new StringBuilder();
params.entrySet().stream().sorted(Map.Entry.comparingByKey()).forEach(entry -> {
if (sb.length() > 0) {
sb.append('&');
}
sb.append(entry.getKey()).append('=');
sb.append(entry.getValue());
});
sb.append('&').append("api_secret")
.append('=').append(key);
return sb.toString();
}
private static String sign(String target) {
MessageDigest md;
try {
md = MessageDigest.getInstance("MD5");
} catch (NoSuchAlgorithmException e) {
log.error("Fail to get MD5 instance");
return null;
}
md.update(target.getBytes());
byte[] dg = md.digest();
StringBuilder output = new StringBuilder(dg.length * 2);
for (byte dgByte : dg) {
int current = dgByte & 0xff;
if (current < 16) {
output.append("0");
}
output.append(Integer.toString(current, 16));
}
return output.toString();
}
private static string GenerateSign(Dictionary<string, object> query, string apiSecret)
{
var sb = new StringBuilder();
var queryParameterString = string.Join("&",
query.Where(kvp => !string.IsNullOrWhiteSpace(kvp.Value.ToString()))
.Select(kvp => $"{kvp.Key}={HttpUtility.UrlEncode(kvp.Value.ToString())}"));
sb.Append(queryParameterString);
if (sb.Length > 0)
{
sb.Append('&');
}
sb.Append("api_secret=").Append(apiSecret);
return sb.ToString();
}
private static string Sign(string source)
{
using var md5 = MD5.Create();
var sourceBytes = Encoding.UTF8.GetBytes(source);
var hash = md5.ComputeHash(sourceBytes);
return BitConverter.ToString(hash).Replace("-", string.Empty).ToLowerInvariant();
}
Edit:
This fixed it. However, it would be nice if someone knows a way to lexicographically sort the dictionary inside that method just like the Java code.
var #params = new Dictionary<string, object>
{
{ "api_key", _apiKey },
{ "req_time", now },
{ "op", "sub.personal" }
};
var javaSorted = #params.OrderBy(item => item.Key, StringComparer.Ordinal)
.ToDictionary(i => i.Key, i => i.Value);
var signature = Sign(GenerateSign(javaSorted, _apiSecret));
In GenerateSign method you can just create instance of SortedDictionary based on dictionary passed as parameter:
private static string GenerateSign(Dictionary<string, object> query, string apiSecret)
{
var sortedDict = new SortedDictionary<string, object>(query, StringComparer.Ordinal);
// rest of the method
}
Or you can do even better (note the important change from Dictionary to IDictionary):
private static string GenerateSign(IDictionary<string, object> query, string apiSecret)
{
query = new SortedDictionary<string, object>(query, StringComparer.Ordinal);
// rest of the method
}
i am using shared preferences to store the user name and password in order to achieve onetime user authentication. I am encrypting and storing the data in shared pref file. Again i am decrypting them and validating the values every time the user open the application. It is working fine until the App is running on background.
If the user close the app from background i am again getting the login screen asking to input the user credentials.
Below is the error:
W/System.err: java.security.InvalidKeyException: unknown key type passed to RSA
W/System.err: at com.android.org.bouncycastle.jcajce.provider.asymmetric.rsa.CipherSpi.engineInit(CipherSpi.java:275)
W/System.err: at com.android.org.bouncycastle.jcajce.provider.asymmetric.rsa.CipherSpi.engineInit(CipherSpi.java:379)
W/System.err: at javax.crypto.Cipher.init(Cipher.java:661)
W/System.err: at javax.crypto.Cipher.init(Cipher.java:621)
Below is my encryption Algorithm. I am calling the genereteKey() method before comitting the data in to shared pref file and encrypting the data then commiting the data.
private final static String RSA = "RSA";
public static PublicKey uk;
public static PrivateKey rk;
public static void generateKey() throws Exception {
KeyPairGenerator gen = KeyPairGenerator.getInstance(RSA);
gen.initialize(512, new SecureRandom());
KeyPair keyPair = gen.generateKeyPair();
uk = keyPair.getPublic();
rk = keyPair.getPrivate();
}
private static byte[] encrypt(String text, PublicKey pubRSA) throws Exception {
Cipher cipher = Cipher.getInstance(RSA);
cipher.init(Cipher.ENCRYPT_MODE, pubRSA);
return cipher.doFinal(text.getBytes());
}
public final static String encrypt(String text) {
try {
return byte2hex(encrypt(text, uk));
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public final static String decrypt(String data) {
try {
return new String(decrypt(hex2byte(data.getBytes())));
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
private static byte[] decrypt(byte[] src) throws Exception {
Cipher cipher = Cipher.getInstance(RSA);
cipher.init(Cipher.DECRYPT_MODE, rk);
return cipher.doFinal(src);
}
public static String byte2hex(byte[] b) {
String hs = "";
String stmp = "";
for (int n = 0; n < b.length; n++) {
stmp = Integer.toHexString(b[n] & 0xFF);
if (stmp.length() == 1) hs += ("0" + stmp);
else
hs += stmp;
}
return hs.toUpperCase();
}
public static byte[] hex2byte(byte[] b) {
if ((b.length % 2) != 0) throw new IllegalArgumentException("hello");
byte[] b2 = new byte[b.length / 2];
for (int n = 0; n < b.length; n += 2) {
String item = new String(b, n, 2);
b2[n / 2] = (byte) Integer.parseInt(item, 16);
}
return b2;
}
It is a code for sha1 hash function. I want access the sb string from main function. I can access the
sb string but not reflected in the main function.
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
public class sha1 {
/**
* #param args
* #throws NoSuchAlgorithmException
*/
public String sb1;
public void printkey(String convert){
sb1=sha1(convert) ;
System.out.println(sb1);
}
public String sha1(String input) {
StringBuffer sb = new StringBuffer();
sha1 rr= new sha1();
try{
MessageDigest mDigest = MessageDigest.getInstance("SHA1");
byte[] result = mDigest.digest(input.getBytes());
for (int i = 0; i < result.length; i++) {
sb.append(Integer.toString((result[i] & 0xff) + 0x100, 16).substring(1));
}
rr.sb1=sb.toString();
System.out.println("\n");
System.out.println(rr.sb1);
}
catch ( NoSuchAlgorithmException nsae ) {
System.out.println(nsae);
}
return sb1;
}
When I am accesing sb1 from main class it not giving the output whatever it print in printkey function. Printkey is giving right output. I want the updated sb should be seen from main function.
public static void main(String[] args) {
sha1 m=new sha1();
System.out.println("\n");
System.out.println(m.sb1);
}
Your problem is that your sha1 method is creating a new instance and setting the String of that new instance. Therefore, sb1 of your original instance (the one created in the main method) is never updated.
Change :
sha1 rr= new sha1();
....
rr.sb1=sb.toString();
To :
sb1 = sb.toString();
In addition, it doesn't look like you call printkey, which calls your sha1 method. You probably want to call it in your main method.
As I said in the comments, there are a lot of problems with your code, here is a working version:
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
public class Sha1 {
private String value = null;
public Sha1(final String input) {
final StringBuffer sb = new StringBuffer();
try {
final MessageDigest mDigest = MessageDigest.getInstance("SHA1");
final byte[] result = mDigest.digest(input.getBytes());
for (int i = 0; i < result.length; i++) {
sb.append(Integer.toString((result[i] & 0xff) + 0x100, 16).substring(1));
}
value = sb.toString();
} catch (NoSuchAlgorithmException nsae) {
value = null;
nsae.printStackTrace();
}
}
public String getValue() {
return value;
}
public static void main(final String[] args) {
final Sha1 testSha1 = new Sha1("test");
System.out.println(testSha1.getValue());
}
}
I've a OAuth2 java client that is trying to create a JWT and then sign with a private key (from Google API console) - follow these pages https://developers.google.com/accounts/docs/OAuth2ServiceAccount. However, google OAuth2 keeps returning "invalid grant".
Here is the client code to sign the JWT:
package com.oauth2.google.app.url;
import java.io.FileInputStream;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.Signature;
import java.text.MessageFormat;
import org.apache.commons.codec.binary.Base64;
public class WebToken
{
private String iss;
private String prn;
private String scope;
private String aud;
private long exp;
private long iat;
private String keystoreLoc;
private String keyAlias;
public WebToken(String iss, String prn, String scope, String aud, long exp, long iat, String keystore,
String keyAlias)
{
super();
this.iss = iss;
this.prn = prn;
this.scope = scope;
this.aud = aud;
this.exp = exp;
this.iat = iat;
this.keystoreLoc = keystore;
this.keyAlias = keyAlias;
}
/**
* Performs base64-encoding of input bytes.
*
* #param rawData * Array of bytes to be encoded.
* #return * The base64 encoded string representation of rawData.
*/
public static String encodeBase64(byte[] rawData)
{
byte[] data = Base64.encodeBase64(rawData);
return new String(data);
}
public String getToken()
throws Exception
{
String header = "{\"alg\":\"RS256\",\"typ\":\"JWT\"}";
//String header = "{\"alg\":\"RS256\"}";
String claimTemplate = "'{'\"iss\": \"{0}\", \"prn\": \"{1}\", \"scope\": \"{2}\", \"aud\": \"{3}\", \"exp\": {4}, \"iat\": {5}'}'";
StringBuffer token = new StringBuffer();
//Encode the JWT Header and add it to our string to sign
token.append(encodeBase64(header.getBytes("UTF-8")));
//Separate with a period
token.append(".");
//Create the JWT Claims Object
String[] claimArray = new String[6];
claimArray[0] = this.iss;
claimArray[1] = this.prn;
claimArray[2] = this.scope;
claimArray[3] = this.aud;
claimArray[4] = "" + this.exp;
claimArray[5] = "" + this.iat;
MessageFormat claims = new MessageFormat(claimTemplate);
String payload = claims.format(claimArray);
print(payload);
//Add the encoded claims object
token.append(encodeBase64(payload.getBytes("UTF-8")));
//Load the private key from a keystore
KeyStore keystore = KeyStore.getInstance("JKS");
keystore.load(new FileInputStream(this.keystoreLoc), "welcome1".toCharArray());
PrivateKey privateKey = (PrivateKey) keystore.getKey(keyAlias, "notasecret".toCharArray());
//Sign the JWT Header + "." + JWT Claims Object
Signature signature = Signature.getInstance("SHA256withRSA");
signature.initSign(privateKey);
signature.update(token.toString().getBytes("UTF-8"));
byte[] sig = signature.sign();
String signedPayload = encodeBase64(sig);
//Separate with a period
token.append(".");
//Add the encoded signature
token.append(signedPayload);
return token.toString();
}
public static void print(String msg)
{
System.out.println(msg);
}
public static void main(String[] args)
throws Exception
{
String iss = "17560ddd122-nggij305nchhs76pge045jg9u1f4567k#developer.gserviceaccount.com";
String prn = "test123#ggmail.com";
String scope = "https://www.googleapis.com/auth/urlshortener";
String aud = "https://accounts.google.com/o/oauth2/token";
long iat = System.currentTimeMillis()/1000 - 60;
long exp = iat + 3600;
String keystoreLoc = "D:\\keystore.jks";
String keyAlias = "privatekey";
WebToken jwt = new WebToken(iss, prn, scope, aud, exp, iat, keystoreLoc, keyAlias);
String token = jwt.getToken();
print(token);
}
}
Then use curl to get token:
curl -k -vSs -X POST -d "grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer&assertion=" https://accounts.google.com/o/oauth2/token
Any idea on what's wrong?
I have following code written in Java
Mac mac = Mac.getInstance("HmacSHA1");
String secretKey ="sKey";
String content ="Hello";
byte[] secretKeyBArr = secretKey.getBytes();
byte[] contentBArr = content.getBytes();
SecretKeySpec secret_key = new SecretKeySpec(secretKeyBArr,"HmacSHA1");
byte[] secretKeySpecArr = secret_key.getEncoded();
mac.init(secret_key);
byte[] final = mac.doFinal(contentBArr);
I want to make same example in C#. So, I wrote following code
HMACSHA1 hmacsha1 = new HMACSHA1();
string secretKey = "sKey";
string content = "Hello";
byte[] secretKeyBArr = Encoding.UTF8.GetBytes(secretKey);
byte[] contentBArr = Encoding.UTF8.GetBytes(content);
hmacsha1.Key = secretKeyBArr;
byte[] final = hmacsha1.ComputeHash(contentBArr);
Final results are not equal. secretKeyBArr and contentBArr are byte array and their values are same in both example. What is unknown is SecretKeySpec passed to mac.init(). So, what is equivalent same class in C#?
The results are identical, but Java uses signed bytes while C# uses unsigned bytes by default.
Furthermore, SecretKeySpec itself normally does not change the underlying data. You need to e.g. put a DES key specification in a SecretKeyFactory to make sure that the parity bits are set correctly (in the resulting SecretKey). So there is no need for an equivalent as the class itself does very little except wrapping the data.
I'm implementing a credit card payment method form a provider (cardinity) that doesn't provide a .net implementation. I'm looking for similar stuff and end-up writing my own as my google skills seem to be ....
What I need is the base64 string of javax.crypto.mac
I am supporting the following methods:
enum EncryptionMethods
{
None=0,
HMACSHA1,
HMACSHA256,
HMACSHA384,
HMACSHA512,
HMACMD5
}
I have implemented the code you have above, the SecretKeySpec and the Mac the following way (you need System.Security.Cryptography.ProtectedData):
internal class Protected
{
private Byte[] salt = Guid.NewGuid().ToByteArray();
protected byte[] Protect(byte[] data)
{
try
{
return ProtectedData.Protect(data, salt, DataProtectionScope.CurrentUser);
}
catch (CryptographicException)//no reason for hackers to know it failed
{
return null;
}
}
protected byte[] Unprotect(byte[] data)
{
try
{
return ProtectedData.Unprotect(data, salt, DataProtectionScope.CurrentUser);
}
catch (CryptographicException)//no reason for hackers to know it failed
{
return null;
}
}
}
internal class SecretKeySpec:Protected,IDisposable
{
readonly EncryptionMethods _method;
private byte[] _secretKey;
public SecretKeySpec(byte[] secretKey, EncryptionMethods encryptionMethod)
{
_secretKey = Protect(secretKey);
_method = encryptionMethod;
}
public EncryptionMethods Method => _method;
public byte[] SecretKey => Unprotect( _secretKey);
public void Dispose()
{
if (_secretKey == null)
return;
//overwrite array memory
for (int i = 0; i < _secretKey.Length; i++)
{
_secretKey[i] = 0;
}
//set-null
_secretKey = null;
}
~SecretKeySpec()
{
Dispose();
}
}
internal class Mac : Protected,IDisposable
{
byte[] rawHmac;
HMAC mac;
public Mac(SecretKeySpec key, string data)
{
switch (key.Method)
{
case EncryptionMethods.HMACMD5:
mac = new HMACMD5(key.SecretKey);
break;
case EncryptionMethods.HMACSHA512:
mac = new HMACSHA512(key.SecretKey);
break;
case EncryptionMethods.HMACSHA384:
mac = new HMACSHA384(key.SecretKey);
break;
case EncryptionMethods.HMACSHA256:
mac = new HMACSHA256(key.SecretKey);
break;
case EncryptionMethods.HMACSHA1:
mac = new HMACSHA1(key.SecretKey);
break;
default:
throw new NotSupportedException("not supported HMAC");
}
rawHmac = Protect( mac.ComputeHash(Cardinity.ENCODING.GetBytes(data)));
}
public string AsBase64()
{
return System.Convert.ToBase64String(Unprotect(rawHmac));
}
public void Dispose()
{
if (rawHmac != null)
{
//overwrite memory address
for (int i = 0; i < rawHmac.Length; i++)
{
rawHmac[i] = 0;
}
//release memory now
rawHmac = null;
}
mac?.Dispose();
mac = null;
}
~Mac()
{
Dispose();
}
}
I have implemented this in an OAuthSigner class the following way:
public override string ComputeSignature(string plainTextToEncode, string consumerSecret)
{
var key = PercentEncode(consumerSecret) + "&";
try
{
using (var secretKey = new SecretKeySpec(key.GetBytes(), EncryptionMethods.HMACSHA1))
using (Mac mac = new Mac(secretKey, plainTextToEncode))
{
return mac.AsBase64();
}
}
finally
{
key = null;//free memory, remove sensitive data
}
}
Then, it's not what you ask for but I need a helper method as I am sending my text to a web service that goes like this and I include it as some might copy the code:
public static String PercentEncode(string textToEncode)
{
return string.IsNullOrEmpty(textToEncode)
?""
: UrlEncoder.Default.Encode(Cardinity.ENCODING.GetString(Cardinity.ENCODING.GetBytes(textToEncode)))
.Replace("+", "%20").Replace("*", "%2A")
.Replace("%7E", "~");
}
The class UrlEncoder comes from System.Text.Encodings.Web, you may have to add a reference.
The class named Cardinity implements a "short-cut" to the Encoding that I use for Cardinity
public abstract class Cardinity
{
...
public static String API_BASE = "https://api.cardinity.com";
public static String API_VERSION = "v1";
public static String VERSION = "0.1";
public static String ENCODING_CHARSET = "UTF-8";
public static Encoding ENCODING => Encoding.UTF8;
}
as Java uses string.GetBytes a lot, I have added an extension method for this that I call above in the key.GetBytes(), here is the extension code:
public static byte[] GetBytes(this string sender)=>
Cardinity.ENCODING.GetBytes(sender);
My test method, I have copied the values from Cardinity API passes without any issues.
private OAuthSigner signer;
public HmacOAuthSigner_Test()
{
signer = new HmacOAuthSigner();
}
[TestMethod]
public void Test_HmacOAuthSigner_ComputeSignature_DefaultText()
{
var expects = "PxkffxyQh6jsDNcgJ23GpAxs2y8=";
var test_data = "justsomerandommessage";
var secretkey = "yvp0leodf231ihv9u29uuq6w8o4cat9qz2nkvs55oeu833s621";
var actual = signer.ComputeSignature(test_data, secretkey);
Assert.AreEqual(expects, actual, $"Expecting {test_data} to return {expects} received {actual}");
}
The whole implementation of the HmacOAuthSigner is here, it implements an abstract class with the PercentEncode method in it.
public class HmacOAuthSigner : OAuthSigner
{
public override string ComputeSignature(string signatureBaseString, string consumerSecret)
{
var key = PercentEncode(consumerSecret) + "&";
var secretKey = new SecretKeySpec(key.GetBytes(), EncryptionMethods.HMACSHA1);
using (Mac mac = new Mac(secretKey, signatureBaseString))
{
return mac.AsBase64();
}
}
public override string GetSignatureMethod()
{
return "HMAC-SHA1";
}
}
and the abstract class that I use as a contract for all the implementations:
public abstract class OAuthSigner
{
/// <summary>
/// Signature method used
/// </summary>
/// <returns>a string that tells the implementation method</returns>
public abstract string GetSignatureMethod();
/// <summary>
/// computes the signature that is used with the encryption based on the keys provided by cardinity
/// </summary>
/// <param name="signatureBaseString">The secret string that services as a base</param>
/// <param name="consumerSecret">The consumer key as specified in the API settings</param>
/// <returns>signature string computed by the provided parameters using the signature method</returns>
public abstract string ComputeSignature(String signatureBaseString, String consumerSecret);
/// <summary>
/// Encode a string into a format expected by Cardinity
/// </summary>
/// <param name="textToEncode">The text that is to be encoded</param>
/// <returns>web encoded string ready for using to send to Cardinity</returns>
public static String PercentEncode(string textToEncode)
{
return string.IsNullOrEmpty(textToEncode)
?""
: UrlEncoder.Default.Encode(Cardinity.ENCODING.GetString(Cardinity.ENCODING.GetBytes(textToEncode)))
.Replace("+", "%20").Replace("*", "%2A")
.Replace("%7E", "~");
}
}