SHA512HMAC in node.js does not match C# - javascript

I have a simple node.js script
var text = "Hello!";
var serverSeed = "d8818b38a14e7461e87301ad4b9809b558bcbca816b650cd470452e018ada255";
var crypto = require('crypto');
var hash = crypto.createHmac('sha512', serverSeed).update(text).digest('hex');
console.log(hash);
I also have the C# program
using System;
using System.Text;
using System.Security.Cryptography;
public class Program
{
public static byte[] StringToByteArray(String hex)
{
int NumberChars = hex.Length;
byte[] bytes = new byte[NumberChars / 2];
for (int i = 0; i < NumberChars; i += 2)
bytes[i / 2] = Convert.ToByte(hex.Substring(i, 2), 16);
return bytes;
}
public static string ByteArrayToString(byte[] ba)
{
StringBuilder hex = new StringBuilder(ba.Length * 2);
foreach (byte b in ba)
hex.AppendFormat("{0:x2}", b);
return hex.ToString();
}
public static void Main()
{
var serverSeed = StringToByteArray("d8818b38a14e7461e87301ad4b9809b558bcbca816b650cd470452e018ada255");
using (var sha = new HMACSHA512(serverSeed))
{
var hash = sha.ComputeHash(Encoding.ASCII.GetBytes("Hello!"));
Console.WriteLine(ByteArrayToString(hash));
}
}
}
(runable version here)
I get from the node.js program
99e3b20acaa9c7674f074da950945ee897876b0afc02121d5a89fa581081465f3e01a084e9b05bed729b7fbdc1d485fb38af7d6f501cbc258b6c66add54410ba
And from the C# program
73250817a927f394b0912afcece47b8c12aeaed31892c64116ae9dd0d407f6e31d5c062d65f68a3cae09a8acb14a7cef1f6afd99f5a22f2b73e46a991fcd079a
What am I doing wrong to cause this difference?

Your C# code is converting the hex characters in your seed to a byte array based on the what the characters represent in hexadecimal format.
But your node code is passing the seed as a string which is converting the characters to a byte.
So for example, your C# code is converting a to a byte with the value of 10 but your node code would be converting a to a byte with the value of 97
Your node code either needs to convert the hex in your string to a Buffer based on their hex values as you are doing in C#.
var text = "Hello!";
var serverSeed = "d8818b38a14e7461e87301ad4b9809b558bcbca816b650cd470452e018ada255";
var crypto = require('crypto');
var buff = new Buffer(seed, "hex")
var hash = crypto.createHmac('sha512', buff).update(text).digest('hex');
console.log(hash);
Or in C# instead of converting hex to bytes you can get a byte array representing the actually characters the seed using GetBytes from a System.Text.Encoding instance.
var serverSeed = Encoding.ASCII.GetBytes("d8818b38a14e7461e87301ad4b9809b558bcbca816b650cd470452e018ada255");
using (var sha = new HMACSHA512(serverSeed))
{
var hash = sha.ComputeHash(Encoding.ASCII.GetBytes("Hello!"));
Console.WriteLine(ByteArrayToString(hash));
}
Most likely you intended to pass the serverSeed as a buffer in the node.js code.

Related

AES Encryption with C# Decryption with crypto-js

