I know this question has been asked before but the answer simply wasn't what I needed. I need to create a password protected text file. I don't mean to encrypt but just create a file with a simple text password. It would also be nice to find out how to open this file in C# as well.
Create a plain-text password protected file and open this file at a later time. All in C#.
Password protecting a text file that is not encrypted is not really possible. However, you can validate if your text file has been modified, so that you are aware it has been altered by someone.
The concept is simple. Use a password to encrypt or obfuscate a hash of the data (text file). You can the later check this hash against the current data and determine whether or not it matches. You will need to store this signature (encrypted hash) somewhere maybe in a file called (textfile.sig).
You can use the SHA1Managed .NET class to create the hash, and the TripleDESCryptoServiceProvider class to encrypt the resulting hash.
something like...
public string GetSignature(string text, string password)
{
byte[] key;
byte[] key2;
GetKeys(password, out key, out key2);
string sig = encryptstring(GetSHA1(text), key, key2);
}
public void GetKeys(string password, out byte[] key, out byte[] key2)
{
byte[] data = StringToByte(password);
SHA1 sha = new SHA1CryptoServiceProvider();
MD5 md5 = new MD5CryptoServiceProvider();
byte[] hash1 = sha.ComputeHash(data);
byte[] hash2 = md5.ComputeHash(data);
// Generate some key data based on the supplied password;
byte[] key = new byte[24];
for (int i = 0; i < 20; i++)
{
key[i] = hash1[i];
}
for (int i = 0; i < 4; i++)
{
key[i + 20] = hash2[i];
}
byte[] key2 = new byte[8];
for (int i = 0; i < 8; i++)
{
key2[i] = hash2[i+4];
}
}
public string GetSHA1(string text)
{
UnicodeEncoding UE = new UnicodeEncoding();
byte[] hashValue;
byte[] message = UE.GetBytes(text);
SHA1Managed hashString = new SHA1Managed();
string hex = "";
hashValue = hashString.ComputeHash(message);
foreach (byte x in hashValue)
{
hex += String.Format("{0:x2}", x);
}
return hex;
}
public string ByteToString(byte[] buff)
{
string sbinary = "";
for (int i = 0; i < buff.Length; i++)
{
sbinary += buff[i].ToString("X2"); // hex format
}
return (sbinary);
}
public string encryptstring(string instr, byte[] key, byte[] key2)
{
TripleDES threedes = new TripleDESCryptoServiceProvider();
threedes.Key = key;
threedes.IV = key2;
ICryptoTransform encryptor = threedes.CreateEncryptor(key, key2);
MemoryStream msEncrypt = new MemoryStream();
CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write);
// Write all data to the crypto stream and flush it.
csEncrypt.Write(StringToByte(instr), 0, StringToByte(instr).Length);
csEncrypt.FlushFinalBlock();
return ByteToString(msEncrypt.ToArray());
}
public string decryptstring(string instr, byte[] key, byte[] key2)
{
if (string.IsNullOrEmpty(instr)) return "";
TripleDES threedes = new TripleDESCryptoServiceProvider();
threedes.Key = key;
threedes.IV = key2;
ICryptoTransform decryptor = threedes.CreateDecryptor(key, key2);
// Now decrypt the previously encrypted message using the decryptor
MemoryStream msDecrypt = new MemoryStream(HexStringToByte(instr));
CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read);
try
{
return ByteToString(csDecrypt);
}
catch (CryptographicException)
{
return "";
}
}
Otherwise, you will if you want to hide the data from people, you need to encrypt the data in the text file. You can do this by calling encryptstring() with the full text data instead of the hash of the data.
Whole point of the question was to not have to encrypt the data manually but let Windows deal with it so I decided to go with Hans Passant's suggestion and simply use a password protected zip file with the needed information (txt files) inside it. This can be easily done with DotNetZip.
Related
I need to decrypt a string in C# that was encrypted via CakePHP's Security::rijndael library.
The string is being encrypted in PHP like this:
base64_encode(Security::rijndael($pw, Configure::read('SecretKey'), 'encrypt'))
The resulting encoding is 88 characters long and stored in the db.
Here is how I'm trying to decrypt it in C#:
public static string Decrypt(string encode, string secretKey)
{
var sha = SHA256Managed.Create();
var saltBytes = sha.ComputeHash(Encoding.ASCII.GetBytes(secretKey));
var key = new Rfc2898DeriveBytes(secretKey, saltBytes);
var rm = new RijndaelManaged();
rm.Mode = CipherMode.CBC;
rm.KeySize = 256;
rm.BlockSize = 256;
rm.Key = key.GetBytes(rm.KeySize / 8);
rm.IV = key.GetBytes(rm.BlockSize / 8);
var decryptor = rm.CreateDecryptor(rm.Key, rm.IV);
var cipher = Convert.FromBase64String(encode);
string decrypted;
using (var msDescrypt = new MemoryStream(cipher))
{
using (var csDescrypt = new CryptoStream(msDescrypt, decryptor, CryptoStreamMode.Read))
{
using (var srDescrypt = new StreamReader(csDescrypt))
{
decrypted = srDescrypt.ReadToEnd();
}
}
}
return decrypted;
}
When reading the stream I get an exception of "Length of the data to decrypt is invalid". Do I need to manipulate the incoming encoded string to make this work?
I am working on re-writing our encryption class to be FIPS compliant, and in doing so have to re-work how we're handling non-secret payload data. At the moment, I'm writing out the size of my non-secret payload, then writing the size of my IV. I follow that up by writing my non-secret payload and IV, with all of these writes sharing a BinaryWriter. Lastly, I then share the same MemoryStream and write my the data needing to be encrypted into the the CryptoStream.
This is what the class currently looks like:
public class Encryption
{
private const int SaltBlockSize = 8;
private const int SaltBitSize = 64;
private const int KeyBitSize = 256;
private const int SaltIterations = 10000;
private const int nonSecretPayloadOffsetInPayload = 0;
private const int ivOffsetInPayload = 1;
public byte[] GetNonSecretPayload(byte[] completePayload)
{
byte[] nonSecretPayload;
using (var memoryStream = new MemoryStream(completePayload))
{
using (var binaryReader = new BinaryReader(memoryStream))
{
int nonSecretPayloadLength = binaryReader.ReadInt32();
binaryReader.BaseStream.Position = 3;
nonSecretPayload = binaryReader.ReadBytes(nonSecretPayloadLength);
}
}
return nonSecretPayload;
}
public byte[] EncryptMessageWithPassword(byte[] secretMessage, string password, byte[] nonSecretPayload = null)
{
if (string.IsNullOrEmpty(password))
{
throw new InvalidOperationException("You can not provide an empty password, you must give a string that is at least 12 characters in size. If you just want to obfuscate the message without any protection, an alternative way is to use a Base64 String");
}
else if (password.Length < 12)
{
throw new InvalidOperationException("The minimum size your password can be is 12 characters.");
}
byte[] saltHash;
byte[] saltKey = this.CreateSaltKeysFromPassword(password, 0, out saltHash);
byte[] encryptedValue = null;
using (AesCryptoServiceProvider aesProvider = new AesCryptoServiceProvider())
{
aesProvider.Key = saltKey;
aesProvider.Mode = CipherMode.CBC;
aesProvider.Padding = PaddingMode.PKCS7;
aesProvider.GenerateIV();
using (MemoryStream memoryStream = new MemoryStream())
{
// Write our IV out first so we can pull the IV off later during decryption.
// The IV does not need to be encrypted, it is safe to store as as unencrypted buffer in the encrypted byte array.
using (BinaryWriter ivWriter = new BinaryWriter(memoryStream, Encoding.UTF8, true))
{
// The first two writes to the stream should be the size of the non-secret payload
// and the size of the IV. If no payload exists, then we write 0.
if (nonSecretPayload == null || nonSecretPayload.Length == 0)
{
ivWriter.Write(0);
}
else
{
ivWriter.Write(nonSecretPayload.Length);
}
ivWriter.Write(aesProvider.IV.Length);
// If we have a payload, write it out.
if (nonSecretPayload != null && nonSecretPayload.Length > 0)
{
ivWriter.Write(nonSecretPayload);
}
// Write the Initialization Vector.
ivWriter.Write(aesProvider.IV);
}
// Create our encryptor and write the secret message to the encryptor stream.
var encryptor = aesProvider.CreateEncryptor(saltKey, aesProvider.IV);
using (CryptoStream cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write))
{
cryptoStream.Write(secretMessage, 0, secretMessage.Length);
cryptoStream.FlushFinalBlock();
}
// Get the non-secret payload, IV, payload and IV lengths and encrypted data back as an array of bytes.
encryptedValue = memoryStream.ToArray();
}
}
return encryptedValue;
}
public string EncryptMessageWithPassword(string secretMessage, string password, byte[] nonSecretPayLoad = null)
{
byte[] secreteMessageBytes = Encoding.UTF8.GetBytes(secretMessage);
byte[] encryptedMessage = this.EncryptMessageWithPassword(secreteMessageBytes, password, nonSecretPayLoad);
return Convert.ToBase64String(encryptedMessage);
}
private byte[] CreateSaltKeysFromPassword(string password, int nonSecretPayloadSize, out byte[] saltHash)
{
byte[] saltKey;
//Use Random Salt to prevent pre-generated weak password attacks.
using (var generator = new Rfc2898DeriveBytes(password, SaltBitSize / SaltBlockSize, SaltIterations))
{
// Get a generated salt derived from the user password, hashed n-times where n = SaltIterations
saltHash = generator.Salt;
//Generate Keys
saltKey = generator.GetBytes(KeyBitSize / SaltBlockSize);
}
return saltKey;
}
}
I would expect in my GetNonSecretPayload(byte[] payload); that by setting the position, or using binaryReader.BaseStream.Seek(2); to skip the IV length item, I would skip the IV size entry in the byte[] array and be able to read the bytes associated with the actual non-secret data. This doesn't work though, presumably because this isn't an array underneath the covers that I can just move to the next element in the array, skipping the IV length wrote out originally.
I have the following unit test.
[TestClass]
public class EncryptionTests
{
private const string _ContentToEncrypt = "This is a test to make sure the encryption Type actually encrypts the data right.";
private const string _Password = "EncryptedPassword1";
[TestMethod]
public void Extract_non_secret_payload_content_from_encrypted_string()
{
// Arrange
var encryption = new Encryption();
string nonSecretData = "My payload is not considered secret and can be pulled out of the payload without decrypting";
// Convert the secret and non-secret data into a byte array
byte[] payload = Encoding.UTF8.GetBytes(nonSecretData);
byte[] encodedBytes = Encoding.UTF8.GetBytes(_ContentToEncrypt);
// Encrypt the secret data while injecting the nonsecret payload into the encrypted stream.
byte[] encryptedValue = encryption.EncryptMessageWithPassword(encodedBytes, _Password, payload);
// Act
// Pull the non-secret payload out of the encrypted message - without having to decrypt it.
byte[] UnencryptedPayloadWithinEncryptedArray = encryption.GetNonSecretPayload(encryptedValue);
string payloadContent = Encoding.UTF8.GetString(UnencryptedPayloadWithinEncryptedArray);
// Assert
Assert.AreEqual(nonSecretData, payloadContent);
}
}
What I get with my current binaryReader.BaseStream.Position = 3 is
"\0\u0010\0\0\0My payload is not considered secret and can be pulled out of the payload without decry"
I've read and wrote data like this in the past using a BinaryWriter, but I've never had to seek through it in order to skip data. What am I doing wrong here?
I was told not to use RSA to encrypt simple text but to use AES. I found a simple piece of code to implement AES:
public static class Crypto
{
#region Settings
private static int _iterations = 2;
private static int _keySize = 256;
private static string _hash = "SHA1";
private static string _salt = "aselrias38490a32"; // Random
private static string _vector = "8947az34awl34kjq"; // Random
#endregion
public static string Encrypt(string value, string password)
{
return Encrypt<AesManaged>(value, password);
}
public static string Encrypt<T>(string value, string password)
where T : SymmetricAlgorithm, new()
{
byte[] vectorBytes = Encoding.ASCII.GetBytes(_vector);
byte[] saltBytes = Encoding.ASCII.GetBytes(_salt);
byte[] valueBytes = Encoding.UTF8.GetBytes(value);
byte[] encrypted;
using (T cipher = new T())
{
PasswordDeriveBytes _passwordBytes =
new PasswordDeriveBytes(password, saltBytes, _hash, _iterations);
byte[] keyBytes = _passwordBytes.GetBytes(_keySize/8);
cipher.Mode = CipherMode.CBC;
using (ICryptoTransform encryptor = cipher.CreateEncryptor(keyBytes, vectorBytes))
{
using (MemoryStream to = new MemoryStream())
{
using (CryptoStream writer = new CryptoStream(to, encryptor, CryptoStreamMode.Write))
{
writer.Write(valueBytes, 0, valueBytes.Length);
writer.FlushFinalBlock();
encrypted = to.ToArray();
}
}
}
cipher.Clear();
}
return Convert.ToBase64String(encrypted);
}
public static string Decrypt(string value, string password)
{
return Decrypt<AesManaged>(value, password);
}
public static string Decrypt<T>(string value, string password) where T : SymmetricAlgorithm, new()
{
byte[] vectorBytes = Encoding.ASCII.GetBytes(_vector);
byte[] saltBytes = Encoding.ASCII.GetBytes(_salt);
byte[] valueBytes = Convert.FromBase64String(value);
byte[] decrypted;
int decryptedByteCount = 0;
using (T cipher = new T())
{
PasswordDeriveBytes _passwordBytes = new PasswordDeriveBytes(password, saltBytes, _hash, _iterations);
byte[] keyBytes = _passwordBytes.GetBytes(_keySize/8);
cipher.Mode = CipherMode.CBC;
try
{
using (ICryptoTransform decryptor = cipher.CreateDecryptor(keyBytes, vectorBytes))
{
using (MemoryStream from = new MemoryStream(valueBytes))
{
using (CryptoStream reader = new CryptoStream(from, decryptor, CryptoStreamMode.Read))
{
decrypted = new byte[valueBytes.Length];
decryptedByteCount = reader.Read(decrypted, 0, decrypted.Length);
}
}
}
}
catch (Exception ex)
{
return String.Empty;
}
cipher.Clear();
}
return Encoding.UTF8.GetString(decrypted, 0, decryptedByteCount);
}
}
However, this is based on a string coming back and then used to decrypt in the same program. I need to encrypt the following data in a WinForms program and the decrypt in a whole separate Windows Service program:
string fileName = System.IO.Path.Combine(Application.StartupPath, "alphaService.xml");
XDocument doc = new XDocument();
XElement xml = new XElement("Info",
new XElement("DatabaseServerName", txtServerName.Text),
new XElement("DatabaseUserName", txtDatabaseUserName.Text),
new XElement("DatabasePassword", txtDatabasePassword.Text),
new XElement("ServiceAccount", txtAccount.Text),
new XElement("ServicePassword", txtServicePassword.Text),
new XElement("RegistrationCode", txtRegistrationCode.Text));
doc.Add(xml);
doc.Save(fileName);
// Convert XML doc to byte stream
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(fileName);
// byte[] fileBytes = Encoding.Default.GetBytes(xmlDoc.OuterXml);
string encrypted = Crypto.Encrypt(xmlDoc.OuterXml, "testpass");
How can I do it? Please show sample code.
EDIT: Kevin, I have implemented your algorithm but the problem is I want to generate the key once and save it for use in the other program to decrypt but I need to pass the byte[] to the encrypt function. So I tried converting using System.Text.Encoding.ASCII.GetBytes(key); and it doesn't do it correctly. I have the wrong number of bytes for byte[] for the key.
string fileName = System.IO.Path.Combine(Application.StartupPath, "alphaService.xml");
XDocument doc = new XDocument();
XElement xml = new XElement("Info",
new XElement("DatabaseServerName", txtServerName.Text),
new XElement("DatabaseUserName", txtDatabaseUserName.Text),
new XElement("DatabasePassword", txtDatabasePassword.Text),
new XElement("ServiceAccount", txtAccount.Text),
new XElement("ServicePassword", txtServicePassword.Text),
new XElement("RegistrationCode", txtRegistrationCode.Text));
doc.Add(xml);
doc.Save(fileName);
// Read file to a string
string contents = File.ReadAllText(fileName);
string key = String.Empty;
byte[] aesKey;
using (var aes = Aes.Create())
{
// aesKey = aes.Key;
key = Convert.ToBase64String(aes.Key);
}
string sKey = "LvtZELDrB394hbSOi3SurLWAvC8adNpZiJmQDJHdfJU=";
aesKey = System.Text.Encoding.UTF8.GetBytes(sKey);
string encyptedText = EncryptDecrpt.EncryptStringToBase64String(contents, aesKey);
File.WriteAllText(fileName, encyptedText);
EDIT2: Here's both parts as they stand now. The encrypting side:
private void SaveForm()
{
try
{
string fileName = System.IO.Path.Combine(Application.StartupPath, "alphaService.xml");
XDocument doc = new XDocument();
XElement xml = new XElement("Info",
new XElement("DatabaseServerName", txtServerName.Text),
new XElement("DatabaseUserName", txtDatabaseUserName.Text),
new XElement("DatabasePassword", txtDatabasePassword.Text),
new XElement("ServiceAccount", txtAccount.Text),
new XElement("ServicePassword", txtServicePassword.Text),
new XElement("RegistrationCode", txtRegistrationCode.Text));
doc.Add(xml);
// doc.Save(fileName);
// Read file to a string
// string contents = File.ReadAllText(fileName);
string key = String.Empty;
byte[] aesKey;
//using (var aes = Aes.Create())
//{
// aesKey = aes.Key;
// key = Convert.ToBase64String(aes.Key);
//}
string sKey = "LvtZELDrB394hbSOi3SurLWAvC8adNpZiJmQDJHdfJU=";
aesKey = Convert.FromBase64String(sKey);
string encyptedText = EncryptDecrpt.EncryptStringToBase64String(doc.ToString(), aesKey);
File.WriteAllText(fileName, encyptedText);
//doc.Save(fileName);
The Windows Service side that tries to decrypt:
try
{
string path = AppDomain.CurrentDomain.BaseDirectory;
eventLog1.WriteEntry(path);
string fileName = System.IO.Path.Combine(path, "alphaService.xml");
string sKey = "LvtZELDrB394hbSOi3SurLWAvC8adNpZiJmQDJHdfJU=";
Byte[] keyBytes = Convert.FromBase64String(sKey);
var encryptedText = File.ReadAllText(fileName, new ASCIIEncoding());
string xmlStr = DecryptStringFromBase64String(encryptedText, keyBytes);
eventLog1.WriteEntry(xmlStr);
using (XmlReader reader = XmlReader.Create(new StringReader(xmlStr)))
{
reader.ReadToFollowing("DatabaseServerName");
DatabaseServerName = reader.ReadElementContentAsString();
reader.ReadToFollowing("DatabaseUserName");
DatabaseUserName = reader.ReadElementContentAsString();
reader.ReadToFollowing("DatabasePassword");
DatabasePassword = reader.ReadElementContentAsString();
reader.ReadToFollowing("RegistrationCode");
RegistrationCode = reader.ReadElementContentAsString();
}
eventLog1.WriteEntry("Configuration data loaded successfully");
}
catch (Exception ex)
{
eventLog1.WriteEntry("Unable to load configuration data. " + ex.Message);
}
The algorithm I wrote below uses a random Initialization Vector that it puts at the beginning of the encrypted value so you can encrypt the same value twice and not get the same encrypted output. This is fairly normal and lets you only pass a single "secret" back and forth.
You will need to share your secret key by some out of bounds process because both encryption and decryption need to know the key. That is a seperate topic of key exchange that is documented in other places. Here is an SO link to get you started if you need some help on it.
Also if you are "making up" random values I recommend that you don't. Use something to help you like the following which generates random bytes and then converts them into a base64 string which is easier for human usage or some types of key exchange. Note that this is just an example of how you could generate random key's... in practice this may be based on some user input that is recreatable or you use the users hash value to lookup your random key that you generate. In any event here is the code for the key...
byte[] key;
string base64Key;
using (var aes = Aes.Create())
{
// key as byte[]
key = aes.Key;
// key as base64string - which one you use depends on how you store your keys
base64Key= Convert.ToBase64String(aes.Key);
}
Usage is as follows...
// you get the base64 encoded key from somewhere
var base64Key = "+CffHxKmykUvCrrCILd4rZDBcrIoe3w89jnPNXYi0rU=";
// convert it to byte[] or alternatively you could store your key as a byte[]
// but that depends on how you set things up.
var key = Convert.FromBase64String(base64Key);
var plainText = "EncryptThis";
var encryptedText = EncryptStringToBase64String(plainText, key);
var decryptedText = DecryptStringFromBase64String(encryptedText, key);
Here are the encryption methods... EncryptStringToBase64String and DecryptStringFromBase64String.
EDIT: Great point owlstead about using Aes.BlockSize for the IV size. I've also cleaned up the arguement checks.
private const int KeySize = 256; // in bits
static string EncryptStringToBase64String(string plainText, byte[] Key)
{
// Check arguments.
if (Key == null || Key.Length <= 0)
throw new ArgumentNullException("Key");
byte[] returnValue;
using (var aes = Aes.Create())
{
aes.KeySize = KeySize;
aes.GenerateIV();
aes.Mode = CipherMode.CBC;
var iv = aes.IV;
if (string.IsNullOrEmpty(plainText))
return Convert.ToBase64String(iv);
var encryptor = aes.CreateEncryptor(Key, iv);
// Create the streams used for encryption.
using (MemoryStream msEncrypt = new MemoryStream())
{
using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
{
using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
{
//Write all data to the stream.
swEncrypt.Write(plainText);
}
// this is just our encrypted data
var encrypted = msEncrypt.ToArray();
returnValue = new byte[encrypted.Length + iv.Length];
// append our IV so our decrypt can get it
Array.Copy(iv, returnValue, iv.Length);
// append our encrypted data
Array.Copy(encrypted, 0, returnValue, iv.Length, encrypted.Length);
}
}
}
// return encrypted bytes converted to Base64String
return Convert.ToBase64String(returnValue);
}
static string DecryptStringFromBase64String(string cipherText, byte[] Key)
{
// Check arguments.
if (string.IsNullOrEmpty(cipherText))
return string.Empty;
if (Key == null || Key.Length <= 0)
throw new ArgumentNullException("Key");
string plaintext = null;
// this is all of the bytes
var allBytes = Convert.FromBase64String(cipherText);
using (var aes = Aes.Create())
{
aes.KeySize = KeySize;
aes.Mode = CipherMode.CBC;
// get our IV that we pre-pended to the data
byte[] iv = new byte[aes.BlockSize/8];
if (allBytes.Length < iv.Length)
throw new ArgumentException("Message was less than IV size.");
Array.Copy(allBytes, iv, iv.Length);
// get the data we need to decrypt
byte[] cipherBytes = new byte[allBytes.Length - iv.Length];
Array.Copy(allBytes, iv.Length, cipherBytes, 0, cipherBytes.Length);
// Create a decrytor to perform the stream transform.
var decryptor = aes.CreateDecryptor(Key, iv);
// Create the streams used for decryption.
using (MemoryStream msDecrypt = new MemoryStream(cipherBytes))
{
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();
}
}
}
}
return plaintext;
}
EDIT 2: Never convert actual binary data (like a random key) into a string using a TextEncoding. If data starts life as a string and you convert into binary using an encoding then and ONLY then can you convert it from binary into a string using the proper encoding. Otherwise you will have code that works sometimes which is a recipe for torturing yourself.
// This is base64 not UTF8, unicode, ASCII or anything else!!!
string sKey = "LvtZELDrB394hbSOi3SurLWAvC8adNpZiJmQDJHdfJU=";
aesKey = Convert.FromBase64String(sKey);
Edit 3:
Why use File.WriteAllText to write the file but use File.ReadAllBytes when you read it? You can write it and read it as text and use ASCII encoding since base64 is guaranteed to be ASCII. Also Decrypt returns a decrypted string which you are not storing or using. The decrypted string is what you need to parse because it's your xml.
You can use this for saving the file...
var encryptedText = File.ReadAllText(fileName, new ASCIIEncoding());
In your decrypt you should do this...
var encryptedText = File.ReadAllText(fileName, new ASCIIEncoding());
string xmlStr = DecryptStringFromBase64String(encryptedStr , keyBytes);
EDIT 4: I've attempted to duplicate your exception and I can't make it happen... here is my test code that I'm running in a console app and it works.
public static void EncryptMethod()
{
var fileName = #"c:/text.xml";
XDocument doc = new XDocument();
XElement xml = new XElement("Info",
new XElement("DatabaseServerName", "txtServerName.Text"),
new XElement("DatabaseUserName", "txtDatabaseUserName.Text"),
new XElement("DatabasePassword", "txtDatabasePassword.Text"),
new XElement("ServiceAccount", "txtAccount.Text"),
new XElement("ServicePassword", "txtServicePassword.Text"),
new XElement("RegistrationCode", "txtRegistrationCode.Text"));
doc.Add(xml);
var sKey = "LvtZELDrB394hbSOi3SurLWAvC8adNpZiJmQDJHdfJU=";
var aesKey = Convert.FromBase64String(sKey);
string encyptedText = EncryptStringToBase64String(doc.ToString(), aesKey);
File.WriteAllText(fileName, encyptedText);
}
public static void DecryptMethod()
{
var fileName = #"c:/text.xml";
string sKey = "LvtZELDrB394hbSOi3SurLWAvC8adNpZiJmQDJHdfJU=";
Byte[] keyBytes = Convert.FromBase64String(sKey);
var encryptedText = File.ReadAllText(fileName, new ASCIIEncoding());
string xmlStr = DecryptStringFromBase64String(encryptedText, keyBytes);
using (XmlReader reader = XmlReader.Create(new StringReader(xmlStr)))
{
reader.ReadToFollowing("DatabaseServerName");
Console.WriteLine(reader.ReadElementContentAsString());
reader.ReadToFollowing("DatabaseUserName");
Console.WriteLine(reader.ReadElementContentAsString());
reader.ReadToFollowing("DatabasePassword");
Console.WriteLine(reader.ReadElementContentAsString());
reader.ReadToFollowing("RegistrationCode");
Console.WriteLine(reader.ReadElementContentAsString());
}
}
Usage from the console app...
EncryptMethod();
DecryptMethod();
I am trying to encrypt below base64 string in objective c .. Now i need to decrypt the output of below code in C# ... Please advice simplest way as i don't want to install any libs at server.
Please advice how can convert encripted string back to base64 using C# .
Thanks
My Objective c Code for encripting the base 64
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
SSCrypto *crypto;
NSString *password =#"abcdefghijklmnoqrstuvwzyzabcdefghijklmnopqrstuvwzyzabcdefghijklmnopqrstuvwzyzabcdefghijklmnopqrstuvwzyzabcdefghijklmnopqrstuvwzyzabcdefghijklmnopqrstuvwzyzabcdefghijklmnopqrstuvwzyzabcdefghijklmnopqrstuvwzyzabcdefghijklmnoqrstuvwzyzabcdefghijklmnopqrstuvwzyzabcdefghijklmnopqrstuvwzyzabcdefghijklmnopqrstuvwzyzabcdefghijklmnopqrstuvwzyzabcdefghijklmnopqrstuvwzyzabcdefghijklmnopqrstuvwzyzabcdefghijklmnopqrstuvwzyz";
NSData *seedData1 = [password dataUsingEncoding:NSUTF8StringEncoding];
crypto = [[SSCrypto alloc] initWithSymmetricKey:seedData1];
NSString *base64String = #"SUkqAAgAAAARAP4ABAABAAAAAAAAAAABAwABAAAAsAQAAAEBAwABAAAAEAIAAAIBAwABAAAAAQAAAAMBAwABAAAABAAAAAYBAwABAAAAAAAAAAoBAwABAAAAAQAAABEBBAABAAAACgEAABIBAwABAAAAAQAAABUBAwABAAAAAQAAABYBAwABAAAAEAIAABcBBAABAAAA3yUAABoBBQABAAAA2gAAABsBBQABAAAA4gAAACUBBAABAAAAAAAAACgBAwABAAAAAgAAADEBAgAgAAAA6gAAAAAAAADIAAAAAQAAAMgAAAABAAAAQ29tcHJlc3NlZCB3aXRoIFBBTklOSSBJbWFnZSsAAAAmoy1xRBcev/r/LSUkbRxE+cRxGpEdF0Rw2EcNhHDIBcufR2+R0XZJjI6OIj5HjCPRHR9F0R8wCmRwXI6I+eROjiNxpEdkdEdl2R8ujTMMuy+R0R8j5HZvNo2yOzcXyOiPkdFzLsjxHGRwbC5lwJQaQRDaCchYbAxEREREREREREREREREREREREREREREREREI0BuRww+RIyPFw2MSnIccp0yY58fgxZ5EcKaVhJdx//pL0OQwm5EA///WlgnWSAb/+q9KEgt4X+961qgtKkZJCMH/XlnojiSvNBUkmSCPZBiQlT//iR7pdK0tgnwzep3zv//DLtrqmuLDodf/X7w2lasnCq7CWPf/0l2a6sIIRSVjoLFf9/3sMJ+jrkIjeTonRHRtE6N5A86ohEQtF0d6Ijol0IiIiIiIiIiIi0IiIiIiOIiIiIiIiIiIiIiIiIiIiIgwjtb8tBIhXABABA==";
[crypto setClearTextWithString:base64String];
NSData *cipherText = [crypto encrypt:#"aes256"];
NSLog(#"Cipher text: '%#' using %#", [cipherText encodeBase64WithNewlines:NO], #"aes256");
NSLog(#" ");
[ pool release];
Below is my C# code for Decryption
public static string DecryptString(string base64StringToDecrypt, string passphrase)
{
//Set up the encryption objects
using (AesCryptoServiceProvider acsp = GetProvider(Encoding.Default.GetBytes(passphrase)))
{
byte[] RawBytes = Convert.FromBase64String(base64StringToDecrypt);
ICryptoTransform ictD = acsp.CreateDecryptor();
// TripleDES.
//RawBytes now contains original byte array, still in Encrypted state
//Decrypt into stream
MemoryStream msD = new MemoryStream(RawBytes, 0, RawBytes.Length);
CryptoStream csD = new CryptoStream(msD, ictD, CryptoStreamMode.Read);
//csD now contains original byte array, fully decrypted
//return the content of msD as a regular string
return (new StreamReader(csD)).ReadToEnd();
}
private static AesCryptoServiceProvider GetProvider(byte[] key)
{
AesCryptoServiceProvider result = new AesCryptoServiceProvider();
result.BlockSize = 128;
result.KeySize = 128;
result.Mode = CipherMode.CBC;
result.Padding = PaddingMode.PKCS7;
result.GenerateIV();
result.IV = new byte[] {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
byte[] RealKey = GetKey(key, result);
result.Key = RealKey;
// result.IV = RealKey;
return result;
}
private static byte[] GetKey(byte[] suggestedKey, SymmetricAlgorithm p)
{
byte[] kRaw = suggestedKey;
List<byte> kList = new List<byte>();
for (int i = 0; i < p.LegalKeySizes[0].MinSize; i += 8)
{
kList.Add(kRaw[(i / 8) % kRaw.Length]);
}
byte[] k = kList.ToArray();
return k;
}
Please advice if anything is missing or if i have done something gross wrong ..
I have lost more than 2 days in searching.
You can do this easily with the AesManaged class. See:
http://msdn.microsoft.com/en-us/library/system.security.cryptography.aesmanaged.aspx
I want to obfuscate one query string parameter in ASP.NET. The site will have a high volume of request, so the algorithm shouldn't be too slow.
My problem is that all the algorithms I found result in unwanted characters (like +/=)
Here is an example of what i want to achieve:
www.domain.com/?id=1844
to
www.domain.com/?id=3GQ5DTL3oVd91WsGj74gcQ
The obfuscated param should only include a-z and A-Z and 0-9 characters.
I know I can encrypt using base64, but this will generate unwanted characters such as / or = or +.
Any idea what algorithm can be used?
Update:
I'm aware of UrlEncoding , i want to avoid encoding the string.
because that will generate charaters like %F2 or %B2 in the url.
You can use triple DES to encode the value using a narow block cipher.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security.Cryptography;
namespace ConsoleApplication1 {
class Program {
static string ToHex(byte[] value) {
StringBuilder sb = new StringBuilder();
foreach (byte b in value)
sb.AppendFormat("{0:x2}", b);
return sb.ToString();
}
static string Encode(long value, byte[] key) {
byte[] InputBuffer = new byte[8];
byte[] OutputBuffer;
unsafe {
fixed (byte* pInputBuffer = InputBuffer) {
((long*)pInputBuffer)[0] = value;
}
}
TripleDESCryptoServiceProvider TDes = new TripleDESCryptoServiceProvider();
TDes.Mode = CipherMode.ECB;
TDes.Padding = PaddingMode.None;
TDes.Key = key;
using (ICryptoTransform Encryptor = TDes.CreateEncryptor()) {
OutputBuffer = Encryptor.TransformFinalBlock(InputBuffer, 0, 8);
}
TDes.Clear();
return ToHex(OutputBuffer);
}
static long Decode(string value, byte[] key) {
byte[] InputBuffer = new byte[8];
byte[] OutputBuffer;
for (int i = 0; i < 8; i++) {
InputBuffer[i] = Convert.ToByte(value.Substring(i * 2, 2), 16);
}
TripleDESCryptoServiceProvider TDes = new TripleDESCryptoServiceProvider();
TDes.Mode = CipherMode.ECB;
TDes.Padding = PaddingMode.None;
TDes.Key = key;
using (ICryptoTransform Decryptor = TDes.CreateDecryptor()) {
OutputBuffer = Decryptor.TransformFinalBlock(InputBuffer, 0, 8);
}
TDes.Clear();
unsafe {
fixed (byte* pOutputBuffer = OutputBuffer) {
return ((long*)pOutputBuffer)[0];
}
}
}
static void Main(string[] args) {
long NumberToEncode = (new Random()).Next();
Console.WriteLine("Number to encode = {0}.", NumberToEncode);
byte[] Key = new byte[24];
(new RNGCryptoServiceProvider()).GetBytes(Key);
Console.WriteLine("Key to encode with is {0}.", ToHex(Key));
string EncodedValue = Encode(NumberToEncode, Key);
Console.WriteLine("The encoded value is {0}.", EncodedValue);
long DecodedValue = Decode(EncodedValue, Key);
Console.WriteLine("The decoded result is {0}.", DecodedValue);
}
}
}
The output should be something like this:
Number to encode = 873435734.
Key to encode with is 38137b6a7aa49cc6040c4297064fdb4461c79a895f40b4d1.
The encoded value is 43ba3fb809a47b2f.
The decoded result is 873435734.
Note that the encoded value is only 16 characters wide.
If you're really conserned about abuse, then AES can be used in a similar manner. In the next example I switch in AES and write the 64 bit id number into both sides of the block. If it doesn't decode with the same value on both sides then it is rejected. This can prevent people from writing in random numbers.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security.Cryptography;
namespace ConsoleApplication1 {
class Program {
static string ToHex(byte[] value) {
StringBuilder sb = new StringBuilder();
foreach (byte b in value)
sb.AppendFormat("{0:x2}", b);
return sb.ToString();
}
static string Encode(long value, byte[] key) {
byte[] InputBuffer = new byte[16];
byte[] OutputBuffer;
unsafe {
fixed (byte* pInputBuffer = InputBuffer) {
((long*)pInputBuffer)[0] = value;
((long*)pInputBuffer)[1] = value;
}
}
AesCryptoServiceProvider Aes = new AesCryptoServiceProvider();
Aes.Mode = CipherMode.ECB;
Aes.Padding = PaddingMode.None;
Aes.Key = key;
using (ICryptoTransform Encryptor = Aes.CreateEncryptor()) {
OutputBuffer = Encryptor.TransformFinalBlock(InputBuffer, 0, 16);
}
Aes.Clear();
return ToHex(OutputBuffer);
}
static bool TryDecode(string value, byte[] key, out long result) {
byte[] InputBuffer = new byte[16];
byte[] OutputBuffer;
for (int i = 0; i < 16; i++) {
InputBuffer[i] = Convert.ToByte(value.Substring(i * 2, 2), 16);
}
AesCryptoServiceProvider Aes = new AesCryptoServiceProvider();
Aes.Mode = CipherMode.ECB;
Aes.Padding = PaddingMode.None;
Aes.Key = key;
using (ICryptoTransform Decryptor = Aes.CreateDecryptor()) {
OutputBuffer = Decryptor.TransformFinalBlock(InputBuffer, 0, 16);
}
Aes.Clear();
unsafe {
fixed (byte* pOutputBuffer = OutputBuffer) {
//return ((long*)pOutputBuffer)[0];
if (((long*)pOutputBuffer)[0] == ((long*)pOutputBuffer)[1]) {
result = ((long*)pOutputBuffer)[0];
return true;
}
else {
result = 0;
return false;
}
}
}
}
static void Main(string[] args) {
long NumberToEncode = (new Random()).Next();
Console.WriteLine("Number to encode = {0}.", NumberToEncode);
byte[] Key = new byte[24];
(new RNGCryptoServiceProvider()).GetBytes(Key);
Console.WriteLine("Key to encode with is {0}.", ToHex(Key));
string EncodedValue = Encode(NumberToEncode, Key);
Console.WriteLine("The encoded value is {0}.", EncodedValue);
long DecodedValue;
bool Success = TryDecode(EncodedValue, Key, out DecodedValue);
if (Success) {
Console.WriteLine("Successfully decoded the encoded value.");
Console.WriteLine("The decoded result is {0}.", DecodedValue);
}
else
Console.WriteLine("Failed to decode encoded value. Invalid result.");
}
}
}
The result should now look something like this:
Number to encode = 1795789891.
Key to encode with is 6c90323644c841a00d40d4407e23dbb2ab56530e1a4bae43.
The encoded value is 731fceec2af2fcc2790883f2b79e9a01.
Successfully decoded the encoded value.
The decoded result is 1795789891.
Also note that since we have now used a wider block cipher the encoded value is now 32 characters wide.
You can use HttpServerUtility.UrlTokenEncode and HttpServerUtility.UrlTokenDecode
Encode uses base64 encoding, but replaces URL unfriendly characters.
There's a similar answer in a previous SO question. See the accepted answer.
So here's a working example that I put together from a few different examples that takes an integer ID and converts it to a hexidecimal formatted encrypted string. This encrypted string should not include URL-unfriendly characters and will not include escaped characters either.
Here's the entire working console app. Please note that it's a prototype and definitely not for production -- this just illustrates a solution and definitely needs to be refactored.
When you run the code, your output should be this:
1234 get encrypted as ZaB5GE/bWMJcNaeY/xJ6PQ==
ZaB5GE/bWMJcNaeY/xJ6PQ== encrypted is this in hex 5a61423547452f62574d4a634e6165592f784a3650513d3d
5a61423547452f62574d4a634e6165592f784a3650513d3d gets dehexed as ZaB5GE/bWMJcNaeY/xJ6PQ==
ZaB5GE/bWMJcNaeY/xJ6PQ== got decrypted as 1234
Sources:
byte to hex article on SO: Encryption to alphanumeric in System.Security.Cryptography
Crypto helper class: Encrypt and decrypt a string (4th answer)
Program2.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security.Cryptography;
using System.IO;
namespace ConsoleApplication1
{
class Program2
{
static void Main(string[] args)
{
int theId = 1234; //the ID that's being manipulated
byte[] byteArray; //the byte array that stores
//convert the ID to an encrypted string using a Crypto helper class
string encryptedString = Crypto.EncryptStringAES(theId.ToString(), "mysecret");
Console.WriteLine("{0} get encrypted as {1}", theId.ToString(), encryptedString);
//convert the encrypted string to byte array
byteArray = ASCIIEncoding.Default.GetBytes(encryptedString);
StringBuilder result = new StringBuilder();
//convert each byte to hex and append to a stringbuilder
foreach (byte outputByte in byteArray)
{
result.Append(outputByte.ToString("x2"));
}
Console.WriteLine("{0} encrypted is this in hex {1}", encryptedString, result.ToString());
//now reverse the process, and start with converting each char in string to byte
int stringLength = result.Length;
byte[] bytes = new byte[stringLength / 2];
for (int i = 0; i < stringLength; i += 2)
{
bytes[i / 2] = System.Convert.ToByte(result.ToString().Substring(i, 2), 16);
}
//convert the byte array to de-"hexed" string
string dehexedString = ASCIIEncoding.Default.GetString(bytes);
Console.WriteLine("{0} gets dehexed as {1}", result, dehexedString);
//decrypt the de-"hexed" string using Crypto helper class
string decryptedString = Crypto.DecryptStringAES(dehexedString, "mysecret");
Console.WriteLine("{0} got decrypted as {1}", dehexedString, decryptedString);
Console.ReadLine();
}
}
public class Crypto
{
private static byte[] _salt = Encoding.ASCII.GetBytes("o6806642kbM7c5");
/// <summary>
/// Encrypt the given string using AES. The string can be decrypted using
/// DecryptStringAES(). The sharedSecret parameters must match.
/// </summary>
/// <param name="plainText">The text to encrypt.</param>
/// <param name="sharedSecret">A password used to generate a key for encryption.</param>
public static string EncryptStringAES(string plainText, string sharedSecret)
{
if (string.IsNullOrEmpty(plainText))
throw new ArgumentNullException("plainText");
if (string.IsNullOrEmpty(sharedSecret))
throw new ArgumentNullException("sharedSecret");
string outStr = null; // Encrypted string to return
RijndaelManaged aesAlg = null; // RijndaelManaged object used to encrypt the data.
try
{
// generate the key from the shared secret and the salt
Rfc2898DeriveBytes key = new Rfc2898DeriveBytes(sharedSecret, _salt);
// Create a RijndaelManaged object
// with the specified key and IV.
aesAlg = new RijndaelManaged();
aesAlg.Key = key.GetBytes(aesAlg.KeySize / 8);
aesAlg.IV = key.GetBytes(aesAlg.BlockSize / 8);
// Create a decrytor to perform the stream transform.
ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);
// Create the streams used for encryption.
using (MemoryStream msEncrypt = new MemoryStream())
{
using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
{
using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
{
//Write all data to the stream.
swEncrypt.Write(plainText);
}
}
outStr = Convert.ToBase64String(msEncrypt.ToArray());
}
}
finally
{
// Clear the RijndaelManaged object.
if (aesAlg != null)
aesAlg.Clear();
}
// Return the encrypted bytes from the memory stream.
return outStr;
}
/// <summary>
/// Decrypt the given string. Assumes the string was encrypted using
/// EncryptStringAES(), using an identical sharedSecret.
/// </summary>
/// <param name="cipherText">The text to decrypt.</param>
/// <param name="sharedSecret">A password used to generate a key for decryption.</param>
public static string DecryptStringAES(string cipherText, string sharedSecret)
{
if (string.IsNullOrEmpty(cipherText))
throw new ArgumentNullException("cipherText");
if (string.IsNullOrEmpty(sharedSecret))
throw new ArgumentNullException("sharedSecret");
// 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 a RijndaelManaged object
// with the specified key and IV.
aesAlg = new RijndaelManaged();
aesAlg.Key = key.GetBytes(aesAlg.KeySize / 8);
aesAlg.IV = key.GetBytes(aesAlg.BlockSize / 8);
// Create a decrytor to perform the stream transform.
ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV);
// Create the streams used for decryption.
byte[] bytes = Convert.FromBase64String(cipherText);
using (MemoryStream msDecrypt = new MemoryStream(bytes))
{
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();
}
}
}
finally
{
// Clear the RijndaelManaged object.
if (aesAlg != null)
aesAlg.Clear();
}
return plaintext;
}
}
}
The problem with obfuscating the id, is that you need a way to de-obfuscicate. This requires either:
Fullblown encryption, which if it's any good will require a pretty large value.
Storing the value along with the id number, so it becomes an alternative identifier.
Something that depends on security-by-obscurity.
Alternatively, keep the id clear, but use a check as well.
public static String ChkSumStr(int id, int reduce)
{
return string.Concat(ReduceStrength(ChkSum(id), reduce).Select(b => b.ToString("X2")).ToArray());
}
public static byte[] ChkSum(int id)
{
byte[] idBytes = Encoding.UTF8.GetBytes("This is an arbitrary salt" + id);
return SHA256.Create().ComputeHash(idBytes);
}
private static byte[] ReduceStrength(byte[] src, int reduce)
{
byte[] ret = null;
for(int i = 0; i != reduce; ++i)
{
ret = new byte[src.Length / 2];
for(int j = 0; j != ret.Length; ++j)
{
ret[j] = (byte)(src[j * 2] ^ src[j * 2 + 1]);
}
src = ret;
}
return src;
}
The higher the value given for reduce, the smaller the result (until at 6 it keeps producing the empty string). A low value (or 0) gives better security, at the cost of a longer URI.
The string "This is an arbitrary salt" needs to be secret for best security. It can be hardcoded in some uses, but would want to be obtained from a secure source for others.
With the above, an id of 15 and a reduce of 3 produces a result of 05469B1E. We can then use this as:
www.domain.com/?id=15&chk=05469B1E
In the handler that would look up whatever 15 is, we do the same thing again, and if the result is different to 05469B1E we can either return a 403 Forbidden or arguably more reasonable 404 Not Found (on the basis that we've received a URI that as a whole doesn't identify anything).
Have you tried URL encoding your query string text? It's part of the HttpUtility class which:
Provides methods for encoding and
decoding URLs when processing Web
requests.
and should allow you to pass your base64 encoded text in the query string.
Do your encryption and then use HttpServerUtility.UrlTokenEncode() to encode the byte array.