Signing a string with HMAC-MD5 with C# - security

I got the following HMAC key (in hexadecimal format):
52320e181a481f5e19507a75b3cae4d74d5cfbc328f7f2b738e9fb06b2e05b55b632c1c3d331dcf3baacae8d3000594f839d770f2080910b52b7b8beb3458c08
I need to sign this string:
1100002842850CHF91827364
The result should be this (in hexadecimal format):
2ad2f79111afd818c1dc0916d824b0a1
I have the following code:
string key = "52320e181a481f5e19507a75b3cae4d74d5cfbc328f7f2b738e9fb06b2e05b55b632c1c3d331dcf3baacae8d3000594f839d770f2080910b52b7b8beb3458c08";
string payload = "1100002842850CHF91827364";
byte[] keyInBytes = Encoding.UTF8.GetBytes(key);
byte[] payloadInBytes = Encoding.UTF8.GetBytes(payload);
var md5 = new HMACMD5(keyInBytes);
byte[] hash = md5.ComputeHash(payloadInBytes);
var result = BitConverter.ToString(hash).Replace("-", string.Empty);
However, I am not getting the result. What am I doing wrong?

when hashing with key HMAC md5
var data = Encoding.UTF8.GetBytes(plaintext);
// key
var key = Encoding.UTF8.GetBytes(transactionKey);
// Create HMAC-MD5 Algorithm;
var hmac = new HMACMD5(key);
// Compute hash.
var hashBytes = hmac.ComputeHash(data);
// Convert to HEX string.
return System.BitConverter.ToString(hashBytes).Replace("-", "").ToLower();

Instead of doing this:
byte[] keyInBytes = Encoding.UTF8.GetBytes(key);
you need to convert key from a hex string to array of bytes. Here you can find example:
How do you convert Byte Array to Hexadecimal String, and vice versa?

Related

Node.JS encrypt using MD5 / ECB and padding w/PKCS7