I'm triying to Encrypt string with C# and decrypt it using Angular crypto-js library but it's giving me different output.
I tried different c# aes encryption implementations but crypto-js library can't decrypt the encrypted data in c#. Thank you for any help.
Here is my code
Program.cs
static void Main()
{
var r = EncryptString("exampleString", "examplePassword");
Console.Write(r);
}
public static string EncryptString(string plainText, string passPhrase)
{
if (string.IsNullOrEmpty(plainText))
{
return "";
}
// generate salt
byte[] key, iv;
var salt = new byte[8];
var rng = new RNGCryptoServiceProvider();
rng.GetNonZeroBytes(salt);
DeriveKeyAndIv(passPhrase, salt, out key, out iv);
// encrypt bytes
var encryptedBytes = EncryptStringToBytesAes(plainText, key, iv);
// add salt as first 8 bytes
var encryptedBytesWithSalt = new byte[salt.Length + encryptedBytes.Length + 8];
Buffer.BlockCopy(Encoding.ASCII.GetBytes("Salted__"), 0, encryptedBytesWithSalt, 0, 8);
Buffer.BlockCopy(salt, 0, encryptedBytesWithSalt, 8, salt.Length);
Buffer.BlockCopy(encryptedBytes, 0, encryptedBytesWithSalt, salt.Length + 8, encryptedBytes.Length);
// base64 encode
return Convert.ToBase64String(encryptedBytesWithSalt);
}
private static void DeriveKeyAndIv(string passPhrase, byte[] salt, out byte[] key, out byte[] iv)
{
// generate key and iv
var concatenatedHashes = new List<byte>(48);
var password = Encoding.UTF8.GetBytes(passPhrase);
var currentHash = new byte[0];
var md5 = MD5.Create();
bool enoughBytesForKey = false;
// See http://www.openssl.org/docs/crypto/EVP_BytesToKey.html#KEY_DERIVATION_ALGORITHM
while (!enoughBytesForKey)
{
var preHashLength = currentHash.Length + password.Length + salt.Length;
var preHash = new byte[preHashLength];
Buffer.BlockCopy(currentHash, 0, preHash, 0, currentHash.Length);
Buffer.BlockCopy(password, 0, preHash, currentHash.Length, password.Length);
Buffer.BlockCopy(salt, 0, preHash, currentHash.Length + password.Length, salt.Length);
currentHash = md5.ComputeHash(preHash);
concatenatedHashes.AddRange(currentHash);
if (concatenatedHashes.Count >= 48)
enoughBytesForKey = true;
}
key = new byte[32];
iv = new byte[16];
concatenatedHashes.CopyTo(0, key, 0, 32);
concatenatedHashes.CopyTo(32, iv, 0, 16);
md5.Clear();
}
static byte[] EncryptStringToBytesAes(string plainText, byte[] key, byte[] iv)
{
// Check arguments.
if (plainText == null || plainText.Length <= 0)
throw new ArgumentNullException("plainText");
if (key == null || key.Length <= 0)
throw new ArgumentNullException("key");
if (iv == null || iv.Length <= 0)
throw new ArgumentNullException("iv");
// Declare the stream used to encrypt to an in memory
// array of bytes.
MemoryStream msEncrypt;
// Declare the RijndaelManaged object
// used to encrypt the data.
RijndaelManaged aesAlg = null;
try
{
// Create a RijndaelManaged object
// with the specified key and IV.
aesAlg = new RijndaelManaged { Mode = CipherMode.CBC, KeySize = 256, BlockSize = 128, Key = key, IV = iv };
// Create an encryptor to perform the stream transform.
ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);
// Create the streams used for encryption.
msEncrypt = new MemoryStream();
using (var csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
{
using (var swEncrypt = new StreamWriter(csEncrypt))
{
//Write all data to the stream.
swEncrypt.Write(plainText);
swEncrypt.Flush();
swEncrypt.Close();
}
}
}
finally
{
// Clear the RijndaelManaged object.
aesAlg?.Clear();
}
// Return the encrypted bytes from the memory stream.
return msEncrypt.ToArray();
}
Simply decrypting it using crypto-js
let CryptoJS = require('crypto-js');
let r = CryptoJS.AES.decrypt('exampleString', 'examplePassword').toString();
The example code is attempting to decrypt the original unencrypted string, which looks to be a mistake perhaps created when trying to simplify the example code for posting the question? Either way the steps required are not too difficult, but the toString() call needs to be replaced.
var data = "U2FsdGVkX1/Zvh/5BnLfUgfbg5ROSD7Aohumr9asPM8="; // Output from C#
let r2 = CryptoJS.enc.Utf8.stringify(CryptoJS.AES.decrypt(data, 'examplePassword'));
console.log(r2);

