How to create same MD5 token in different platforms? - c#

I am using MD5 hashing first time, I am trying to create a token to secure my web-services. I have found three different md5 hashing method for Android, IOS and C#. Before I call any webservice, I create a token and send it as a parameter. Then, when I get the token in service side, I create a token with the same algorithm in C# too and compare those two tokens. If the tokens are the same, I permit to the process. If not, I throw an exeption. My problem is, tokens are always different. I suppose that the difference of md5 creation methods causes this problem.
MD5 method in the C# code:
public static string MD5(string stringToEncrypted)
{
// step 1, calculate MD5 hash from input
var md5 = System.Security.Cryptography.MD5.Create();
byte[] inputBytes = System.Text.Encoding.ASCII.GetBytes(stringToEncrypted);
byte[] hash = md5.ComputeHash(inputBytes);
// step 2, convert byte array to hex string
var sb = new StringBuilder();
for (int i = 0; i < hash.Length; i++)
{
sb.Append(hash[i].ToString("X2"));
}
return sb.ToString();
}
In Swift Code:
extension String
{
var md5: String!
{
let str = self.cStringUsingEncoding(NSUTF8StringEncoding)
let strLen = CC_LONG(self.lengthOfBytesUsingEncoding(NSUTF8StringEncoding))
let digestLen = Int(CC_MD5_DIGEST_LENGTH)
let result = UnsafeMutablePointer<CUnsignedChar>.alloc(digestLen)
CC_MD5(str!, strLen, result)
var hash = NSMutableString()
for i in 0..<digestLen
{
hash.appendFormat("%02x", result[i])
}
result.destroy()
return String(format: hash)
}
}
In Android Code:
public static final String md5(final String stringToEncrypted)
{
final String MD5 = "MD5";
try
{
// Create MD5 Hash
MessageDigest digest = java.security.MessageDigest.getInstance(MD5);
digest.update(stringToEncrypted.getBytes());
byte messageDigest[] = digest.digest();
// Create Hex String
StringBuilder hexString = new StringBuilder();
for (byte aMessageDigest : messageDigest)
{
String h = Integer.toHexString(0xFF & aMessageDigest);
while (h.length() < 2)
h = "0" + h;
hexString.append(h);
}
return hexString.toString();
}
catch (NoSuchAlgorithmException e)
{
e.printStackTrace();
}
return "";
}
In Android using: h = "0" + h;
In IOS using: hash.appendFormat("%02x", result[i])
In C# using: sb.Append(hash[i].ToString("X2"));
Do these differences cause this problem ?
Thank you for your answers,
Best regards

Code example from SO Answer
func doSha256(#dataIn:NSData) -> NSData {
var shaOut: NSMutableData! = NSMutableData(length: Int(CC_SHA256_DIGEST_LENGTH));
CC_SHA256(dataIn.bytes, CC_LONG(dataIn.length), UnsafeMutablePointer<UInt8>(shaOut.mutableBytes));
return shaOut;
}
Obviously just change the constants for other hash methods.
If you want another format, say Base64 or hex put those conversions in a wrapper method that calls this method. It is better not to co-mingle methods, allow each to do a single thing. (Single Responsibility Principle) It also allows for easier testing and debugging.

Related

C# hmac hashing function converting to PHP

I am working on a rest call to an asp.net web service and was provided sample code in C#. I am having trouble converting the following function
public static string ComputeHash(string hashedPassword, string message)
{
var key = Encoding.UTF8.GetBytes(hashedPassword.ToUpper());
string hashString;
using (var hmac = new HMACSHA256(key))
{
var hash = hmac.ComputeHash(Encoding.UTF8.GetBytes(message));
hashString = Convert.ToBase64String(hash);
}
return hashString;
}
So far this is the draft php equivalent, is there anything specific I should be taking into account when converting between the two? Ignore any formatting am just trying to keep variable names the same for now for sanity. I can't seem to get these to come out to be the same.
function ComputeHash($hashedPassword, $message)
{
$key = utf8_encode(strtoupper($hashedPassword));
$hashString=hash_hmac('sha256',utf8_encode($message),$key,True);
$hashString = base64_encode($hashString);
return $hashString;
}
EDIT
Thanks to comments below I am able to get both functions doing the same.
Now the issue I have is around the input message
In C# this comes out like this
"GET\n03 August 2022 14:57:27.569500\n/api/member\nuserName=tom.woodforde#redox-software.co.uk";
If I feed the same into the function below the output is different.
This is how the function is built in C# to generate the message
private const String _newLineChar = "\n";
private static String CreateMessage(String method, String dateUTC, String apiModule, String data)
{
return method + _newLineChar + dateUTC + _newLineChar + apiModule + _newLineChar + data;
}
In PHP this is my equivalent
function CreateMessage($method, $dateUTC, $apiModule, $data){
$newLineChar = "\\n";
$message= $method.$newLineChar.$dateUTC.$newLineChar.$apiModule.$newLineChar.$data;
return $message;
}
I can feed in the exact same string but get a different result from the encryption and I am assuming it must be because of \n