I inherited a database that has the passwords utilizing the following functions to encrypt/decrypt in VB.NET
Public Shared Function EncryptString(ByVal Message As String, ByVal Passphrase As String) As String
Dim Results As Byte()
Dim UTF8 As System.Text.UTF8Encoding = New System.Text.UTF8Encoding()
Dim HashProvider As MD5CryptoServiceProvider = New MD5CryptoServiceProvider()
Dim TDESKey As Byte() = HashProvider.ComputeHash(UTF8.GetBytes(Passphrase))
Dim TDESAlgorithm As TripleDESCryptoServiceProvider = New TripleDESCryptoServiceProvider()
TDESAlgorithm.Key = TDESKey
TDESAlgorithm.Mode = CipherMode.ECB
TDESAlgorithm.Padding = PaddingMode.PKCS7
Dim DataToEncrypt As Byte() = UTF8.GetBytes(Message)
Dim Encryptor As ICryptoTransform = TDESAlgorithm.CreateEncryptor()
Results = Encryptor.TransformFinalBlock(DataToEncrypt, 0, DataToEncrypt.Length)
Return Convert.ToBase64String(Results)
End Function
Public Shared Function DecryptString(ByVal Message As String, ByVal Passphrase As String) As String
Dim Results As Byte()
Dim UTF8 As System.Text.UTF8Encoding = New System.Text.UTF8Encoding()
Dim HashProvider As MD5CryptoServiceProvider = New MD5CryptoServiceProvider()
Dim TDESKey As Byte() = HashProvider.ComputeHash(UTF8.GetBytes(Passphrase))
Dim TDESAlgorithm As TripleDESCryptoServiceProvider = New TripleDESCryptoServiceProvider()
TDESAlgorithm.Key = TDESKey
TDESAlgorithm.Mode = CipherMode.ECB
TDESAlgorithm.Padding = PaddingMode.PKCS7
Dim DataToDecrypt As Byte() = Convert.FromBase64String(Message)
Dim Decryptor As ICryptoTransform = TDESAlgorithm.CreateDecryptor()
Results = Decryptor.TransformFinalBlock(DataToDecrypt, 0, DataToDecrypt.Length)
Return UTF8.GetString(Results)
End Function
I am having a hard time encrypting the data to query the SQL server.
When using EncryptString with the following parameters:
Message = stack-overflow
Passphrase = Danielle
I get:
1c2wL+guV34oyVS0vkxwVA==
I need to do this encryption and decryption with node.js
I tried this but it did not work:
var crypto = require('crypto');
encrypt(text, paraphrase) {
const key =text;
const secret =paraphrase;
// Encryption
const cipher = crypto.createCipheriv("aes-192-ecb", Buffer.from(key, "base64"), null);
const encryptedSecret = cipher.update(secret, "utf8", "base64") + cipher.final("base64");
return(encryptedSecret);
};
I also tried installing MD5 by npm but I had no luck either.
Any help to encrypt and decrypt will be appreciated. Thank you all.
For the NodeJS code to be compatible with the VB code, TripleDES must be used instead of AES. The key must be derived with MD5. Since MD5 provides a 16 bytes key, TripleDES is applied in the double-length key variant (2TDEA), which combines two DES keys K1 and K2 to a TripleDES key K1|K2|K1. One possible implementation is:
var key16 = crypto.createHash('md5').update(passphrase).digest();
var cipher = crypto.createCipheriv("des-ede-ecb", key16, null);
var ciphertext = cipher.update(data, "utf8", "base64") + cipher.final("base64");
If des-ede-ecb is not supported, des-ede3-ecb can be used as an alternative. This implements TripleDES in the triple-length key variant (3TDEA), which combines three DES keys K1, K2 and K3 to a TripleDES key K1|K2|K3. For K3 = K1 this is equivalent to 2TDEA:
var key16 = crypto.createHash('md5').update(passphrase).digest();
var key24 = Buffer.concat([key16, key16.slice(0, 8)]);
var cipher = crypto.createCipheriv("des-ede3-ecb", key24, null);
Note that ECB is insecure (better a mode with an IV like GCM), as is key derivation via a cryptographic hash function (better a reliable key derivation function like PBKDF2) and the broken MD5 as such (better SHA256). TripleDES is deprecated and slow (better AES).
Edit:
The decryption is completely analog, e.g. for 2TDEA:
var key16 = crypto.createHash('md5').update(passphrase).digest();
var decipher = crypto.createDecipheriv("des-ede-ecb", key16, null);
var decrypted = decipher.update(ciphertext, "base64", "utf8") + decipher.final("utf8");

Node - Convert from base64 string to utf-8 byte code array

I'm currently trying to convert images from a base 64 string to a utf-8 byte code array. Below is the code I'm attempting to use with the base 64 string replaced for clarity. I'm getting what looks to be a utf-8 byte array, but have not been able to find a way to verify. Any help with a resource to help verify or if the code is incorrect?
const b64 = '...base64 string';
// convert to utf8 string
const utf8 = (Buffer.from(b64, 'base64')).toString('utf8');
// create a buffer of utf8 string
const buff = Buffer.from(utf8, 'utf8');
//
const arr = [...buff];

Replicating Asp.net Identity Password Hash to Chilkat