Match Javascript RSA Encryption in C#

I am trying to convert one JavaScript encryption function in C#. But not getting the valid output. Can someone help me with this issue.
Below is my JavaScript function for encryption
function encryptPassword()
{
var password="samplepassword"; mod="ce33b5600160ba9354a0e56ee3e43369e9a824e0f88e20ad52bbde015cc8c8fed57aa2d65c4eb13dc099f38718fcbc471e60bb4d82a5e05b6a64c5dc8d2e17ca44c205a6c54540d4884285808e829043b2f11838dc5d603750616bf5fd711d810b50f7a468e31d0cfc5b70e6a72fe887a2e26db44c3868a86febb5e4c5f6c27000b202a0460488db3eac48f1ee80e7ca6bb8a9af2b985c28aa25ecdc74e485abd20311c21add507e8c65c3d11d743ebbd76c44933bb68981e1171376d1ddf33607fa2116cb747f0c3f238bae5b01878004157cfef9c8cdd697ee39759625f84abd89469a8b8cd9eabb2e79fd9269ab4e62670d6ec8b5b1f6d986385b2394bfdb";
var rsa = new RSAKey();
rsa.setPublic(mod, "10001");
var res = rsa.encrypt(password);
if(res) {
document.getElementById('password').value=res;
return true;
}
else
{
return false;
}
}
Below is my C# implementation for the same. But somehow I am not getting the proper output. C# generated output is not validated at server side.
C# code
public static string EncryptRSAx(string public_keyHex, uint exp, string data)
{
byte[] bytes = new byte[public_keyHex.Length / 2];
for (int i = 0; i < public_keyHex.Length - 1; i += 2)
{
bytes[i / 2] = byte.Parse(public_keyHex.Substring(i, 2), System.Globalization.NumberStyles.HexNumber);
}
string public_key = Convert.ToBase64String(bytes);
return RSAEncrypt(public_key, Convert.ToBase64String(BitConverter.GetBytes(exp)), data);
}
public static string RSAEncrypt(string public_key, string exponent, string data)
{
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
//rsa.FromXmlString()
rsa.FromXmlString(String.Format("<RSAKeyValue><Modulus>{0}</Modulus><Exponent>{1}</Exponent></RSAKeyValue>", public_key, exponent));
byte[] plainbytes = System.Text.Encoding.UTF8.GetBytes(data);
byte[] cipherbytes = rsa.Encrypt(plainbytes,false);
return Convert.ToBase64String(cipherbytes);
}

Converting Java's PBEWithMD5AndDES to JavaScript

