I have a desktop application developed in c# language that has some games that the users play and the App will store their names, scores and other information on the client computer, currently I am using an xml file to store the info of the user, but a drawback is that the user can edit the file manually and mess with it by editing their scores or name.
I want to store the info so that the users can not manually edit it, so what file format should I use or should I encrypt the data before storing in the file.
My file is stored in the C:\Users\jobs\AppData\Local folder
tnx
If you need some basic protection against an average PC user you probably want to use something really simple, like this:
// data for example
var data = new XElement("gamedata",
new XElement("player", new XAttribute("name", "t0taln00b"),
new XElement("score",
new XAttribute("game", "bite your elbow"),
new XAttribute("score", 9000),
new XAttribute("progress", "19 %"))
)
);
// set up encryption.
// You probably will want to do this once at program startup and store Key and IV globally
var rnd = new Random(12562);
var keysize = 128;
byte[]
Key = new byte[keysize / 8],
IV = new byte[keysize / 8];
rnd.NextBytes(Key);
rnd.NextBytes(IV);
// lets encrypt
using (Aes aes = new AesManaged() { Padding = PaddingMode.PKCS7, KeySize = keysize })
{
aes.Key = Key;
aes.IV = IV;
using (Stream file = new FileStream("save.xml.aes", FileMode.Create, FileAccess.Write))
using (Stream encrypter = new CryptoStream(file, aes.CreateEncryptor(), CryptoStreamMode.Write))
data.Save(encrypter);
}
//and decrypt
using (Aes aes = new AesManaged() { Padding = PaddingMode.PKCS7, KeySize = keysize })
{
aes.Key = Key;
aes.IV = IV;
using (Stream file = new FileStream("save.xml.aes", FileMode.Open, FileAccess.Read))
using (Stream decrypter = new CryptoStream(file, aes.CreateDecryptor(), CryptoStreamMode.Read))
{
var loaded = XElement.Load(decrypter);
Console.WriteLine(loaded.ToString());
}
}
Please note that this would be an extremely weak encryption. Keys generated by System.Random cannot be strong, and a whole encryption code can be revealed with tools like .NET Reflector.
If you need really strong encryption i would suggest having something like a webserver which encrypts the file with an asymmetric encryption and keeps the private key secure. This will not absolutely prevent a hacker from reading the file, but at least will make you totally sure that he cannot modify its content.
Related
I am searching for C# Code to reproduce the following openssl command.
openssl enc -d -aes-256-cbc -in my_encrypted_file.csv.enc -out my_decrypted_file.csv -pass file:key.bin
Additional information:
The encrypted file in present as byte[]
The key.bin is a byte[] with length of 256 (the key is obtained by a more simple decryption of yet another file, which i managed to realize in C#).
I have been trying out various examples found by searching the web.
The problem is, that all of these examples require an IV (initialization vector). Unfortunately, I don't have an IV and no one on the team knows what this is or how it could be defined.
The openssl command does not seem to need one, so I am a bit confused about this.
Currently, the code, I am trying with, looks as follows:
public static string DecryptAesCbc(byte[] cipheredData, byte[] key)
{
string decrypted;
System.Security.Cryptography.Aes aes = System.Security.Cryptography.Aes.Create();
aes.KeySize = 256;
aes.Key = key;
byte[] iv = new byte[aes.BlockSize / 8];
aes.IV = iv;
aes.Mode = CipherMode.CBC;
ICryptoTransform decipher = aes.CreateDecryptor(aes.Key, aes.IV);
using (MemoryStream ms = new MemoryStream(cipheredData))
{
using (CryptoStream cs = new CryptoStream(ms, decipher, CryptoStreamMode.Read))
{
using (StreamReader sr = new StreamReader(cs))
{
decrypted = sr.ReadToEnd();
}
}
return decrypted;
}
}
The code fails saying that my byte[256] key has the wrong length for this kind of algorithm.
Thanks for any help with this!
Cheers, Mike
The posted OpenSSL statement uses the -pass file: option and thus a passphrase (which is read from a file), see openssl enc. This causes the encryption process to first generate a random 8 bytes salt and then, together with the passphrase, derive a 32 bytes key and 16 bytes IV using the (not very secure) proprietary OpenSSL function EVP_BytesToKey. This function uses several parameters, e.g. a digest and an iteration count. The default digest for key derivation is MD5 and the iteration count is 1. Note that OpenSSL version 1.1.0 and later uses SHA256 as default digest, i.e. depending on the OpenSSL version used to generate the ciphertext, the appropriate digest must be used for decryption. Preceding the ciphertext is a block whose first 8 bytes is the ASCII encoding of Salted__, followed by the 8 bytes salt.
Therefore, the decryption must first determine the salt. Based on the salt, together with the passphrase, key and IV must be derived and then the rest of the encrypted data can be decrypted. Thus, first of all an implementation of EVP_BytesToKey in C# is required, e.g. here. Then a possible implementation could be (using MD5 as digest):
public static string DecryptAesCbc(byte[] cipheredData, string passphrase)
{
string decrypted = null;
using (MemoryStream ms = new MemoryStream(cipheredData))
{
// Get salt
byte[] salt = new byte[8];
ms.Seek(8, SeekOrigin.Begin);
ms.Read(salt, 0, 8);
// Derive key and IV
OpenSslCompat.OpenSslCompatDeriveBytes db = new OpenSslCompat.OpenSslCompatDeriveBytes(passphrase, salt, "MD5", 1);
byte[] key = db.GetBytes(32);
byte[] iv = db.GetBytes(16);
using (Aes aes = Aes.Create())
{
aes.Padding = PaddingMode.PKCS7;
aes.Mode = CipherMode.CBC;
aes.Key = key;
aes.IV = iv;
// Decrypt
ICryptoTransform decipher = aes.CreateDecryptor(aes.Key, aes.IV);
using (CryptoStream cs = new CryptoStream(ms, decipher, CryptoStreamMode.Read))
{
using (StreamReader sr = new StreamReader(cs, Encoding.UTF8))
{
decrypted = sr.ReadToEnd();
}
}
}
}
return decrypted;
}
Note that the 2nd parameter of DecryptAesCbc is the passphrase (as string) and not the key (as byte[]). Also note that StreamReader uses an encoding (UTF-8 by default), which requires compatible data (i.e. text data, but this should be met for csv files). Otherwise (i.e. for binary data as opposed to text data) StreamReader must not be used.
I'm trying to encrypt and then decrypt a file when needed, and read the file for use of the program. Still learning cryptography and I looked at several examples and tried this.
I have managed to en- and decrypt string but sadly that did not work in this case.
Cryptostream gives an IV does not match block size error on encrypt. Could not even have tested decrypting but that is most likely off too.
So question is: How can I encrypt, decrypt and read a .txt file?
private void EncryptFile(string resultFile)
{
string password = #"test";
UnicodeEncoding UE = new UnicodeEncoding();
byte[] key = UE.GetBytes(password);
FileStream fsCrypt = File.Create(resultFile);
RijndaelManaged rmCrypto = new RijndaelManaged();
CryptoStream cs = new CryptoStream(fsCrypt, rmCrypto.CreateEncryptor(key, key),CryptoStreamMode.Write);
StreamWriter sWriter = new StreamWriter(cs);
sWriter.WriteLine(resultFile);
sWriter.Close();
cs.Close();
fsCrypt.Close();
}
private void DecryptFile(string resultFile)
{
string password = #"test";
UnicodeEncoding UE = new UnicodeEncoding();
byte[] key = UE.GetBytes(password);
FileStream fsCrypt = new FileStream(resultFile, FileMode.Open);
RijndaelManaged rmCrypto = new RijndaelManaged();
CryptoStream cs = new CryptoStream(fsCrypt, rmCrypto.CreateDecryptor(key, key), CryptoStreamMode.Read);
StreamWriter sWriter = new StreamWriter(cs);
sWriter.WriteLine(resultFile);
sWriter.Close();
cs.Close();
fsCrypt.Close();
}
I would recommend using AesManaged instead of RijndaelManaged, mostly because the former follows a defined standard.
Aes/rijndael have specified sizes for the key and initialization vector. You should not use bytes from a password directly. The correct way is to run the password thru a key derivation function see keyderivationalgorithmprovider, to ensure you have a key of the correct size and of good quality. Or use a randomly generated binary key of the correct size.
The second problem is the Initialization vector, this also need to be of a the correct size and should be random. But the IV is not secret and will usually be attached to the encrypted message, so using the key as the IV will not be secure.
The best option is probably to use the key and IV from the encryption class. See microsofts example for more details.
I have simple symmetric encrypt/decrypt application that works fine:
namespace Crypto
{
class Program
{
public static void EncryptSomeText()
{
string original ="My secretdata!";
using (SymmetricAlgorithm symmetricAlgorithm =
new AesManaged())
{
byte[] encrypted = Encrypt(symmetricAlgorithm, original);
string roundtrip = Decrypt(symmetricAlgorithm, encrypted);
// Displays: My secret data!
Console.WriteLine("Original:{ 0}", original);
Console.WriteLine("RoundTrip:{ 0}", roundtrip);
}
}
static byte[] Encrypt(SymmetricAlgorithm aesAlg, string plainText)
{
ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);
using (MemoryStream msEncrypt = new MemoryStream())
{
using (CryptoStream csEncrypt =
new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
{
using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
{
swEncrypt.Write(plainText);
}
return msEncrypt.ToArray();
}
}
}
static string Decrypt(SymmetricAlgorithm aesAlg, byte[] cipherText)
{
ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV);
using (MemoryStream msDecrypt = new MemoryStream(cipherText))
{
using (CryptoStream csDecrypt =
new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
{
using (StreamReader srDecrypt = new StreamReader(csDecrypt))
{
return srDecrypt.ReadToEnd();
}
}
}
}
static void Main(string[] args)
{
SymmetricAlgorithm sma = SymmetricAlgorithm.Create();
byte[] b = Encrypt(sma,"bla bla");
string s= Decrypt(sma, b);
Console.WriteLine("Decrypted {0}", s);
}
}
}
I'm just wondering regarding key creation. Does it means that it is created in automatic way during symmetric algorithm creation? Is it generated each time different? I suppose, user should pass key for this purpose.
SymmetricAlgorithm sma = SymmetricAlgorithm.Create();
Random key is generated when you access Key property of your SymmetricAlgorithm, assuming you did not set that Key before that to some predefined key. It's not generated during construction of SymmetricAlgorithm itself.
var sma = SymmetricAlgorithm.Create();
// no key generated here yet
var key = sma.Key; // generated key
// accessing `Key` causes random key generation
var sma = SymmetricAlgorithm.Create();
// no key here
sma.Key = myKey;
// you set the key, so no random key is generated
The same is true for IV (nonce) value.
Generating random key makes sense only once, then you have to share it between encrypting\decrypting party in some safe way and use it for future encryptions\decryptions. While you are there, note that IV value should be random for each encryption, so usually you just prepend that IV value to the encrypted binary array and before decrypting you cut that IV from the beginning of encrypted array and use for decryption.
On this github page there is utility class written in Java which performs encryption/decryption using symmetric algorithm, below could be one of encryption flows using symmetric algorithm :
generate secret key using the selected algorithm (DES, 3DES, AES etc)
generate secure random number of seed bytes which is computed with available the seed generation algorithm
probably text message to be encrypted is not the multiples of 8 byte blocks that's why message must be padded with additional bytes to make the text message to be multiples of 8-byte blocks.(e.g. PKCS5Padding padding scheme )
use random generated secure seed bytes as initialization vector as block cipher
initialize cipher function with the symmetric key and block cipher
finish encryption
apply binary to text encoding with selected standard for initialization vector(IV) and encrypted binary data
define message format before sending in order to split the message into IV and encrypted for decryption on recipient side
I'm writing a ClickOnce application that runs a batch file process with service account credentials. I need to store the service account credentials so that the program can add the username/password to the process.startinfo property before running the process. The users do not know this password, so there's no prompt for them to enter in a password. I believe this means I cannot store the hash and verify the password that way, the hash value I generate must be reversible so that it can add the correct password to the startinfo property. I searched around this site and came up with a Frankenstein-type solution that works, but it's not very secure. Currently, I used this method to encrypt the password, stored the encrypted value, then use the decrypt method to obtain the password during runtime (the encrypt method is never ran during runtime, I ran it in Visual Studio during debug, copied the value, then used that value in the decrypt method below this):
// used to generate decrypted acct creds
private void EncryptText(string plaintext)
{
string outsrt = null;
RijndaelManaged aesAlg = null;
try
{
// generate key from secret and salt
Rfc2898DeriveBytes key = new Rfc2898DeriveBytes(sharedsecret, _salt);
aesAlg = new RijndaelManaged();
aesAlg.Key = key.GetBytes(aesAlg.KeySize / 8);
ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);
using (MemoryStream mEncrypt = new MemoryStream())
{
// prepend the IV
mEncrypt.Write(BitConverter.GetBytes(aesAlg.IV.Length), 0, sizeof(int));
mEncrypt.Write(aesAlg.IV, 0, aesAlg.IV.Length);
using (CryptoStream csEncrypt = new CryptoStream(mEncrypt, encryptor, CryptoStreamMode.Write))
{
using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
{
// write all data to the stream
swEncrypt.Write(plaintext);
}
}
outsrt = Convert.ToBase64String(mEncrypt.ToArray());
}
}
finally
{
if (aesAlg != null)
aesAlg.Clear();
}
Console.WriteLine(outsrt);
}
Here's the decrypt method:
private string GetServiceAcctPW()
{
// Declare the RijndaelManaged object
// used to decrypt the data.
RijndaelManaged aesAlg = null;
// Declare the string used to hold
// the decrypted text.
string plaintext = null;
try
{
// generate the key from the shared secret and the salt
Rfc2898DeriveBytes key = new Rfc2898DeriveBytes(sharedsecret, _salt);
// Create the streams used for decryption.
byte[] bytes = Convert.FromBase64String("EncryptedValueHere");
using (MemoryStream msDecrypt = new MemoryStream(bytes))
{
// Create a RijndaelManaged object
// with the specified key and IV.
aesAlg = new RijndaelManaged();
aesAlg.Key = key.GetBytes(aesAlg.KeySize / 8);
// Get the initialization vector from the encrypted stream
aesAlg.IV = ReadByteArray(msDecrypt);
// Create a decrytor to perform the stream transform.
ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV);
using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
{
using (StreamReader srDecrypt = new StreamReader(csDecrypt))
// Read the decrypted bytes from the decrypting stream
// and place them in a string.
plaintext = srDecrypt.ReadToEnd();
}
}
}
catch(Exception e)
{
Console.WriteLine("Error decrypting password");
Console.WriteLine(e.StackTrace);
logger.WriteToLog(Logger.LogCodes.ERROR, "Error decrypting service account password");
MessageBox.Show("An error occurred while trying to start the installation process\nPlease contact the Service Desk for further assistance");
}
finally
{
// Clear the RijndaelManaged object.
if (aesAlg != null)
aesAlg.Clear();
}
return plaintext;
}
This code works just fine, however, I know it's not secure. My code review guy said he was able to crack it with dotPeek in an hour because it's only adding a layer of obfuscation. What would be the best/proper way to store these credentials within the application?
The encryption key is on a dedicated server.
The password is sent to the server along with an id to be encrypted and the encrypted password returned for DB storage.
When the the password is needed a request is made to the dedicated server with the id and a decrypted password is returned.
The password is never saved to disk and the key is never available off the dedicated server.
The dedicated server is kind-of-like a poor-mans HSM.
This is encryption, not hashing. The encryption key is secret along with a random IV that that is saved with the id on the dedicated server. The key is not available and not related to the password so there is no better attack than brute force against the encryption key which is essentially to large to be attacked by brute force.
The server needs to be very secure, only a couple of two factor logins and not available to the Internet.
Intro
I'm trying to encrypt and decrypt texts and sometimes, especially for larger texts, random characters appear within the decrypted text. I'm using AES cryptography within the System.Security.Cryptography namespace and the text I'm trying to encrypt at the moment would be a URL and some info, such as the page title. I've provided an example below and what I've attempted. I've also written the two encryption and decryption methods, minus any lines that output to the Debug Window. The Key and IV used shouldn't be a problem as, for now, they would be constant.
I think it would be wise for me to point out that it encrypts and decrypts 18/01/2013;18/01/2013, in a separate occurrence, as expected.
Example
Say I wanted to decrypt this text:
Barnabe Googes Information & Homepage | Search and Research on BarnabeGooge.com;18/01/2013;18/01/2013;;http://www.googe.com
By default it uses UTF-8 and it would encrypt to:
뤟羜ڮ胂淺弊놛荧ꠃ錺槝ヸ蘜ầᄼꕒヘ⍩㗪潺뱂施㒞ꨛ殳硪픴ی뿨춃�燲ᯁﱪ뙊힓琲鯖嶑⨹갂Ѭ쳀鿜�྄䋖⭫ퟂ㪏�荾ꆺשּ붹梾麦膛
And decrypts back to:
Barnabe Googes Information & Homepage | Search and Research on B���Ax2�!��f�M]18/01/20�;18/01[�;>َ�l?����m��*-��+��^A[=�
What I've attempted
I've attempted to change to other Encodings, but UTF-8 seem to affect the decrypted text the least.
Changed to different types of padding, but Padding.Zeros seems the best. I also can't use Padding.None because it throws a NotSupportedException: bad data length.
Changed the Mode to CBC (Not that it should matter).
Flush/Close CryptoStream so it could flush the final block, or something.
Just in case the fault rested with the title, I used WebUtility.HtmlDecode() to decode the title, but it didn't affect it.
Encryption Method
The encryption below uses AES Encryption, as you can see. I want to point out that key and IV are two global strings within the same class as both of the Encryption and Decryption methods. The reason I've done this is to mess around with different encodings and CryptographyServiceProviders, just if by chance a random change works. Please ignore these as they are constant and won't affect the final encryption/decryption.
public static byte[] EncryptStringToBytes(string plainText, Encoding Enc)
{
if (plainText == null || plainText.Length <= 0)
throw new ArgumentNullException("plainText");
byte[] encrypted;
using (AesCryptoServiceProvider tdsAlg = new AesCryptoServiceProvider())
{
tdsAlg.Key = (byte[])Enc.GetBytes(key).Take(tdsAlg.Key.Length).ToArray();
tdsAlg.IV = (byte[])Enc.GetBytes(IV).Take(tdsAlg.IV.Length).ToArray();
tdsAlg.Padding = PaddingMode.Zeros;
tdsAlg.Mode = CipherMode.CBC;
ICryptoTransform encryptor = tdsAlg.CreateEncryptor(tdsAlg.Key, tdsAlg.IV);
using (MemoryStream msEncrypt = new MemoryStream())
{
using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
{
using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
{
swEncrypt.Write(plainText);
}
encrypted = msEncrypt.ToArray();
csEncrypt.Close();
}
}
}
return encrypted;
}
Decryption Method
public static string DecryptStringFromBytes(byte[] cipherText,Encoding Enc)
{
if (cipherText == null || cipherText.Length <= 0)
throw new ArgumentNullException("cipherText");
string plaintext = null;
using (AesCryptoServiceProvider tdsAlg = new AesCryptoServiceProvider())
{
tdsAlg.Key = (byte[])Enc.GetBytes(key).Take(tdsAlg.Key.Length).ToArray();
tdsAlg.IV = (byte[])Enc.GetBytes(IV).Take(tdsAlg.IV.Length).ToArray() ;
tdsAlg.Padding = PaddingMode.Zeros;
tdsAlg.Mode = CipherMode.CBC;
ICryptoTransform decryptor = tdsAlg.CreateDecryptor();
using (MemoryStream msDecrypt = new MemoryStream(cipherText))
{
using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
{
using (StreamReader srDecrypt = new StreamReader(csDecrypt,true))
{
plaintext = srDecrypt.ReadToEnd().Replace("\0","");
csDecrypt.Close();
return plaintext.Replace("\0",string.Empty);
}
}
}
}
return plaintext;
}
Bootnote
Just in case it matters, I'm using this to get the title of the webpage, but as I've mentioned using HtmlDecode doesn't affect it.
WebClient x = new WebClient();
string source = x.DownloadString(Url);
x.Dispose();
string title= Regex.Match(source, #"\<title\b[^>]*\>\s*(?<Title>[\s\S]*?)\</title\>", RegexOptions.IgnoreCase).Groups["Title"].Value;
title = title.Replace(";", " ");
return title;
Thanks to Hans Passant I found the solution. The problem was that I was using Encoding.GetString() or Encoding.GetBytes() when I was encrypting and decrypting, when I should have been using Convert.ToBase64String() or Convert.FromBase64String().
I had the same problem of extra output. For me it was not encoding-problem, because I was passing it as byte array in BCrypt library. As it is plain-text, I would use space-character as padding before encryption and trim after decryption.
int padding = BLOCK_SIZE - (input_len+1)%BLOCK_SIZE;
if(padding && (input_len+padding) <= buf_size)
{
memset(buf+input_len, ' ', padding);
input_len += padding;
}
For 128 bit encryption, the block-size is 16. Note that the buf_size should be multiple of the block-size to make it work all the time. As we padded the input already, we do not need the padding algorithm in decryption.
tdsAlg.Padding = PaddingMode.None;
And at the end of decryption, I would trim the output.