Hi I want to replicate the password hashing that is done in asp.net identity such that, the resulting value of password hashed by asp.net identity and the password hashed by Chilkat are same. Is that even possible?
In C# asp.net, we use Rfc2898DeriveBytes that does the pbkdf2 for us. How can I do the same in Chilkat?
private const int PBKDF2IterCount = 1000; // default for Rfc2898DeriveBytes
private const int PBKDF2SubkeyLength = 256 / 8; // 256 bits
private const int SaltSize = 128 / 8; // 128 bits
//[ComVisible(true)]
public string HashPassword(string password)
{
if (password == null)
{
throw new ArgumentNullException("password cannot be null");
}
// Produce a version 0 (see comment above) text hash.
byte[] salt;
byte[] subkey;
using (var deriveBytes = new Rfc2898DeriveBytes(password, SaltSize, PBKDF2IterCount))
{
salt = deriveBytes.Salt;
subkey = deriveBytes.GetBytes(PBKDF2SubkeyLength);
}
var outputBytes = new byte[1 + SaltSize + PBKDF2SubkeyLength];
Buffer.BlockCopy(salt, 0, outputBytes, 1, SaltSize);
Buffer.BlockCopy(subkey, 0, outputBytes, 1 + SaltSize, PBKDF2SubkeyLength);
return Convert.ToBase64String(outputBytes);
}
Currently, the parameters I am using at Chilkat are:
Function EncryptChilkat(sPassword As String) As String
Dim crypt As New ChilkatCrypt2
Dim success As Long
success = crypt.UnlockComponent("ACHIEV.CR1082018_dCrRA3zr4e1M ")
If (success <> 1) Then
Debug.Print crypt.LastErrorText
Exit Function
End If
Dim hexKey As String
Dim pw As String
pw = "pwd"
Dim pwCharset As String
pwCharset = "base64"
' Hash algorithms may be: sha1, md2, md5, etc.
Dim hashAlg As String
hashAlg = "HMCSHA1"
' The salt should be 8 bytes:
Dim saltHex As String
saltHex = "78578E5A5D63CB06"
Dim iterationCount As Long
iterationCount = 1000
' Derive a 128-bit key from the password.
Dim outputBitLen As Long
outputBitLen = 128
' The derived key is returned as a hex or base64 encoded string.
' (Note: The salt argument must be a string that also uses
' the same encoding.)
Dim enc As String
enc = "base64"
hexKey = crypt.Pbkdf2(pw, pwCharset, hashAlg, saltHex, iterationCount, outputBitLen, enc)
EncryptChilkat = hexKey
End Function
Check the binary values of both the password and the salt on both sides. Also check for trailing nulls, carriage returns, and line feeds.
Additionally, you can see which, if either, algorithm is misbehaving - I have a copy of Jither's .NET PBKDF2 implementation at my github repository including test vectors, and for your Chillkat, you can create what you need from my LibreOffice Calc sheet of PBKDF2 test vectors.
Run these through both implementations; whichever one fails is wrong.
If both succeed... then you're not giving both the same parameters.

TripleDes CBC Nodejs implementation throuble