How to transform to C# the PHP pack('H40',$string);?

I am trying to reproduce the result of a PHP pack('H40',$string) function.
So far i got good results but slightly different.
Here is what i tried
PHP Code
<?php
function test(){
$packed = pack('H40',"42d5abcd859afd2cc0f6b6f8cc2a9cd41f66a120");
return $packed;
}
echo test();
?>
C# Code:
private void test(){
string result = PackH40("42d5abcd859afd2cc0f6b6f8cc2a9cd41f66a120");
}
private string PackH40(string input)
{
string result = string.Empty;
input = input.Replace("-", " ");
byte[] hashBytes = new byte[input.Length / 2];
for (int i = 0; i < hashBytes.Length; i++)
{
hashBytes[i] = Convert.ToByte(input.Substring(i * 2, 2), 16);
}
return Encoding.UTF8.GetString(hashBytes);
}
The results look similar in browser but if you write them to a file they are not equal.
So far i tried every Encoding for the C# output and nothing.
What am i doing wrong?
Edit:
I tried the approach in
Trying to reproduce PHP's pack("H*") function in C# the issue is similar and in this case did not help me.
Resolved: Major problem was that i wanted to 'see' the data and compare it. I was terribly wrong! Converted all to byte[] and it worked like a charm !

Send byte array from c# to angular through websocket

Hi I would like to implement a function that a byte array is sent from C# to my Angular 7 through websocket. Basically, I have a websocket running with C# and my frontend is written in Angular7.
Please note, the following example is a simplified one. In my real application, the object I am going to send includes about 400 fields. After serialise the object to json string, it is about 6kb. In addition, I need to send 30 such objects in about one second. Sending binary data will significantly reduce the package size and speed it up.
In C#, I have such example code to generate the payload of the websocket:
public class Test
{
public int Id { get; set; }
public float Value { get; set; }
public string Description { get; set; }
}
public class Payload
{
public object Obj { get; set; }
}
Test[] tests = new Test[]
{
new Test
{
Id = 0,
Value = 1.12f,
Description = "The First Test"
}
};
byte[] testsByteArray;
using (MemoryStream m = new MemoryStream())
{
using (BinaryWriter binaryWriter = new BinaryWriter(m))
{
foreach (Test test in tests)
{
binaryWriter.Write(test.Id);
binaryWriter.Write(test.Value);
binaryWriter.Write(test.Description);
}
}
testsByteArray = m.ToArray();
}
Payload payload = new Payload
{
Obj = testsByteArray
};
string a = JsonConvert.SerializeObject(payload);
Eventually I got a as {"Obj":"AAAAAClcjz8OVGhlIEZpcnN0IFRlc3Q="} I guess the value of Obj is base64 encoded value of testsByteArray.
Now, in my frontend, I can receive the json string. The question is how to convert AAAAAClcjz8OVGhlIEZpcnN0IFRlc3Q= back to an object with the same format as Test in Angular.
What I tried:
I tried to use the following function atob() to decode the base64, and then use the following function to convert the decoded string to byte array
str2ab(str): ArrayBuffer {
var buf: ArrayBuffer = new ArrayBuffer(str.length * 2); // 2 bytes for each char
var bufView = new Uint16Array(buf);
for (var i = 0, strLen = str.length; i < strLen; i++) {
bufView[i] = str.charCodeAt(i);
}
return buf;
}
Then I try to use the following code to get the values
let dataView = new DataView(arrayBuffer);
console.log(dataView.getInt32(0)); // = 0
console.log(dataView.getInt32(2)); // = 0
console.log(dataView.getInt32(4)); // = 0
console.log(dataView.getInt32(6)); // = 10496
I am not exactly sure how to convert to correct value. Can anyone help out.
Thank you
Sending binary data between different platforms and languages is fragile and difficult to get right. I'd suggest converting to a platform agnostic representation (such as a JSON array) before sending, and convert back on the other side.
For example,
string a = JsonConvert.SerializeObject(tests);

