I am trying to replicate the following code in PHP, It is example code for an API I have to interface with (The API & Example code is in C#, My app is in PHP 5.3). I'm not a C# developer and so am having trouble doing this.
// C# Code I am trying to replicate in PHP
var apiTokenId = 1887;
var apiToken = "E1024763-1234-5678-91E0-T32E4E7EB316";
// Used to authenticate our request by the API (which is in C#)
var stringToSign = string.Empty;
stringToSign += "POST"+"UserAgent"+"http://api.com/post";
// Here is the issue, How can I do the following 3 lines in PHP?
// No "secret key" provided?.. How do I do this in PHP?
var hmacsha1 = new HMACSHA1(new Guid(apiToken).toByteArray());
// Make a byte array with ASCII encoding.
byte[] byteArray = System.Text.Encoding.ASCII.GetBytes(stringToSign);
// Finally, 'computeHash' of the above (what does this do exactly?!)
var calculatedSignature = Convert.ToBase64String(hmacsha1.ComputeHash(byteArray));
I've tried many variations using pack() and other functions I've found online, but without anything to compare it to, I don't know if i've done it right or not.
Can any C# devs run the above code and post the values generated so I can use that to check/test against?
I've tried checking the MSDN to see what these methods do, but am stuck (and not sure if its correct, as I have nothing to compare it to).
PHP Pseudo Code
// Set vars
$apiToken = 'E1024763-1234-5678-91E0-T32E4E7EB316';
$apiTokenId = '1887';
$stringToSign = "POST"."UserAgent"."http://api.com/post";
// HowTo: Build a `byteArray` of our apiToken? (i think)
// C#: var hmacsha1 = new HMACSHA1(new Guid(apiToken).toByteArray());
// HowTo: Convert our $stringToSign to a ASCII encoded `byteArray`?
// C#: byte[] byteArray = System.Text.Encoding.ASCII.GetBytes(stringToSign);
// HowTo: Generate a base64 string of our (`hmacsha1`.ComputeHash(byteArray))
// C#: var calculatedSignature = Convert.ToBase64String(hmacsha1.ComputeHash(byteArray));
This sounds pretty simple and straightforwaard, but I'm not sure what a few of these C# methods do..
What do these C# methods do/return?
ComputeHash(byteArray) - Computed to what?.. what is returned?
System.Text.Encoding.ASCII.GetBytes(stringToSign); - What does this return?
new HMACSHA1(new Guid(apiToken).toByteArray()); No Secret Key?, what is the key used?
Any resources or help would be much appreciated.
I tried variations of other answers on SO, but no joy.
Can I run the 3 lines of code somewhere online (like JSFiddle but for C#?) so I can see the output of each line?
Update - Bounty Added
Still having trouble with this, I have managed to test the C# code in Visual Studio, but am having trouble getting the same hash generated in PHP.
I would like...
.. the above C# code (specifically, the 3 lines which create the SHA1 hash) to be converted into PHP (Check out the Pseudo Code I posted above). I should be able to match the C# hash using PHP.
If you have any other questions, please ask.
The issue is that the string form of the GUID reverses the order of the 2-character hexadecimal numbers in the first 3 segments of the GUID. For more information see the comments in the example at: http://msdn.microsoft.com/en-us/library/system.guid.tobytearray.aspx
The following code should work:
$apiTokenId = 1887;
$apiToken = "E1024763-1234-5678-91E0-FF2E4E7EB316";
$stringToSign = '';
$hexStr = str_replace('-','',$apiToken);
$c = explode('-',chunk_split($hexStr,2,'-'));
$hexArr = array($c[3],$c[2],$c[1],$c[0],$c[5],$c[4],$c[7],$c[6],$c[8],$c[9],$c[10],$c[11],$c[12],$c[13],$c[14],$c[15]);
$keyStr = '';
for ($i = 0; $i < 16; ++$i) {
$num = hexdec($hexArr[$i]);
$keyStr .= chr($num);
}
$stringToSign .= "POST" . "UserAgent" . "http://api.com/post";
$hmacsha1 = base64_encode(hash_hmac('sha1',$stringToSign,$keyStr,true));
I've tested this code against the C# code you provided above and the output was the same. However, the GUID specified in the original code is not valid so I had to change it slightly.
It's pretty easy, when i don't have to test the code :P
http://php.net/manual/en/function.hash-hmac.php - that's the equivalent of the HMACSHA1 c# class.
string hash_hmac (string $algo , string $data , string $key [, bool $raw_output = false ] )
So $algo = "sha1"
$data is your $stringToSign - since that is already an ascii string (i hope) - the C# was just taking the byte equivalent of the same.
new Guid(apiToken).toByteArray() -> that's a 16 byte (16*8 = 128) representation of the GUID - which is 32*4 = 128 bits. This is the key.
$key is a string so you need the ASCII string equivalent for your $apiToken (which is 32 hex chars - first strip / ignore the dashes in between) - E10247631234567891E0T32E4E7EB316 (correct the key - it cannot have a "T")
function hex2str($hex) {
for($i=0;$i<strlen($hex);$i+=2) $str .= chr(hexdec(substr($hex,$i,2)));
return $str;
}
$hexKey = hex2str($apiToken); //strip the dashes first
http://www.linux-support.com/cms/php-convert-hex-strings-to-ascii-strings/
So the method call now works :
$almostResult = hash_hmac ("sha1" , $stringToSign, $hexKey, true)
This returns a binary string - which you need to convert to base64 encoding.
$final = base64_encode ($almostResult)
That should do it...enjoy :)
I faced almost the same problem and after some googling i found this post:
https://www.reddit.com/r/PHP/comments/2k9tol/string_to_byte_array_using_utf8_encoding/
In PHP strings are already byte arrays. What is the specific problem you are having?
For me the solution was just base64_encode('apikey')
Related
Below is the code of both C# and PHP, I need some help regarding it. I am trying to generate the authenticationKey which is in C# but want the convert in PHP. All is done but I don't know how to implement [System.Text] in PHP as there is hash_hmac() in PHP but what might be the $string in the same function.
C# Version
var hmac = new System.Security.Cryptography.HMACSHA256();
var buffer = userName + accessKey + timeStamp + originUrl;
var hash = hmac.ComputeHash(System.Text.Encoding.UTF8.GetBytes(buffer));
var authenticationKey = Convert.ToBase64String(hash);
PHP version
$hmac = hash_hmac('sha256', $string, $buffer);
$encoded = base64_encode($hmac);
Can anyone help me with that. It will be very helpful. Thanks.
I was also searching for this and atlast it was just a mistake of 1 small varibale, in the hash_hmac function pass the last value as true, this is will return raw output and when you convert it to base64 it will give the same output as the c# code.
$buffer = $userName.$accessKey.$timeStamp.$originUrl;
$hmac = hash_hmac('sha256', $buffer, $tokenSecret, true);
$authenticationKey = base64_encode($hmac);
Just in your c# function use the key, as shown in the code below
var hmac = new System.Security.Cryptography.HMACSHA256();
hmac.Key = System.Text.Encoding.UTF8.GetBytes(tokenSecret);
In my code I have tokenSecret variable in bas64 form.
You may get the hex value, using unpack method:
$value = unpack('H*', buffer);
echo $value[1];
In your c# code, you used a randomly generated key. Check the documentation:
https://learn.microsoft.com/en-us/dotnet/api/system.security.cryptography.hmacsha256?view=netframework-4.8#constructors
HMACSHA256()
Initializes a new instance of the HMACSHA256 class with a randomly generated key.
HMACSHA256(Byte[])
Initializes a new instance of the HMACSHA256 class with the specified key data
Now the php documentation in comparison: https://www.php.net/manual/en/function.hash-hmac.php
hash_hmac ( string $algo , string $data , string $key [, bool $raw_output = FALSE ] ) : string
You see, what you call buffer in php, is actually the key and what you call buffer in c# is the data you hash.
So the$string should contain the data to be hashed.
As per the conversion, you don't need to get bytes in php: What is the PHP equivalent of this C# encoding code?
I'm working on rewriting a piece of PHP code to C#. This code is used for password hashing. In the first step it produces a string like "password{salt}", than hashes it via sha512 hash algorithm. After that a loop is hashing the combination of the first hash and the salt again for 5000 iterations.
The PHP Code looks like this:
<?php
$password = 'abc';
$salt = 'def';
$salted = $password.'{'.$salt.'}';
$digest = hash('sha512', $salted, true);
for ($i=1; $i<5000; $i++) {
$digest = hash('sha512', $digest.$salted, true);
}
$encodedPassword = base64_encode($digest);
//$encodedPassword contains the final hash code
I was able to get it working without the loop (with just the first hash() call). So the main hashing and base64 encoding is done correctly. I found out that this part is what I cannot manage to rewrite in C#:
$digest.$salted
$digest seems to be a binary representation since PHP's hash() function was used with "true" as the last parameter (see PHP hash - manual). $salted is a string. Both get somehow magically combined by PHP's dot / concat operator. I guess there will be some sort of standard conversion from binary to string under the hood when using the dot operator with a non-string operand.
This is my code so far:
void Main()
{
string password = "abc";
string salt = "def";
string salted = String.Format("{0}{{{1}}}", password, salt);
byte[] digest = hash(salted);
for(int i = 1; i < 1; i++)
{
digest = hash(String.Format("{0}{1}", System.Text.Encoding.UTF8.GetString(digest), salted));
}
var encodedPassword = System.Convert.ToBase64String(digest);
//$encodedPassword should contain the final hash code
}
static byte[] hash(string toHash)
{
System.Security.Cryptography.SHA512 sha512 = new System.Security.Cryptography.SHA512Managed();
return sha512.ComputeHash(System.Text.Encoding.UTF8.GetBytes(toHash));
}
As you see I tried to convert the hash bytes back to a string with System.Text.Encoding.UTF8.GetString() and then append the salt but that doesn't produce the same output as the PHP code.
I would be very happy if someone could help me on this. Thank you very much.
In the PHP version you loop 4999 times, while in the C# version 0. The second problem is that the returned bytes from hash() have no encoding at all.
This should give you the same result as the PHP version:
System.Security.Cryptography.SHA512 sha512 = new System.Security.Cryptography.SHA512Managed();
var saltedUtf8Bytes = System.Text.Encoding.UTF8.GetBytes(salted);
for(int i = 1; i < 5000; i++)
{
digest = sha512.ComputeHash(digest.Concat(saltedUtf8Bytes).ToArray());
}
I'm trying to get a website Login page and a C# launcher to connect to a MySQL database, my C# code converts a string to SHA256 but in uppercase. So I figured it would be easier to change in PHP, so using strtoupper I pass the variable string for the encrypted password. It works great the only problem is this:
bec4c38f480db265e86e1650b1515216be5095f7a049852f76eea9934351b9ac - Original
BEC4C38F48DB265E86E1650B1515216BE5095F7A049852F76EEA9934351B9AC - C#
^ Right here there is meant to be a 0
I'm not sure what's gone wrong as both are using the exact same encryption method and it's odd that it's only one Character... Has anyone experienced this before?
PHP to encrypt text to SHA256 and then strtoupper:
$encrypt_password=(hash('sha256', $mypassword));
$s_password = strtoupper($encrypt_password);
C# Convert string to SHA256:
System.Security.Cryptography.SHA256 sha256 = new System.Security.Cryptography.SHA256Managed();
byte[] sha256Bytes = System.Text.Encoding.Default.GetBytes(txtpass.Text);
byte[] cryString = sha256.ComputeHash(sha256Bytes);
string sha256Str = string.Empty;
for (int i = 0; i < cryString.Length; i++)
{
sha256Str += cryString[i].ToString("X");
}
This is the only code that involves encrypting on both sides.
A value like 13 is just "D" not "0D" like would represented in the hash. You need to pad values that are less than 2 digits. Use "X2" as the format string.
I have an algorithm in C# running on server side which hashes a base64-encoded string.
byte[] salt = Convert.FromBase64String(serverSalt); // Step 1
SHA256Managed sha256 = new SHA256Managed(); // Step 2
byte[] hash = sha256.ComputeHash(salt); // Step 3
Echo("String b64: " + Convert.ToBase64String(hash)); // Step 4
The hash is then checked against a database list of hashes.
I'd love to achieve the same with javascript, using the serverSalt as it is transmitted from C# through a websocket.
I know SHA-256 hashes different between C# and Javascript because C# and Javascript have different string encodings.
But I know I can pad zeros in the byte array to make Javascript behave as C# (step 1 above is solved).
var newSalt = getByteArrayFromCSharpString(salt); // Pad zeros where needed
function getByteArrayFromCSharpString(inString)
{
var bytes = [];
for (var i = 0; i < inString.length; ++i)
{
bytes.push(inString.charCodeAt(i));
bytes.push(0);
}
return bytes;
}
Could anyone provide some insight on which algorithms I could use to reproduce steps 2, 3 and 4?
PS: there are already questions and answers around but not a single code snippet.
Here's the solution, I really hope this could help other people in the same situation.
In the html file, load crypto-js library
<!-- library for doing password hashing, base64 eoncoding / decoding -->
<script src="http://crypto-js.googlecode.com/svn/tags/3.0.2/build/components/core-min.js"></script>
<script src="http://crypto-js.googlecode.com/svn/tags/3.0.2/build/components/enc-base64-min.js"></script>
<script src="http://crypto-js.googlecode.com/svn/tags/3.0.2/build/rollups/sha256.js"></script>
In the javascript, do the following
// This function takes a base64 string, hashes it with the SHA256 algorithm
// and returns a base64 string.
function hashBase64StringAndReturnBase64String(str)
{
// Take the base64 string and parse it into a javascript variable
var words = CryptoJS.enc.Base64.parse(str);
// Create the hash using the CryptoJS implementation of the SHA256 algorithm
var hash = CryptoJS.SHA256(words);
var outString = hash.toString(CryptoJS.enc.Base64)
// Display what you just got and return it
console.log("Output string is: " + outString);
return outString;
}
check Java script SHA256 implementation on the following URL
http://www.movable-type.co.uk/scripts/sha256.html
The question is pretty much self-explanatory. I Googled many sites, many methods, tried many encodings, but I can't get it to match.
I'm trying to make the string "asdasd" match. (http://www.fileformat.info/tool/hash.htm?text=asdasd)
Try this
using System.Security.Cryptography
public static string HashPassword(string unhashedPassword)
{
return BitConverter.ToString(new SHA512CryptoServiceProvider().ComputeHash(Encoding.Default.GetBytes(unhashedPassword))).Replace("-", String.Empty).ToUpper();
}
BitConverter works just fine ...
var testVal = "asdasd";
var enc = new ASCIIEncoding();
var bytes = enc.GetBytes( testVal );
var sha = new SHA512Managed();
var result = sha.ComputeHash( bytes );
var resStr = BitConverter.ToString( result );
var nodash = resStr.Replace( "-", "" );
nodash.Dump();
(Fixed for 512-bit hash, sorry :)
I just spent several hours trying to get a .NET hash function to match PHP's Crypt function. Not fun.
There are multiple challenges here, since the PHP implementation of Crypt returns a base64 encoded string, and doesn't do multiple hashing iterations (e.g. 5000 is default for Crypt.) I was not able to get similar outputs from .NET using several libraries, until I found CryptSharp. It accepts a salt similar to PHP's (or the original C) function (e.g. "$6$round=5000$mysalt"). Note that there is no trailing $, and that if you don't provide a salt it will autogenerate a random one.
You can find CryptSharp here:
http://www.zer7.com/software.php?page=cryptsharp
Good background reading:
- http://www.akkadia.org/drepper/SHA-crypt.txt