i need to replicate in Nodejs the results of the 3DS CBC encrypt in http://tripledes.online-domain-tools.com/.
This is my code:
const crypto = require('crypto');
const cipher = crypto.createCipher('des-ede3-cbc', key);
password = Buffer.from('MYPASS', 'utf8');
let encrypted = [cipher.update(password)];
encrypted.push(cipher.final());
encrypted = Buffer.concat(encryptedArr);
console.log(encrypted.toString('hex'));
The result of tripledes.online-domain-tools.com is:
Note that the result should be 59 30 20 02 a5 8c dd 5e, but my code gives me 33 97 d8 b0 e3 00 d1 53.
What am i missing?
Edit2:
Following your suggestions, I changed my code (Also added some Tests made with the guide of the NIST Publication):
const crypto = require('crypto');
function encrypt (inputkey, keyformat, password, passwordformat) {
let shortkey = Buffer.from(inputkey, keyformat);
let key = Buffer.alloc(24);
key.fill('\0');
for (i = 0; i < shortkey.length; i++) {
key[i] = shortkey[i];
}
let IV = Buffer.alloc(8);
const cipher = crypto.createCipheriv('des-ede3-cbc', key, IV);
password = Buffer.from(password, passwordformat);
let encryptedArr = [cipher.update(password)];
encryptedArr.push(cipher.final());
encrypted = Buffer.concat(encryptedArr);
return encrypted;
}
console.log(encrypt('1046913489980131','hex','0000000000000000','hex')); // works
console.log(encrypt('1007103489988020','hex','0000000000000000','hex')); // works
console.log(encrypt('10071034C8980120','hex','0000000000000000','hex')); // works
console.log(encrypt('1046103489988020','hex','0000000000000000','hex')); // works
console.log(encrypt('MYKEY','utf8','MYPASS','utf8')); // fails
Every Permutation Operation Known Answer Test of the NIST works great, but several other examples (including the one of the image) just fails
The reason i'm testing with this shady page is because my service provider is using it as reference.
This site made me some troubles for some time , well here is the implementation it uses internally to expand the key to be a 24 bytes !
i am going to talk about tripledes but i guess this would apply to other algorithms used by this site
step 1
it first checks if the key entered has the length that it expects it to be, (you can find a table at the bottom of that site telling the length of key for each Encryption algorithm)
if it does not it will complete with 0x00 bytes like this:
var key;// is a string containing the bytes wich will be used to encrypt the msg
var nullByte = 0x00;
var padding_needed;
for (var i=key.length ;i < expected_key_length ; ++)
{padding_needed =padding_needed + nullBute.tostring(16); }
key = key + padding_needed
so for example the length that it expects for 3DES is 24 bytes ; if you happen to enter just 15 bytes like this (112233445566778899aabbccddeeff) it will be like if you entered (112233445566778899aabbccddeeff00)
step2
in the case of tripledes the algorithm to expand the 16 bytes to 24 bytes key (which is the key length required by the algorithm) this site has a simple approach to do that
it copies the first 8 bytes and append it to the end of the key like this
key =key + key.substring(0,8);
and that is the key that is going to be given to the 3DES encryption function to work with
this simple approache is not used by openssl for example , open ssl uses the first 8 bytes of the MD5 of the key ,and append them to the 16 bytes of the original key to get the 24 bytes key that is required by 3DES, like this
key = key + (MD5(key)).substring(0,8);
Summary
in that tool if you enter the key 112233445566778899AABBCCDDEEFF is the same as if you entered 112233445566778899AABBCCDDEEFF00 and same as if you entered 112233445566778899AABBCCDDEEFF001122334455667788 so to solve your problem you should give your function the complete 24 bytes of key that you gave to that site and you will surely get the same results, beacause nodejs is probably is doing the same thing as openssl does to expand the key(uses md5)
PS
if you are using the cbc mode which is your case try to specify the IV to be 8 bytes of \x00 like this "0000000000000000"
the results will be the same !!
here is a working implementation of your code you can check it in the site
const crypto = require('crypto');
function encrypt (inputkey, keyformat, password, passwordformat) {
let shortkey = Buffer.from(inputkey, keyformat);
let key = Buffer.alloc(24);
key.fill('\0');
for (i = 0; i < shortkey.length; i++) {
key[i] = shortkey[i];
}
let IV = Buffer.alloc(8);
var expansionStart = shortkey.length>16?shortkey.length:16;
for (i=expansionStart;i<24;i++){
key[i]=key[i-expansionStart];
}
console.log(key);
const cipher = crypto.createCipheriv('des-ede3-cbc', key, IV);
password = Buffer.from(password, passwordformat);
let encryptedArr = [cipher.update(password)];
encryptedArr.push(cipher.final());
encrypted = Buffer.concat(encryptedArr);
return encrypted;
}
var enc = encrypt("112233445566778899AABBCCDDEEFF","hex","password","utf8");
console.log(enc);

Decoding and encoding in Base64 in java

