I have a javascript backend that use CryptoJS to generate a hash, I need to generate the same hash on C# Client but can't reproduce the same result than javascript.
The backend code are this:
function generateHash (str, cypherkey) {
return CryptoJS.enc.Base64.stringify(CryptoJS.HmacSHA256(str, CryptoJS.enc.Base64.parse(cypherkey)))
}
console.log(generateHash("testString", "UTI5dVozSmhkSE1zSUhsdmRTZDJaU0JtYjNWdVpDQnBkQ0VnUVhKbElIbHZkU0J5WldGa2VTQjBieUJxYjJsdUlIVnpQeUJxYjJKelFIZGhiR3hoY0c5d0xtTnZiUT09"))
And print: "FwdJUHxt/xSeNxHQFiOhmPDRh73NFfuWK7LG6ssN9k4="
Then when I try to do the same on my C# client with this code:
public static string generateHash(string str, string cypherkey)
{
var keyenc = new System.Text.ASCIIEncoding();
byte[] keyBytes = keyenc.GetBytes(cypherkey);
var key = BitConverter.ToString(keyBytes);
var encoding = new System.Text.ASCIIEncoding();
byte[] keyByte = encoding.GetBytes(key);
byte[] messageBytes = encoding.GetBytes(str);
using (var hmacsha256 = new HMACSHA256(keyByte))
{
byte[] hashmessage = hmacsha256.ComputeHash(messageBytes);
return Convert.ToBase64String(hashmessage);
}
}
Print other result: "SiEjJASvYWfO5y+EiSJAqamMcUyBSTDl5Sy1zXl1J/k="
The problem are on the process to convert to Base64 the cypherkey, probably it's wrong.
Anyone know how can solve this?
Greetings and a lot of thanks ^^
I haven't seen the source of CryptoJs so there are assumptions here (from method names, encoding, etc):
public static string generateHash(string str, string cypherkey)
{
// based on CryptoJS.enc.Base64.parse
byte[] keyBytes = System.Convert.FromBase64String(cypherkey);
using (var hmacsha256 = new HMACSHA256(keyBytes))
{
byte[] hashmessage = hmacsha256.ComputeHash(System.Text.Encoding.UTF8.GetBytes(str));
return Convert.ToBase64String(hashmessage);
}
}
Result:
FwdJUHxt/xSeNxHQFiOhmPDRh73NFfuWK7LG6ssN9k4=
Hth
Related
I am trying to decrypt a blowfish encrypted string with Bouncycastle in C#.
I am able to easily encrypt and decrypt my own string but, unfortunately, I have to decrypt a string that is generated by another system.
I AM able to recreate that same string with C# / Bouncycastle using the following but I have yet to decrypt it successfully.
using Org.BouncyCastle.Crypto.Engines;
using Org.BouncyCastle.Crypto.Paddings;
using Org.BouncyCastle.Crypto.Parameters;
...
static readonly Encoding Encoding = Encoding.UTF8;
public string BlowfishEncrypt(string strValue, string key)
{
try
{
BlowfishEngine engine = new BlowfishEngine();
PaddedBufferedBlockCipher cipher = new PaddedBufferedBlockCipher(engine);
KeyParameter keyBytes = new KeyParameter(Encoding.GetBytes(key));
cipher.Init(true, keyBytes);
byte[] inB = Encoding.GetBytes(strValue);
byte[] outB = new byte[cipher.GetOutputSize(inB.Length)];
int len1 = cipher.ProcessBytes(inB, 0, inB.Length, outB, 0);
cipher.DoFinal(outB, len1);
return BitConverter.ToString(outB).Replace("-", "");
}
catch (Exception)
{
return "";
}
}
Below is what I have for decryption at the moment. The line that fails with error "pad block corrupted" is cipher.DoFinal(out2, len2);
public string BlowfishDecrypt(string name, string keyString)
{
BlowfishEngine engine = new BlowfishEngine();
PaddedBufferedBlockCipher cipher = new PaddedBufferedBlockCipher(engine);
StringBuilder result = new StringBuilder();
cipher.Init(false, new KeyParameter(Encoding.GetBytes(keyString)));
byte[] out1 = Convert.FromBase64String(name);
byte[] out2 = new byte[cipher.GetOutputSize(out1.Length)];
int len2 = cipher.ProcessBytes(out1, 0, out1.Length, out2, 0);
cipher.DoFinal(out2, len2); //Pad block corrupted error happens here
String s2 = BitConverter.ToString(out2);
for (int i = 0; i < s2.Length; i++) {
char c = s2[i];
if (c != 0) {
result.Append(c.ToString());
}
}
return result.ToString();
}
Any idea what I might be doing wrong in BlowfishDecrypt()?
Note:
I converted the above (encrypt and decrypt) from a bouncycastle Java example I found somewhere; the encrypt works. The only difference I can see is that the Java example uses a StringBuffer where I use a StringBuilder.
Thank you, Artjom B!
byte[] out1 = Convert.FromBase64String(name);
Should have been
byte[] out1 = Hex.Decode(name);
From there, all I had to do was convert the Hex to a string.
First, I realize there are dozens of other posts that have answers to this question and I have read and tried them all. I still can't seem to get past this issue so am looking for a little help from somebody that knows more about crypto than I do.
Second, the code I am going to share is legacy and because I am not a crypto expert it is still not 100% clear on what everything means. It may be that some or all of this code is total rubbish and should be scrapped; however, there are a lot of other systems already using it and have stored encrypted values that have gone through this code. Changing things like the crypto algorithm is not exactly an option at this point. With that said, the private methods are the legacy code as well as the testing values (i.e. the encryption key) are all things that can't change. The two public static methods are what is new and likely causing problems, but I can't seem to figure it out.
On with the code......
class Program
{
public static string Encrypt(string key, string toEncrypt)
{
var keyArray = Convert.FromBase64String(key);
var info = Encoding.ASCII.GetBytes(toEncrypt);
var encrypted = Encrypt(keyArray, info);
return Encoding.ASCII.GetString(encrypted);
}
public static string Decrypt(string key, string cipherString)
{
var keyArray = Convert.FromBase64String(key);
var cipherText = Encoding.ASCII.GetBytes(cipherString);
var decrypted = Decrypt(keyArray, cipherText);
return Encoding.ASCII.GetString(decrypted);
}
private static byte[] Encrypt(byte[] key, byte[] info)
{
using (var cipher = Aes.Create())
{
cipher.Key = key;
cipher.Mode = CipherMode.CBC;
cipher.Padding = PaddingMode.ISO10126;
using (var ms = new MemoryStream())
{
using (var cs = new CryptoStream(ms, cipher.CreateEncryptor(), CryptoStreamMode.Write))
{
cs.Write(info, 0, info.Length);
}
var ciphertext = ms.ToArray();
var message = new byte[cipher.IV.Length + ciphertext.Length];
cipher.IV.CopyTo(message, 0);
ciphertext.CopyTo(message, cipher.IV.Length);
return message;
}
}
}
private static byte[] Decrypt(byte[] key, byte[] ciphertext)
{
using (var cipher = Aes.Create())
{
cipher.Key = key;
cipher.Mode = CipherMode.CBC;
cipher.Padding = PaddingMode.ISO10126;
var ivSize = cipher.IV.Length;
var iv = new byte[ivSize];
Array.Copy(ciphertext, iv, ivSize);
cipher.IV = iv;
var data = new byte[ciphertext.Length - ivSize];
Array.Copy(ciphertext, ivSize, data, 0, data.Length);
using (var ms = new MemoryStream())
{
using (var cs = new CryptoStream(ms, cipher.CreateDecryptor(), CryptoStreamMode.Write))
{
cs.Write(data, 0, data.Length);
}
return ms.ToArray();
}
}
}
static void Main(string[] args)
{
var newEncryptionKey = Guid.NewGuid().ToString().Replace("-", string.Empty);
var encryptedValue = Encrypt(newEncryptionKey, "test");
Console.WriteLine($"New encrypted value: {encryptedValue}");
var decryptedValue = Decrypt(newEncryptionKey, encryptedValue);
Console.WriteLine($"New decrypted value: {decryptedValue}");
}
}
So there it is. Basically, I am trying to use a test string of "test" and encrypt it using a GUID as a key. Again, I didn't choose this key and there are encrypted values already using a GUID as a key so I can't change that if at all possible. The encryption works fine, but when I go to do the decryption, I get the exception noted in the title of this question.
Any help would be GREATLY appreciated.
You can't just convert a byte[] of ciphertext to ASCII. It doesn't work like that. Character encodings are scary beasts and should not be muddled with if you don't understand them. I don't think there is a real person alive that does ;)
What you should do instead is return your result as base64, which is still a collection of ASCII characters but they are safe to be moved around as a string, and don't result in the loss of any characters.
See the modified code below:
public static string Encrypt(string key, string toEncrypt)
{
var keyArray = Convert.FromBase64String(key);
var info = Encoding.ASCII.GetBytes(toEncrypt);
var encrypted = Encrypt(keyArray, info);
return Convert.ToBase64String(encrypted);
}
public static string Decrypt(string key, string cipherString)
{
var keyArray = Convert.FromBase64String(key);
var cipherText = Convert.FromBase64String(cipherString);
var decrypted = Decrypt(keyArray, cipherText);
return Encoding.ASCII.GetString(decrypted);
}
I need to create a hash-signature in c#.
The pseudo-code example that i need to implement in my c# code:
Signatur(Request) = new String(encodeBase64URLCompatible(HMAC-SHA-256(getBytes(Z, "UTF-8"), decodeBase64URLCompatible(getBytes(S, "UTF-8")))), "UTF-8")
Z: apiSecret
S: stringToSign
The coding for expectedSignatur and apiSecret is Base64 URL Encoding [RFC 4648 Section 5]
My problem is that I always get the wrong result.
public static string Base64Decode(string base64EncodedData)
{
var base64EncodedBytes = Convert.FromBase64String(base64EncodedData);
return Encoding.UTF8.GetString(base64EncodedBytes);
}
public static string Base64Encode(string plainText)
{
var plainTextBytes = Encoding.UTF8.GetBytes(plainText);
return Convert.ToBase64String(plainTextBytes);
}
private static byte[] HmacSha256(string data, string key)
{
using (var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(key)))
{
return hmac.ComputeHash(Encoding.UTF8.GetBytes(data));
}
}
static void Main(string[] args)
{
var apiSecret = "JrXRHCnUegQJAYSJ5J6OvEuOUOpy2q2-MHPoH_IECRY=";
var stringToSign = "f3fea5f3-60af-496f-ac3e-dbb10924e87a:20160201094942:e81d298b-60dd-4f46-9ec9-1dbc72f5b5df:Qg5f0Q3ly1Cwh5M9zcw57jwHI_HPoKbjdHLurXGpPg0yazdC6OWPpwnYi22bnB6S";
var expectedSignatur = "ps9MooGiTeTXIkPkUWbHG4rlF3wuTJuZ9qcMe-Y41xE=";
apiSecret = apiSecret.Replace('-', '+').Replace('_', '/').PadRight(apiSecret.Length + (4 - apiSecret.Length % 4) % 4, '=');
var secretBase64Decoded = Base64Decode(apiSecret);
var hmac = Convert.ToBase64String(HmacSha256(secretBase64Decoded, stringToSign));
var signatur = hmac.Replace('+', '-').Replace('/', '_');
Console.WriteLine($"signatur: {signatur}");
Console.WriteLine($"expected: {expectedSignatur}");
Console.WriteLine(signatur.Equals(expectedSignatur));
Console.ReadLine();
}
You're assuming that your key was originally text encoded with UTF-8 - but it looks like it wasn't. You should keep logically binary data as binary data - you don't need your Base64Encode and Base64Decode methods at all. Instead, your HmacSha256 method should take a byte[] as a key, and you can just use Convert.FromBase64String to get at those bytes from the base64-encoded secret:
using System;
using System.Text;
using System.Security.Cryptography;
class Test
{
private static byte[] HmacSha256(byte[] key, string data)
{
using (var hmac = new HMACSHA256(key))
{
return hmac.ComputeHash(Encoding.UTF8.GetBytes(data));
}
}
static void Main(string[] args)
{
var apiSecret = "JrXRHCnUegQJAYSJ5J6OvEuOUOpy2q2-MHPoH_IECRY=";
var stringToSign = "f3fea5f3-60af-496f-ac3e-dbb10924e87a:20160201094942:e81d298b-60dd-4f46-9ec9-1dbc72f5b5df:Qg5f0Q3ly1Cwh5M9zcw57jwHI_HPoKbjdHLurXGpPg0yazdC6OWPpwnYi22bnB6S";
var expectedSignatur = "ps9MooGiTeTXIkPkUWbHG4rlF3wuTJuZ9qcMe-Y41xE=";
apiSecret = apiSecret.Replace('-', '+').Replace('_', '/').PadRight(apiSecret.Length + (4 - apiSecret.Length % 4) % 4, '=');
var secretBase64Decoded = Convert.FromBase64String(apiSecret);
var hmac = Convert.ToBase64String(HmacSha256(secretBase64Decoded, stringToSign));
var signatur = hmac.Replace('+', '-').Replace('/', '_');
Console.WriteLine($"signatur: {signatur}");
Console.WriteLine($"expected: {expectedSignatur}");
Console.WriteLine(signatur.Equals(expectedSignatur));
}
}
Personally I'd change your HmacSha256 method to:
private static byte[] ComputeHmacSha256Hash(byte[] key, byte[] data)
{
using (var hmac = new HMACSHA256(key))
{
return hmac.ComputeHash(data);
}
}
so that it's more general purpose, maybe adding another method to compute the hash after encoding as UTF-8 for convenience. That way you can sign any data, not just strings.
I am trying to make use of a REST API using C#. The API creator has provided sample libraries in PHP, Ruby and Java. I am getting hung up on one part of it where I need to generate an HMAC.
Here's how it is done in the sample libraries they have provided.
PHP
hash_hmac('sha1', $signatureString, $secretKey, false);
Ruby
digest = OpenSSL::Digest::Digest.new('sha1')
return OpenSSL::HMAC.hexdigest(digest, secretKey, signatureString)
Java
SecretKeySpec signingKey = new SecretKeySpec(secretKey.getBytes(), HMAC_SHA1_ALGORITHM);
Mac mac = null;
mac = Mac.getInstance(HMAC_SHA1_ALGORITHM);
mac.init(signingKey);
byte[] bytes = mac.doFinal(signatureString.getBytes());
String form = "";
for (int i = 0; i < bytes.length; i++)
{
String str = Integer.toHexString(((int)bytes[i]) & 0xff);
if (str.length() == 1)
{
str = "0" + str;
}
form = form + str;
}
return form;
Here's my attempt in C#. It is not working. UPDATE: The C# example below works just fine. I found out that the real problem was due to some cross-platform differences in newline characters in my signatureString.
var enc = Encoding.ASCII;
HMACSHA1 hmac = new HMACSHA1(enc.GetBytes(secretKey));
hmac.Initialize();
byte[] buffer = enc.GetBytes(signatureString);
return BitConverter.ToString(hmac.ComputeHash(buffer)).Replace("-", "").ToLower();
an extension to Vimvq1987's answer:
return hashValue.ToString(); doesn't produce the output you want/need. You have to convert the bytes in the array hashValue to their hex-string representation.
Can be as simple as return BitConverter.toString(hashValue); (prints upper-case letters A-F) or if you like it a bit more complex:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security.Cryptography;
using System.IO;
namespace ConsoleApplication1
{
class Program
{
public static string Encode(string input, byte[] key)
{
HMACSHA1 myhmacsha1 = new HMACSHA1(key);
byte[] byteArray = Encoding.ASCII.GetBytes(input);
MemoryStream stream = new MemoryStream(byteArray);
return myhmacsha1.ComputeHash(stream).Aggregate("", (s, e) => s + String.Format("{0:x2}",e), s => s );
}
static void Main(string[] args)
{
byte[] key = Encoding.ASCII.GetBytes("abcdefghijklmnopqrstuvwxyz");
string input = "";
foreach (string s in new string[] { "Marry", " had", " a", " little", " lamb" })
{
input += s;
System.Console.WriteLine( Encode(input, key) );
}
return;
}
}
}
which prints
3545e064fb59bc4bfc02b6e1c3d4925c898aa504
3249f4c8468d4d67f465937da05b809eaff22fdb
87baaadf5d096677f944015e53d283834eb1e943
6325376820c29a09e3ab30db000033aa71d6927d
54579b0146e2476595381d837ee38863be358213
and I get the exact same result for
<?php
$secretKey = 'abcdefghijklmnopqrstuvwxyz';
$signatureString = '';
foreach( array('Marry',' had',' a',' little',' lamb') as $s ) {
$signatureString .= $s;
echo hash_hmac('sha1', $signatureString, $secretKey, false), "\n";
}
edit: Dmitriy Nemykin suggested the following edit
public static string Encode(string input, byte[] key)
{
byte[] byteArray = Encoding.ASCII.GetBytes(input);
using(var myhmacsha1 = new HMACSHA1(key))
{
var hashArray = myhmacsha1.ComputeHash(byteArray);
return hashArray.Aggregate("", (s, e) => s + String.Format("{0:x2}",e), s => s );
}
}
which was rejected. But as James already pointed out in a comment to this answer at the very least the using statement is a good point.
This site has some pretty good examples across languages: http://jokecamp.wordpress.com/2012/10/21/examples-of-creating-base64-hashes-using-hmac-sha256-in-different-languages/
The c# implementation at the time of writing is:
private string CreateToken(string message, string secret)
{
secret = secret ?? "";
var encoding = new System.Text.ASCIIEncoding();
byte[] keyByte = encoding.GetBytes(secret);
byte[] messageBytes = encoding.GetBytes(message);
using (var hmacsha256 = new HMACSHA256(keyByte))
{
byte[] hashmessage = hmacsha256.ComputeHash(messageBytes);
return Convert.ToBase64String(hashmessage);
}
}
Try this:
http://msdn.microsoft.com/en-us/library/system.security.cryptography.hmacsha1.aspx
quick and dirty code:
public string Encode(string input, byte [] key)
{
HMACSHA1 myhmacsha1 = new HMACSHA1(key);
byte[] byteArray = Encoding.ASCII.GetBytes( input );
MemoryStream stream = new MemoryStream( byteArray );
byte[] hashValue = myhmacsha1.ComputeHash(stream);
return hashValue.ToString();
}
Looking for a way to do the following in C# from a string.
public static String sha512Hex(byte[] data)
Calculates the SHA-512 digest and returns the value as a hex string.
Parameters:
data - Data to digest
Returns:
SHA-512 digest as a hex string
private static string GetSHA512(string text)
{
UnicodeEncoding UE = new UnicodeEncoding();
byte[] hashValue;
byte[] message = UE.GetBytes(text);
SHA512Managed hashString = new SHA512Managed();
string encodedData = Convert.ToBase64String(message);
string hex = "";
hashValue = hashString.ComputeHash(UE.GetBytes(encodedData));
foreach (byte x in hashValue)
{
hex += String.Format("{0:x2}", x);
}
return hex;
}
Would System.Security.Cryptography.SHA512 be what you need?
var alg = SHA512.Create();
alg.ComputeHash(Encoding.UTF8.GetBytes("test"));
BitConverter.ToString(alg.Hash).Dump();
Executed in LINQPad produces:
EE-26-B0-DD-4A-F7-E7-49-AA-1A-8E-E3-C1-0A-E9-92-3F-61-89-80-77-2E-47-3F-88-19-A5-D4-94-0E-0D-B2-7A-C1-85-F8-A0-E1-D5-F8-4F-88-BC-88-7F-D6-7B-14-37-32-C3-04-CC-5F-A9-AD-8E-6F-57-F5-00-28-A8-FF
To create the method from your question:
public static string sha512Hex(byte[] data)
{
using (var alg = SHA512.Create())
{
alg.ComputeHash(data);
return BitConverter.ToString(alg.Hash);
}
}
Got this to work. Taken from here and modified a bit.
public static string CreateSHAHash(string Phrase)
{
SHA512Managed HashTool = new SHA512Managed();
Byte[] PhraseAsByte = System.Text.Encoding.UTF8.GetBytes(string.Concat(Phrase));
Byte[] EncryptedBytes = HashTool.ComputeHash(PhraseAsByte);
HashTool.Clear();
return Convert.ToBase64String(EncryptedBytes);
}
Better memory management:
public static string SHA512Hash(string value)
{
byte[] encryptedBytes;
using (var hashTool = new SHA512Managed())
{
encryptedBytes = hashTool.ComputeHash(System.Text.Encoding.UTF8.GetBytes(string.Concat(value)));
hashTool.Clear();
}
return Convert.ToBase64String(encryptedBytes);
}