I'm trying to replicate the Java code in JavaScript.
below is my Java code:
public static String encrypt(String input)
final byte[] SALT= { (byte) 0x21, (byte) 0x21, (byte) 0xF0, (byte) 0x55, (byte) 0xC3, (byte) 0x9F, (byte) 0x5A, (byte) 0x75 };
final int ITERATION_COUNT = 31;
{
if (input == null)
{
throw new IllegalArgumentException();
}
try
{
KeySpec keySpec = new PBEKeySpec(null, SALT, ITERATION_COUNT);
AlgorithmParameterSpec paramSpec = new PBEParameterSpec(SALT, ITERATION_COUNT);
SecretKey key = SecretKeyFactory.getInstance("PBEWithMD5AndDES").generateSecret(keySpec);
Cipher ecipher = Cipher.getInstance(key.getAlgorithm());
ecipher.init(Cipher.ENCRYPT_MODE, key, paramSpec);
byte[] enc = ecipher.doFinal(input.getBytes());
String res = new String(Base64.encodeBase64(enc));
// escapes for url
res = res.replace('+', '-').replace('/', '_').replace("%", "%25").replace("\n", "%0A");
LOGGER.info("String Encrypted Successfully");
return res;
}
catch (Exception e)
{
LOGGER.error("encrypt Exception: "+e.getMessage());
}
return "";
}
and the JavaScript code, so far hammed up is below:
var encrypt = function(){
var iterations = 31;
var key = CryptoJS.MD5("PBEWithMD5AndDES");
var salt = CryptoJS.enc.Hex.parse('0021002100f0005500C3009F005A0075');
var options = {
mode: CryptoJS.mode.CBC,
iv: salt
};
var hashedPassword = CryptoJS.MD5($scope.data.webPassword);
var encryptedPassword = CryptoJS.DES.encrypt(hashedPassword, key,options).toString();
var result = encryptedPassword.toString(CryptoJS.enc.Base64);
}
but with both the encryption the encoded string I'm getting is different.
PBEwithMD5andDES is obsolete technology and should not be used nowadays. This answer is only provided for demonstration purposes.
PBEwithMD5andDES is defined in PKCS#5 v1.5 which is nothing more than deriving key+IV using PBKDF1 (with MD5) and encrypting with DES.
var password = CryptoJS.enc.Utf8.parse("test");
var salt = CryptoJS.enc.Hex.parse("2121F055C39F5A75");
var iterations = 31;
// PBE according to PKCS#5 v1.5 (in other words: PBKDF1)
var md5 = CryptoJS.algo.MD5.create();
md5.update(password);
md5.update(salt);
var result = md5.finalize();
md5.reset();
for(var i = 1; i < iterations; i++) {
md5.update(result);
result = md5.finalize();
md5.reset();
}
// splitting key and IV
var key = CryptoJS.lib.WordArray.create(result.words.slice(0, 2));
var iv = CryptoJS.lib.WordArray.create(result.words.slice(2, 4));
var encrypted = CryptoJS.DES.encrypt("test", key, {
iv: iv
});
enchex.innerHTML = encrypted.ciphertext.toString();
encbase64.innerHTML = encrypted.ciphertext.toString(CryptoJS.enc.Base64);
<script src="https://cdn.rawgit.com/CryptoStore/crypto-js/3.1.2/build/rollups/tripledes.js"></script>
<script src="https://cdn.rawgit.com/CryptoStore/crypto-js/3.1.2/build/rollups/md5.js"></script>
<div>Hex: <span id="enchex"></span></div>
<div>Base64: <span id="encbase64"></span></div>
Here is a jsFiddle to experiment with and here is the example Java code. Both produce the same result in Hex: aa8101a7d63093c6.
Security considerations:
PBEwithMD5andDES should not be used and there are better alternatives like PBEWithHmacSHA256AndAES_128 which require a slightly different approach.
The number of iterations must be large (a thousand to a million) in order to make it hard to brute-force the password. DES only provides 56 bits of security, so it is even possible to brute-force the key directly with today's means.
The salt must be randomly generated in order to achieve semantic security. The salt itself doesn't need to be secret. Since it has a known length it can be simply prepended to the ciphertext and sliced off before decryption.

Encrypting files with SJCL client-side