I am getting a Base64 decoded String from out side network, i ll convert the string to byte and decode it. then it will change to ASCII format. I ll store this ASCII in string send to another server.
In this case when i again encode the ASCII i am not getting the same value what i have received from out side network. But it works fine in unix system, problem is only in windows.
// Base64 Encode
String orig = "mj5ok9qWt2v6fg3kElm8vZGeg9ZV0BqE1u2sYUQDEm8/heIdCH4ZRf7mcEcGkb8y3I24peAUBHdli8GP/MCZR/PG4NAAzd+AU3uEVM3RKTFnYwGslKQTfKgXzg+K+wkMY/0fexPkDrgVHi0vR7VXzcyx200iJYTYjJZLCwahZ7E2Cp7LW14YpCAYg8vFCp0XSZCe1luRNgq+q9xVQ88tDamAB5nGCxYZcx7X4D49HQR5vUEzIkJu2XenGQygXsGWICKv0UrVq72um0nRf0uJUa/jdMVXWtyeAsKTaVw8KTW5u745d+r7H3Fzcsl9UwL0kBfHv4WMQwz1dQ+MommXmA==";
byte [] decodedvalue = Base64.decodeBase64(orig.getBytes());
byte [] encodedvalue = Base64.encodeBase64(decodedvalue);
System.out.println("ORIGINAL VALUE : "+orig);
System.out.println("ENCODED VALUE : "+new String(encodedvalue));
String aa =new String(decodedvalue);
String temp = new String(Base64.encodeBase64(aa.getBytes()));
System.out.println(" String encoded value : " +temp);
ORIGINAL VALUE : mj5ok9qWt2v6fg3kElm8vZGeg9ZV0BqE1u2sYUQDEm8/heIdCH4ZRf7mcEcGkb8y3I24peAUBHdli8GP/MCZR/PG4NAAzd+AU3uEVM3RKTFnYwGslKQTfKgXzg+K+wkMY/0fexPkDrgVHi0vR7VXzcyx200iJYTYjJZLCwahZ7E2Cp7LW14YpCAYg8vFCp0XSZCe1luRNgq+q9xVQ88tDamAB5nGCxYZcx7X4D49HQR5vUEzIkJu2XenGQygXsGWICKv0UrVq72um0nRf0uJUa/jdMVXWtyeAsKTaVw8KTW5u745d+r7H3Fzcsl9UwL0kBfHv4WMQwz1dQ+MommXmA==
ENCODED VALUE : mj5ok9qWt2v6fg3kElm8vZGeg9ZV0BqE1u2sYUQDEm8/heIdCH4ZRf7mcEcGkb8y3I24peAUBHdli8GP/MCZR/PG4NAAzd+AU3uEVM3RKTFnYwGslKQTfKgXzg+K+wkMY/0fexPkDrgVHi0vR7VXzcyx200iJYTYjJZLCwahZ7E2Cp7LW14YpCAYg8vFCp0XSZCe1luRNgq+q9xVQ88tDamAB5nGCxYZcx7X4D49HQR5vUEzIkJu2XenGQygXsGWICKv0UrVq72um0nRf0uJUa/jdMVXWtyeAsKTaVw8KTW5u745d+r7H3Fzcsl9UwL0kBfHv4WMQwz1dQ+MommXmA==
String encoded value : mj5ok9qWt2v6fg3kElm8vZGeg9ZV0BqE1u2sYUQDEm8/heIdCH4ZRf7mcEcGkb8y3D+4peAUBHdli8E//MCZR/PG4NAAzd+AU3uEVM3RKTFnYwGslKQTfKgXzg+K+wkMY/0fexPkDrgVHi0vR7VXzcyx200iJYTYjJZLCwahZ7E2Cp7LW14YpCAYg8vFCj8XST+e1luRNgq+q9xVQ88tDamAB5nGCxYZcx7X4D49HQR5vUEzIkJu2XenGQygXsGWICKv0UrVq72um0nRf0uJUa/jdMVXWtyeAsKTaVw8KTW5u745d+r7H3Fzcsl9UwL0PxfHv4WMQwz1dQ+MommXmA==

Resources