I am developing an app in C# .NET. The app will not connect to any database and during the installation it will create files and settings for the application and user. I want to store user data in a local file such as text file or flat file or JSON file.
In simple words I want to prevent user opening the file but if somehow user find a way to open it then at least he should not understand what information is stored. Is there any good method to encrypt file and data in it?
Just like how Google chrome stores data:
1 ŒA û œA àû ¯A ü ÂA °ü ÒA ý åA Pý õA À% B & B p& (B °m <B n OB ðn bB Po uB °o ˆB q ›B Àq ®B r ÁB €r ÔB s çB pt úB °u
C #v C v 3C x FC Àx YC z lC P| C °| ’C ð} ¥C P~ ¸C 0 ËC ÞC € ñC € D D ‚ *D °ƒ =D „ ND „
You can try encrypting the file and decrypt it when you need to access it.
Encryption
Decryption
Example of encryption and decryption from MS Docs:
using System.IO;
using System.Security.AccessControl;
namespace FileSystemExample
{
class FileExample
{
public static void Main()
{
try
{
string FileName = "test.xml";
Console.WriteLine("Encrypt " + FileName);
// Encrypt the file.
AddEncryption(FileName);
Console.WriteLine("Decrypt " + FileName);
// Decrypt the file.
RemoveEncryption(FileName);
Console.WriteLine("Done");
}
catch (Exception e)
{
Console.WriteLine(e);
}
Console.ReadLine();
}
// Encrypt a file.
public static void AddEncryption(string FileName)
{
File.Encrypt(FileName);
}
// Decrypt a file.
public static void RemoveEncryption(string FileName)
{
File.Decrypt(FileName);
}
}
}
I wrote a very simple text encryptor that you can use. Simply encrypt the text before storing it and decrypt it as you need it.
This will not stop someone who is willing to spend a little bit of time with a debugger, but just like locking your door it will keep honest people honest.
I also included some unit tests, feel free to remove them.
using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using NUnit.Framework;
namespace Encryption;
public static class SimpleEncryptor
{
public static async Task<string> EncryptAsync(string cleartext, string password)
{
var hasher = SHA256.Create();
var key = hasher.ComputeHash(Encoding.UTF8.GetBytes(password));
using var aes = Aes.Create();
aes.Key = key;
aes.Padding = PaddingMode.PKCS7;
var iv = aes.IV;
var byteStream = new MemoryStream(10000);
byteStream.Write(iv, 0, iv.Length);
using var cryptoStream = new CryptoStream(
byteStream,
aes.CreateEncryptor(),
CryptoStreamMode.Write);
var encryptWriter = new StreamWriter(cryptoStream);
await encryptWriter.WriteAsync(cleartext);
encryptWriter.Close();
var bytes = byteStream.ToArray();
var base64 = Convert.ToBase64String(bytes);
return base64;
}
public static async Task<string> DecryptAsync(string ciphertext, string password)
{
var hasher = SHA256.Create();
var key = hasher.ComputeHash(Encoding.UTF8.GetBytes(password));
var encryptedArray = Convert.FromBase64String(ciphertext);
var byteStream = new MemoryStream(encryptedArray);
using var aes = Aes.Create();
aes.Key = key;
aes.Padding = PaddingMode.PKCS7;
var iv = new byte[aes.IV.Length];
var numBytesToRead = aes.IV.Length;
var numBytesRead = 0;
while (numBytesToRead > 0)
{
var n = byteStream.Read(iv, numBytesRead, numBytesToRead);
if (n == 0) break;
numBytesRead += n;
numBytesToRead -= n;
}
using var cryptoStream = new CryptoStream(
byteStream,
aes.CreateDecryptor(key, iv),
CryptoStreamMode.Read);
var decryptReader = new StreamReader(cryptoStream);
var decryptedMessage = await decryptReader.ReadToEndAsync();
return decryptedMessage;
}
}
public class EncryptorTests
{
[Test]
[TestCase("How do you turn this on?", "Swordfish", "GHzrU6z5hsgb6HSJtMZyirEs11sHY/X4l5zElwxHz9jpIGA+D9TAxv7SEU31/Jgb")]
[TestCase("Orange you glad I didn't say banana?", "hunter12", "qqNFxhwKYkkYzsN0vDzWhQguZ7f9xc+60duZXQATAzQslRhJsn6lc691+yVR0SWJYDJUD9ZbezpW/v4vYi6qeA==")]
[TestCase("Orange you glad I didn't say banana?", "hunter12", "TsjCbMOT4UKVi6L43Kkc0rMsl6IyeEfLBR3ruAsG+APUjb1zesVLGA/B0yF4FkFV/j1Rc5B55ClZYHV2zoubBA==")]
[TestCase("Your mother is rather fat.", "12345", "WD8e5E+PtQ5kMqkPSIZa18pDutbqn8OroSU5utHFTuikbgIWLA4IRAHihrfiXrV6")]
[TestCase("Yer' a wizard harry!", "Swordfish", "Z6tF/3iDTu72qTeVnKa8DZOsL5NFD9XfqJTWebANVrjQysm+8ps3Z9RuoJyenk30")]
public async Task TestDecryption(string text, string password, string ciphertext)
{
var decoded = await SimpleEncryptor.DecryptAsync(ciphertext, password);
Assert.AreEqual(text, decoded);
}
[Test]
[TestCase("How do you turn this on?", "Swordfish")]
[TestCase("Orange you glad I didn't say banana?", "hunter12")]
[TestCase("Your mother is rather fat.", "12345")]
[TestCase("Yer' a wizard harry!", "Swordfish")]
public async Task TestEncryptAndDecrypt(string text, string password)
{
var ciphertext = await SimpleEncryptor.EncryptAsync(text, password);
var decodedtext = await SimpleEncryptor.DecryptAsync(ciphertext, password);
Assert.AreEqual(text, decodedtext);
}
}
Related
I would like to limit the length of the encrypted output code like 8 or 10 or 12 character etc.
I have created the very small encrypted coed using he "Advanced Encryption Standard (AES)" with Cryptography.SymmetricAlgorithm.IV.
But the result of the Encrypted code as example below:
Input Password = "090400551"
Converted Output = "mkopj3WFb6RZMp34urFLew==" // This should be half the length
I want to reduce the length of 8 to 12 character. Any C# cryptography library or algorithm would be fine
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
namespace AnotherEncryption
{
class Encryption
{
public static class Global
{
// set password
public const string strPassword = "090400551";
public const String strPermutation = "Secure1234";
public const Int32 bytePermutation1 = 0x78;
public const Int32 bytePermutation2 = 0x56;
public const Int32 bytePermutation3 = 0x34;
public const Int32 bytePermutation4 = 0x88;
}
static void Main(string[] args)
{
Console.Title = "Secure Password v2";
Console.WriteLine("Output---");
Console.WriteLine("");
Console.WriteLine("Password: " + Global.strPassword);
string strEncrypted = (Encrypt(Global.strPassword));
Console.WriteLine("Encrypted: " + strEncrypted);
string strDecrypted = Decrypt(strEncrypted);
Console.WriteLine("Decrypted: " + strDecrypted);
//mkopj3WFb6RZMp34urFLew==
Console.ReadKey();
}
public static string Encrypt(string strData)
{
byte[] test = Encoding.UTF8.GetBytes(strData);
return Convert.ToBase64String(Encrypt(test));
}
public static string Decrypt(string strData)
{
return Encoding.UTF8.GetString(Decrypt(Convert.FromBase64String(strData)));
}
// encrypt
public static byte[] Encrypt(byte[] strData)
{
PasswordDeriveBytes passbytes =
new PasswordDeriveBytes(Global.strPermutation,
new byte[] { Global.bytePermutation1,
Global.bytePermutation2,
Global.bytePermutation3,
Global.bytePermutation4
});
MemoryStream memstream = new MemoryStream();
Aes aes = new AesManaged();
aes.Key = passbytes.GetBytes(aes.KeySize / 8);
aes.IV = passbytes.GetBytes(aes.BlockSize / 8);
CryptoStream cryptostream = new CryptoStream(memstream, aes.CreateEncryptor(), CryptoStreamMode.Write);
cryptostream.Write(strData, 0, strData.Length);
cryptostream.Close();
return memstream.ToArray();
}
// decrypt
public static byte[] Decrypt(byte[] strData)
{
PasswordDeriveBytes passbytes =
new PasswordDeriveBytes(Global.strPermutation,
new byte[] { Global.bytePermutation1,
Global.bytePermutation2,
Global.bytePermutation3,
Global.bytePermutation4
});
MemoryStream memstream = new MemoryStream();
Aes aes = new AesManaged();
aes.Key = passbytes.GetBytes(aes.KeySize / 8);
aes.IV = passbytes.GetBytes(aes.BlockSize / 8);
CryptoStream cryptostream = new CryptoStream(memstream,
aes.CreateDecryptor(), CryptoStreamMode.Write);
cryptostream.Write(strData, 0, strData.Length);
cryptostream.Close();
return memstream.ToArray();
}
}
}
If you put Rijndael into CFB mode with a block size of 8, then it acts as a stream cipher - for every byte you put in, you get a byte out again.
public static void Main(string[] args)
{
var algorithm = new RijndaelManaged()
{
Mode = CipherMode.CFB,
// This is the equivalent of BlockSize in CFB mode. We set it to 8 (bits) to prevent any buffering of data
// while waiting for whole blocks.
FeedbackSize = 8,
};
// Don't hard-code in real life, obviously
var key = new byte[32];
var iv = new byte[16];
var input = new byte[] { 1, 2, 3 };
byte[] result;
using (var ms = new MemoryStream())
{
using (var cryptoStream = new CryptoStream(ms, algorithm.CreateEncryptor(key, iv), CryptoStreamMode.Write))
{
cryptoStream.Write(input, 0, input.Length);
}
result = ms.ToArray();
}
}
Note that this only appears to work on .NET Framework - .NET Core doesn't seem to support CFB (see this GitHub issue).
Note that encryption doesn't prevent tampering! People can't read your plaintext message, but they can very easily change the ciphertext to control what it gets decrypted to. Stream ciphers tend to be particularly vulnerable to this. If you need to stop someone controlling what the encrypted output decrypts into, then you need a signature.
Also note that you should not use the same IV across multiple messages. Create a random IV, and transfer it alongside your message, frequently as the first 2 bytes.
I need to correct an encryption method written in C#.
First, a little background: I am taking charge of an existing web application with an ecma6 / html frontend and c# web api .net standard 4.6 backend.
It has many integrations with different customers for user identification. Some of the integrations simply navigate to a customers URL to do a login process on the infrastructure of the customer, and then return to the app with an encripted user token in the URL's query string.
This token is encripted using AES256 encryption.
The backend is correctly decrypting the tokens, but when I tried to use the encryption routine to build a unit test, I discovered something is wrong. When I encrypt and then decrypt a message, the decryption routine throws the following error:
Unhandled Exception:
System.Security.Cryptography.CryptographicException: Length of the data to decrypt is invalid.
Input message is "key1=value1;key2=value2" (without the quotes)
Encrypted message I get is NzcrOTc3Kzk3Nys5NzcrOTc3Kzk3Nys5NzcrOVpsVHZ2NzF3NzcrOUZ6UVlRZ3Z2djcxSVlPKy92U0V6NzcrOVNqZFY3Nys5VHpBZA==
I need to correct the implementation error in the encryption method. The implementation of the decryption method shows expected behavior, and you'll notice a double Base64 decoding done on the encrypted string: this is given, as we are integrated with an already developed encryption routine done by a customer in PERL which we detected did double encoding.
I inspected the order of operations to see a mismatch among the encryption and decryption and I was unable to detect an inconsistency, so the need to ask for help.
The code I synthesized for this test is:
using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;
class MainClass {
public static void Main (string[] args) {
var secretKey = "This is my secret key";
var secretIV = "This is my secret iv";
var originalMessage = "key1=value1;key2=value2";
var userToken = Cryptography.EncryptAES256CBCBase64x2(originalMessage, secretKey, secretIV);
Console.WriteLine(userToken);
var unencryptedToken = Cryptography.DecryptAES256CBCBase64x2(userToken, secretKey, secretIV);
if (originalMessage == unencryptedToken)
Console.WriteLine("All fine!");
else
Console.WriteLine("Error!");
}
}
public static class Cryptography
{
public static string DecryptAES256CBCBase64x2(string base64EncryptedString, string secretKey, string secretIV)
{
base64EncryptedString = SaveBase64String(base64EncryptedString);
var keyBytes = Encoding.UTF8.GetBytes(secretKey);
var ivBytes = Encoding.UTF8.GetBytes(secretIV);
var hash = SHA256.Create();
var keyHash = hash.ComputeHash(keyBytes);
Array.Resize(ref keyHash, 32);
var keyHashString = string.Empty;
foreach (byte x in keyHash)
keyHashString += string.Format("{0:x2}", x);
keyHash = Encoding.UTF8.GetBytes(keyHashString.Substring(0, 32));
var ivHash = hash.ComputeHash(ivBytes);
Array.Resize(ref ivHash, 16);
var ivHashString = string.Empty;
foreach (byte x in ivHash)
ivHashString += string.Format("{0:x2}", x);
ivHash = Encoding.UTF8.GetBytes(ivHashString.Substring(0, 16));
// Create an RijndaelManaged object
// with the specified key and IV.
using (var rijAlg = new RijndaelManaged())
{
rijAlg.Padding = PaddingMode.PKCS7;
rijAlg.Mode = CipherMode.CBC;
rijAlg.Key = keyHash;
rijAlg.IV = ivHash;
var encryptedBytes =
Convert.FromBase64String(
Encoding.UTF8.GetString(
Convert.FromBase64String(base64EncryptedString)));
// Create a decryptor to perform the stream transform.
var decryptor = rijAlg.CreateDecryptor(rijAlg.Key, rijAlg.IV);
// Create the streams used for decryption.
using (var msDecrypt = new MemoryStream(encryptedBytes))
{
using (var csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
{
using (var srDecrypt = new StreamReader(csDecrypt))
{
// Read the decrypted bytes from the decrypting stream
// and place them in a string.
return srDecrypt.ReadToEnd();
}
}
}
}
}
public static string EncryptAES256CBCBase64x2(string baseString, string secretKey, string secretIV)
{
var keyBytes = Encoding.UTF8.GetBytes(secretKey);
var ivBytes = Encoding.UTF8.GetBytes(secretIV);
var hash = SHA256.Create();
var keyHash = hash.ComputeHash(keyBytes);
Array.Resize(ref keyHash, 32);
var keyHashString = string.Empty;
foreach (byte x in keyHash)
keyHashString += string.Format("{0:x2}", x);
keyHash = Encoding.UTF8.GetBytes(keyHashString.Substring(0, 32));
var ivHash = hash.ComputeHash(ivBytes);
Array.Resize(ref ivHash, 16);
var ivHashString = string.Empty;
foreach (byte x in ivHash)
ivHashString += string.Format("{0:x2}", x);
ivHash = Encoding.UTF8.GetBytes(ivHashString.Substring(0, 16));
// Create an RijndaelManaged object
// with the specified key and IV.
using (var rijAlg = new RijndaelManaged())
{
rijAlg.Padding = PaddingMode.PKCS7;
rijAlg.Mode = CipherMode.CBC;
rijAlg.Key = keyHash;
rijAlg.IV = ivHash;
var encryptedBytes = Encoding.UTF8.GetBytes(baseString);
// Create a encryptor to perform the stream transform.
var encryptor = rijAlg.CreateEncryptor(rijAlg.Key, rijAlg.IV);
// Create the streams used for encryption.
using (var msEncrypt = new MemoryStream(encryptedBytes))
{
using (var csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Read))
{
using (var srEncrypt = new StreamReader(csEncrypt))
{
// Read the encrypted bytes from the encrypting stream
// and place them in a string.
var result = srEncrypt.ReadToEnd();
return Convert.ToBase64String(
Encoding.UTF8.GetBytes(
Convert.ToBase64String(
Encoding.UTF8.GetBytes(result))));
}
}
}
}
}
public static string SaveBase64String(string data)
{
data = data.Replace("-", "+").Replace("_", "/");
var mod = data.Length % 4;
if (mod > 2)
mod = 1;
return data + string.Empty.PadRight(mod, '=');
}
}
At the following link an online example is available for you to try: https://repl.it/#ormasoftchile/Test-encrypt-decrypt
Thank you everyone.
In the current code, the ciphertext is stored in a string (StreamReader.ReadToEnd), which generally doesn't work, since the data are corrupted thereby. Instead, the ciphertext should be stored in a byte-array, which can be Base64-encoded if required.
To fix the problem
remove the line:
var encryptedBytes = Encoding.UTF8.GetBytes(baseString);
and replace the entire MemoryStream-block by:
using (var msEncrypt = new MemoryStream())
{
using (var csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
{
using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
{
swEncrypt.Write(baseString);
}
var encryptedBytes = msEncrypt.ToArray();
return Convert.ToBase64String(Encoding.UTF8.GetBytes(Convert.ToBase64String(encryptedBytes)));
}
}
Another point is the double Base64-encoding/decoding. This makes no sense and is simply redundant and degrades performance. If possible, this should be changed.
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 have to request data from an external existing webservice written in C#.
This webservice requires some of the data to be encrypted (The connection uses an SSL connection, some of the data is aes encrypted)
On the php site openssl is used for decrypting.
The following settings are used on the c# site
(This are the default values for the AesCryptoServiceProvider):
Algorithm: AES
Padding: PKCS7
Mode: CBC
Keysize: 256
The padding for PKCS7 works as following:
01 If 1 byte is missing
02 02 If 2 bytes are missing
and so on
so this values are not added by the padding.
What am I doing wrong?
I've checked this with c#, php and ruby - the decrypted data starts with 255, 254
To reproduce use the following parameters:
data:1234567890123456
key: First1
salt(iv):Data
using System;
using System.Security.Cryptography;
using System.Text;
using System.IO;
namespace crypto_test
{
class MainClass
{
public static void Main(string[] args)
{
bool running = true;
while (running)
{
Console.WriteLine("Enter data:");
var data = Console.ReadLine();
Console.WriteLine("Enter key:");
var key = Console.ReadLine();
Console.WriteLine("Enter iv:");
var iv = Console.ReadLine();
Console.WriteLine("Enter d for decode");
var decode = (Console.ReadLine() == "d");
string encoded=Crypt(data, key, iv, decode);
Console.WriteLine(encoded);
if (!decode)
{
encoded= Crypt(encoded, key, iv, true);
Console.WriteLine(encoded);
}
Console.WriteLine("quit to exit");
running = !(Console.ReadLine() == "quit");
}
}
public static string Crypt(string value, string password, string salt, bool decrypt)
{
DeriveBytes rgb = new Rfc2898DeriveBytes(password, Encoding.Unicode.GetBytes(salt));
SymmetricAlgorithm algorithm = new AesCryptoServiceProvider();
byte[] rgbKey = rgb.GetBytes(algorithm.KeySize >> 3);
byte[] rgbIV = rgb.GetBytes(algorithm.BlockSize >> 3);
Console.WriteLine("rbKey: size:{0} key:{1}", (algorithm.KeySize >> 3), GetHex(rgbKey));
Console.WriteLine("rgbIV: size:{0} key:{1}", (algorithm.BlockSize >> 3), GetHex(rgbIV));
ICryptoTransform transform = decrypt ? algorithm.CreateDecryptor(rgbKey, rgbIV) : algorithm.CreateEncryptor(rgbKey, rgbIV);
Console.WriteLine("Mode {0}", algorithm.Mode);
Console.WriteLine("PAdding {0}", algorithm.Padding);
using (MemoryStream buffer = new MemoryStream())
{
using (CryptoStream stream = new CryptoStream(buffer, transform, CryptoStreamMode.Write))
{
try
{
if (decrypt)
{
byte[] data = Convert.FromBase64String(value);
stream.Write(data,0,data.Length);
}
else
{
using (StreamWriter writer = new StreamWriter(stream, Encoding.Unicode))
{
writer.Write(value);
}
}
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
byte[] buff = buffer.ToArray();
if (decrypt)
{
return Encoding.Unicode.GetString(buff) + "\r\n" + GetHex(buff);
}
else
return Convert.ToBase64String(buff);
}
}
public static string GetHex(byte[] data)
{
StringBuilder sb = new StringBuilder();
for (int i = 0; i < data.Length; ++i)
sb.Append(data[i].ToString("X2"));
return sb.ToString();
}
}
}
I have not found an equivalent to Rfc2898DeriveBytes until now,
so I copied the key and iv
php
<?php
$salt='Data';
$pass='First1';
$data='1234567890123456';
$encrypted_base64='VKNd9Pi+cttaM6ne8pzAuFbH1U0gJiJ2Wlbbr1rU5z8vbIfAS6nb0/5py4p54aK7';
$encrypted=base64_decode($encrypted_base64);
$key = pack('H*', "30EE7F95F0EF4835F048A481424F2F52EE21B7CEB97F8CC437E5949DB53797D9");
$iv = pack('H*', "B29F5ECF7057065758102385509F0637");
$cipher='AES-256-CBC';
$decrypted = openssl_decrypt($encrypted,$cipher, $key,true,$iv);
for($i =0; $i<strlen($decrypted);++$i)
{
echo "char:" . ord($decrypted[$i])."<br/>";
}
echo $decrypted
?>
ruby:
require ('openssl')
require ('base64')
while true
enc_data='VKNd9Pi+cttaM6ne8pzAuFbH1U0gJiJ2Wlbbr1rU5z8vbIfAS6nb0/5py4p54aK7'
data = Base64.decode64(enc_data)
key_hex='30EE7F95F0EF4835F048A481424F2F52EE21B7CEB97F8CC437E5949DB53797D9'
iv_hex='B29F5ECF7057065758102385509F0637'
key = [key_hex].pack('H*')
iv = [iv_hex].pack('H*')
decipher = OpenSSL::Cipher::AES.new(256, :CBC)
decipher.decrypt
decipher.key = key
decipher.iv = iv
plain = decipher.update(data) + decipher.final
puts plain
puts plain.bytes
end
Good news, your decryption seems to work OK.
What you are seeing in the decrypted ciphertext is the byte order mark for UTF-16 LE, which is (incorrectly) indicated by Microsoft as Encoding.Unicode. You need to do either one off two things:
decode the text with a decoder that groks UTF-16 LE including byte order mark;
encode using much more reasonable UTF-8 encoding (in the C# code).
Personally I would put a strong preference on (2).
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();
}