I have problem encrypting files with SJCL and javascript.
I have managed to encrypt text-files with using FileReader API and readAsBinaryString.
When it comes to encrypting pdf/png/.. then problem arrises probably due to encoding.
I found that I can use readAsArrayBuffer which suits this task perfectly, so I basically read file and create new typed array with new Uint8Array() but i dont know how exactly I am supposed to encrypt such a data.
Here's my code:
/** Convert from an array of bytes to a bitArray. */
function toBitArrayCodec(bytes) {
var out = [], i, tmp=0;
for (i=0; i<bytes.length; i++) {
tmp = tmp << 8 | bytes[i];
if ((i&3) === 3) {
out.push(tmp);
tmp = 0;
}
}
if (i&3) {
out.push(sjcl.bitArray.partial(8*(i&3), tmp));
}
return out;
}
/** Convert from a bitArray to an array of bytes. */
function fromBitArrayCodec(arr) {
var out = [], bl = sjcl.bitArray.bitLength(arr), i, tmp;
for (i=0; i<bl/8; i++) {
if ((i&3) === 0) {
tmp = arr[i/4];
}
out.push(tmp >>> 24);
tmp <<= 8;
}
return out;
}
var reader = new FileReader();
reader.readAsArrayBuffer(fileData); //filedata comes from function
reader.onload = function() {
var bytes = new Uint8Array(reader.result);
var bits = toBitArrayCodec(bytes);
var crypt = sjcl.encrypt("aaaaa", bits);
var decrypt = sjcl.decrypt("aaaaa", crypt);
var byteNumbers = fromBitArrayCodec(decrypt);
var byteArray = new Uint8Array(byteNumbers);
saveData(byteArray, 'png.png');
I am getting error on
Uncaught URIError: URI malformed sjcl.js:12sjcl.codec.utf8String.fromBits sjcl.js:12sjcl.json.decrypt sjcl.js:44reader.onload
I need to know how to encrypt uint8array or another alternative how to encrypt(pdf/png/..) files.
The plaintext in sjcl is expected to be utf8 encoded. Encrypting a manually built bitArray works, because the encryption is done on the bitArray and it doesn't have to be decoded. But at the end of the decryption is an encoding step which converts the recovered plaintext bitArray into a utf8string. This doesn't work, because it contains unprintable characters, because the source was probably binary.
The solution would be to encode it as Base64 before encrypting and convert it back after decrypting.
var bytes = new Uint8Array(reader.result);
var bits = toBitArrayCodec(bytes);
var base64bits = sjcl.codec.base64.fromBits(bits); // added
var crypt = sjcl.encrypt("aaaaa", base64bits);
var base64decrypt = sjcl.decrypt("aaaaa", crypt);
var decrypt = sjcl.codec.base64.toBits(base64decrypt); // added
var byteNumbers = fromBitArrayCodec(decrypt);
var byteArray = new Uint8Array(byteNumbers);

AES ECB No padding

I am trying to implement AES/ECB/NoPadding with cryptojs.
On the Java side I have this:
public static String encrypt(String input, String key) throws Exception {
SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes(), "AES");
Cipher cipher = Cipher.getInstance("AES/ECB/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
return DatatypeConverter.printHexBinary(cipher.doFinal(padToMultipleOf32(input.getBytes())));
}
public static byte[] padToMultipleOf32(final byte[] bytes) {
int n16 = ((bytes.length + 31) / 32);
int paddedLen = n16 * 32;
byte[] result = new byte[paddedLen];
for (int i = 0; i < bytes.length; i++) {
result[i] = bytes[i];
}
for (int i = bytes.length; i < paddedLen; i++) {
result[i] = 0x00;
}
System.out.println(new String(result).length());
return result;
}
Running Test.encrypt("test", "4g2ef21zmmmhe678")
Gives me: C24F53DDEAD357510A27AA283C74BBF4638B3F81F8EB44652D424D7C32897525
How would I do the same in cryptojs, what I have currently doesnt work:
var pwd = CryptoJS.AES.encrypt("test", "4g2ef21zmmmhe678", {
mode : CryptoJS.mode.ECB,
padding : CryptoJS.pad.NoPadding
});
expect(pwd.ciphertext.toString(CryptoJS.enc.Base64)).toEqual("C24F53DDEAD357510A27AA283C74BBF4638B3F81F8EB44652D424D7C32897525");
Please help
The documentation of CryptoJS says that when passing a plain passphrase (i.e. a String) to encrypt () , it will automatically generate a 256 bit key (Java implements the 128 bit version) using that password as the seed.
To prevent that you can use this function to directly convert your passphrase to a key:
var key = CryptoJS.enc.Utf8.parse("password");
Possibly you will also have to synchronize the encodings of the ciphertexts. See here for a detailed example on how to perform interoperable encryption with Java and CryptoJS.

Categories

Resources