SHA1 hash code comparing fails

I have written two method called-MakeHash and CompareHash on my .NET Core application. Now with MakeHash I am able to successfully converting SHA1 code but the problem is the way I am trying to compare hash code is always returns false. That means the CompareHash method is unable to compare plain code and SHA1 codes. Can you tell me how I can fix CompareHash so it will able to compare between plain text and SHA1 hash code? What am I doing wrong in CompareHash method? Thanks in advance
public static string MakeHash(string str)
{
// generate a 128-bit salt using a secure PRNG
byte[] salt = new byte[128 / 8];
using (var rng = RandomNumberGenerator.Create())
{
rng.GetBytes(salt);
}
// derive a 256-bit subkey (use HMACSHA1 with 10,000 iterations)
string hashed = Convert.ToBase64String(KeyDerivation.Pbkdf2(
password: str,
salt: salt,
prf: KeyDerivationPrf.HMACSHA1,
iterationCount: 10000,
numBytesRequested: 256 / 8));
return hashed;
}
public static bool CompareHash(string plainString, string hashString)
{
if (MakeHash(plainString)==hashString)
{
return true;
}
else
{
return false;
}
}
Well, if you need some quick solution without storing salt on your database then you can give a try with the code below. This works for me. But this is highly recommended to use salt and match between them. Because it's about security you should be careful and put some more effort into it. My example is just to provide you an idea, not for production usage.
public static string MakeHash(string value)
{
return Convert.ToBase64String(
System.Security.Cryptography.SHA256.Create().ComputeHash(Encoding.UTF8.GetBytes(value))
);
}
public static bool CompareHash(string plainString, string hashString)
{
if (MakeHash(plainString) == hashString)
{
return true;
}
else
{
return false;
}
}

How to properly verify data with rsa?

I want to sign a message with a private key and verify it with a public key, but I can't get it to work..
Here is how I sign the data (edited, but still not working):
public static string SignData(string message, string privateKey) {
byte[] plainText = ASCIIEncoding.Unicode.GetBytes(message);
var rsaWrite = new RSACryptoServiceProvider();
rsaWrite.FromXmlString(privateKey);
byte[] signature = rsaWrite.SignData(plainText, new SHA1CryptoServiceProvider());
return Convert.ToBase64String(signature);
}
Here is how I test the data (edited, still not working):
public static bool VerifyData(string sign, string publicKey, string orig) {
byte[] signature = Convert.FromBase64String(sign);
byte[] original = ASCIIEncoding.Unicode.GetBytes(orig);
var rsaRead = new RSACryptoServiceProvider();
rsaRead.FromXmlString(publicKey);
if (rsaRead.VerifyData(original, new SHA1CryptoServiceProvider(), signature)) {
return true;
} else {
return false;
}
}
I store the keypair as an xml string inside my account class. This function is executed in the constructor of account.cs:
public void addKeys() {
RSACryptoServiceProvider provider = new RSACryptoServiceProvider(1024);
privateKey = provider.ToXmlString(true);
publicKey = provider.ToXmlString(false);
}
I test the overall thing with this:
string signedHash = Utility.SignData("test" ,account.privateKey);
if (Utility.VerifyData(signedHash, account.publicKey, "test")) {
Console.WriteLine("WORKING!");
} else {
Console.WriteLine("SIGNING NOT WORKING");
}
Why isn't the overall thing working? My guess is that it doesn't work because of some encoding stuff.
return ASCIIEncoding.Unicode.GetString(signature);
The signature is arbitrary binary data, it isn't necessarily legal Unicode/UCS-2. You need to use an arbitrary encoding (https://en.wikipedia.org/wiki/Binary-to-text_encoding#Encoding_standards) to encode all of the arbitrary data. The most popular transport for signatures is Base64, so you'd want
return Convert.ToBase64String(signature);
And, of course, use Convert.FromBase64String in the verify method.
If you're compiling with a target of .NET 4.6 or higher you can also make use of the newer sign/verify API:
rsaRead.VerifyData(original, new SHA1CryptoServiceProvider(), signature)
would be
rsaRead.VerifyData(original, signature, HashAlgorithmName.SHA1, RSASignaturePadding.Pkcs1)
While it might not look simpler, it prevents the allocation and finalization of the SHA1CryptoServiceProvider that the other method did, and it sets up for The Future when you may want to switch from Pkcs1 signature padding to PSS signature padding. (But the real advantage is that method is on the RSA base class instead of the RSACryptoServiceProvider specific type).

Categories

Resources