I am doing this application and it depends on settings that are stored in an xml file. This file should be encrypted and the values inside it are provided by the guy responsible for creating the application setup and is used to determine available functionality options depending on the version the user installed.
I need a way to store the password hard-coded in my software to be able to decrypt that file at runtime and read the values in it to see which features of the app the user has access to.
Bear in mind that this file should not be edited and is provided as part of the software.
I haven't provided any code, because its more of a design issue than a coding issue.
I know that hard-coding a password is stupid yet I am out of options.
If you're giving the application to untrustworthy users (i.e. this is a desktop app, rather than code running on an [ASP] server that users can't access directly) then there's nothing that you can do.
If you are giving the code to the user that will decrypt a configuration file, at some point, they will be able to access that file themselves. You could make it harder, possibly even a lot harder if you put in the time/effort/money, but you can't make it impossible. Here are some things that they could do:
Decompile your program and look for the password = "12345" line of code.
Monitor the program's memory; see when it loads the XML file, and try to find the decrypted version of it in memory.
Find the section of code where you read the decrypted XML file and do some action accordingly and change the code so that it always does whatever they want regardless of what's in the file (essentially just commenting out the if check).
Some things you can do to make the above steps harder (but not impossible) include:
Obfuscating your code.
Signing your code.
Doing random pointless stuff to try to confuse would be code sniffers (for example play shell games by having 3 files, reading them all, decrypting them all, and then having 2 of them not actually be used.
Send the config file to a web service of yours to be decrypted, rather than decrypting it locally. (This can be defeated by sniffing the network for the decrypted result).
Have a web service that you query to see if the user has permissions to do what you want (again, this can be defeated by sniffing/spoofing the network connection).
Now, it might be possible to actually prevent the user from doing "something", depending on what the "something" is, by not giving them the code that does it in the first place. These would be (potentially; if coded correctly) unbreakable:
Do the work on a server.
Have a web service that does some of the sensitive work. The desktop app only manages the UI or other non-sensitive tasks. If you do this the user can only break the code you've given them.
Make the whole app a website, or other server based application (i.e. think of MMORPGS) where it simply doesn't function at all without a server; it does almost all of the sensitive (and non-sensitive) work.
Note that the only true solutions require an internet connection being available for all users when using the application; they can't be offline.
internal const string XmlPassword = "This is more like security through "+
+"obfuscation than real security. If it fits your purposes, cool, but you "+
+"might want to consider using real encryption, like public key encryption";
If I'm reading the question right - the OP needs to decrypt a file provided (already encrypted) with a 3rd party software package of some type and he has obtained the decryption password (key) from the original publisher of the software.
I would do something similar to what itsmatt suggests, only instead of encrypting the file, encrypt the provided password, store that encrypted password in your config file, then read and decrypt the password at runtime, and use it to decrypt the file.
This way you are not hardcoding a plaintext password in your source to be easily sniffed out. By keeping it in your config file, you can easily change it in the future if needed.
Answer to comment:
I use AES256.
I use this class to AES256 encrypt/decrypt:
#region Preprocessor Directives
# if __FRAMEWORK_V35 || __FRAMEWORK_V4
#define __DotNet35Plus
#endif
#if !__DotNet35Plus
#warning AES implementation used by this compile of the library is not NIST certified as FIPS 140-2 compliant
#endif
#endregion
#region Namespaces
using System;
using System.Security.Cryptography;
using System.IO;
using System.Text;
using System.Threading;
using System.Runtime.Serialization.Formatters.Binary;
#endregion
namespace Simple
{
public static class AES256Encryption
{
private static readonly object _lock = new object();
private const Int32 KeySize = 256;
#if __DotNet35Plus
private static AesCryptoServiceProvider thisCSP = new AesCryptoServiceProvider();
#else
private static RijndaelManaged thisCSP = new RijndaelManaged();
#endif
private static MemoryStream msEncrypt = new MemoryStream();
private static CryptoStream csEncrypt;
private static MemoryStream msDecrypt = new MemoryStream();
private static CryptoStream csDecrypt;
public enum stringIOType
{
base64EncodedString = 0,
HexEncodedString = 1
}
public static bool NISTCertified()
{
#if __DotNet35Plus
return true;
#else
return false;
#endif
}
#region Encryption Methods
public static byte[] encryptBytes(byte[] Value, string PassPhrase, Encoding PassPhraseEncoding)
{
try
{
Monitor.Enter(_lock);
return encryptBytes(Value, getKeyFromPassPhrase(PassPhrase, PassPhraseEncoding), getIVFromPassPhrase(PassPhrase, PassPhraseEncoding));
}
finally
{
Monitor.Exit(_lock);
}
}
public static byte[] encryptBytes(byte[] Value, byte[] Key, byte[] IV)
{
try
{
Monitor.Enter(_lock);
#if __DotNet35Plus
thisCSP = new AesCryptoServiceProvider();
#else
thisCSP = new RijndaelManaged();
#endif
thisCSP.KeySize = KeySize;
Int32 bitLength = Key.Length * 8;
if (bitLength != thisCSP.KeySize)
{
throw new ArgumentException("The supplied key's length [" + bitLength.ToString() + " bits] is not a valid key size for the AES-256 algorithm.", "Key");
}
bitLength = IV.Length * 8;
if (bitLength != thisCSP.BlockSize)
{
throw new ArgumentException("The supplied IV's length [" + bitLength.ToString() + " bits] is not a valid IV size for the AES-256 algorithm.", "IV");
}
ICryptoTransform Encryptor = thisCSP.CreateEncryptor(Key, IV);
msEncrypt = new MemoryStream();
csEncrypt = new CryptoStream(msEncrypt, Encryptor, CryptoStreamMode.Write);
csEncrypt.Write(Value, 0, Value.Length);
csEncrypt.FlushFinalBlock();
Encryptor.Dispose();
Encryptor = null;
msEncrypt.Close();
return msEncrypt.ToArray();
}
finally
{
thisCSP = null;
Monitor.Exit(_lock);
}
}
public static string encryptString(string Value, string PassPhrase, Encoding PassPhraseEncoding, Encoding inputEncoding, stringIOType outputType)
{
try
{
Monitor.Enter(_lock);
return encryptString(Value, getKeyFromPassPhrase(PassPhrase, PassPhraseEncoding), getIVFromPassPhrase(PassPhrase, PassPhraseEncoding), inputEncoding, outputType);
}
finally
{
Monitor.Exit(_lock);
}
}
public static string encryptString(string Value, byte[] Key, byte[] IV, Encoding inputEncoding, stringIOType outputType)
{
try
{
Monitor.Enter(_lock);
byte[] baseValue = (byte[])Array.CreateInstance(typeof(byte), inputEncoding.GetByteCount(Value));
baseValue = inputEncoding.GetBytes(Value);
switch(outputType)
{
case stringIOType.base64EncodedString:
return Convert.ToBase64String(encryptBytes(baseValue, Key, IV));
case stringIOType.HexEncodedString:
return ByteArrayToHexString(encryptBytes(baseValue, Key, IV));
default:
return Convert.ToBase64String(encryptBytes(baseValue, Key, IV));
}
}
finally
{
Monitor.Exit(_lock);
}
}
#endregion
#region Decryption Methods
public static byte[] decryptBytes(byte[] Value, string PassPhrase, Encoding PassPhraseEncoding)
{
try
{
Monitor.Enter(_lock);
return decryptBytes(Value, getKeyFromPassPhrase(PassPhrase, PassPhraseEncoding), getIVFromPassPhrase(PassPhrase, PassPhraseEncoding));
}
finally
{
Monitor.Exit(_lock);
}
}
public static byte[] decryptBytes(byte[] Value, byte[] Key, byte[] IV)
{
try
{
Monitor.Enter(_lock);
#if __DotNet35Plus
thisCSP = new AesCryptoServiceProvider();
#else
thisCSP = new RijndaelManaged();
#endif
thisCSP.KeySize = KeySize;
Int32 bitLength = Key.Length * 8;
if (bitLength != thisCSP.KeySize)
{
throw new ArgumentException("The supplied key's length [" + bitLength.ToString() + " bits] is not a valid key size for the AES-256 algorithm.", "Key");
}
bitLength = IV.Length * 8;
if (bitLength != thisCSP.BlockSize)
{
throw new ArgumentException("The supplied IV's length [" + bitLength.ToString() + " bits] is not a valid IV size for the AES-256 algorithm.", "IV");
}
try
{
byte[] Decrypted;
ICryptoTransform Decryptor = thisCSP.CreateDecryptor(Key, IV);
msDecrypt = new MemoryStream(Value);
csDecrypt = new CryptoStream(msDecrypt, Decryptor, CryptoStreamMode.Read);
Decrypted = (byte[])Array.CreateInstance(typeof(byte), msDecrypt.Length);
csDecrypt.Read(Decrypted, 0, Decrypted.Length);
Decryptor.Dispose();
Decryptor = null;
msDecrypt.Close();
Int32 trimCount = 0;
// Remove any block padding left over from encryption algorithm before returning
for (Int32 i = Decrypted.Length - 1; i >= 0; i--)
{
if (Decrypted[i] == 0) { trimCount++; } else { break; }
}
if (trimCount > 0)
{
byte[] buffer = (byte[])Array.CreateInstance(typeof(byte), Decrypted.Length - trimCount);
Array.ConstrainedCopy(Decrypted, 0, buffer, 0, buffer.Length);
Array.Clear(Decrypted, 0, Decrypted.Length);
Array.Resize<byte>(ref Decrypted, buffer.Length);
Array.Copy(buffer, Decrypted, buffer.Length);
buffer = null;
}
return Decrypted;
}
finally
{
thisCSP = null;
}
}
finally
{
Monitor.Exit(_lock);
}
}
public static string decryptString(string Value, string PassPhrase, Encoding PassPhraseEncoding, stringIOType inputType, Encoding outputEncoding)
{
try
{
Monitor.Enter(_lock);
return decryptString(Value, getKeyFromPassPhrase(PassPhrase, PassPhraseEncoding), getIVFromPassPhrase(PassPhrase, PassPhraseEncoding), inputType, outputEncoding);
}
finally
{
Monitor.Exit(_lock);
}
}
public static string decryptString(string Value, byte[] Key, byte[] IV, stringIOType inputType, Encoding outputEncoding)
{
try
{
Monitor.Enter(_lock);
byte[] baseValue;
switch (inputType)
{
case stringIOType.base64EncodedString:
baseValue = Convert.FromBase64String(Value);
break;
case stringIOType.HexEncodedString:
baseValue = HexStringToByteArray(Value);
break;
default:
baseValue = Convert.FromBase64String(Value);
break;
}
return outputEncoding.GetString(decryptBytes(baseValue, Key, IV));
}
finally
{
Monitor.Exit(_lock);
}
}
#endregion
#region Key/Digest Generation Methods
public static byte[] getKeyFromPassPhrase(string PassPhrase, Encoding encoder)
{
Monitor.Enter(_lock);
try
{
return getDigest(PassPhrase, encoder, 32);
}
finally
{
Monitor.Exit(_lock);
}
}
public static byte[] getIVFromPassPhrase(string PassPhrase, Encoding encoder)
{
Monitor.Enter(_lock);
try
{
byte[] buffer = (byte[])Array.CreateInstance(typeof(byte), encoder.GetByteCount(PassPhrase));
byte[] reverseBuffer = (byte[])Array.CreateInstance(typeof(byte), encoder.GetByteCount(PassPhrase));
buffer = encoder.GetBytes(PassPhrase);
for (Int32 i = 0; i <= buffer.Length - 1; i++)
{
reverseBuffer[i] = buffer[buffer.Length - i - 1];
}
return getDigest(reverseBuffer, 16);
}
finally
{
Monitor.Exit(_lock);
}
}
public static byte[] getDigest(string value, Encoding encoder, Int32 digestLength)
{
Monitor.Enter(_lock);
try
{
return getDigest(encoder.GetBytes(value), digestLength);
}
finally
{
Monitor.Exit(_lock);
}
}
public static byte[] getDigest(object value, Int32 digestLength)
{
Monitor.Enter(_lock);
try
{
BinaryFormatter bf = new BinaryFormatter();
MemoryStream ms = new MemoryStream();
bf.Serialize(ms, value);
return getDigest(ms.ToArray(), digestLength);
}
finally
{
Monitor.Exit(_lock);
}
}
public static byte[] getDigest(byte[] value, Int32 digestLength)
{
Monitor.Enter(_lock);
try
{
Int32 iterations = 0;
// Find first non-zero byte value to use to calculate iterations
for (Int32 i = 0; i < value.Length; i++)
{
if (value[i] != 0) { iterations = (Int32)(value[i] * 10); break; }
}
// There were no non-zero byte values use the max for iterations
if (iterations == 0) { iterations = (Int32)(byte.MaxValue * 10); }
Rfc2898DeriveBytes deriveBytes = new Rfc2898DeriveBytes(value, new SHA256Managed().ComputeHash(value), iterations);
return deriveBytes.GetBytes(digestLength);
}
finally
{
Monitor.Exit(_lock);
}
}
#endregion
#region HexArray/String String/HexArray Converters
public static string ByteArrayToHexString(byte[] ba)
{
try
{
Monitor.Enter(_lock);
StringBuilder hex = new StringBuilder(ba.Length * 2);
foreach (byte b in ba)
hex.AppendFormat("{0:x2}", b);
return hex.ToString();
}
finally
{
Monitor.Exit(_lock);
}
}
public static byte[] HexStringToByteArray(String hex)
{
try
{
Monitor.Enter(_lock);
int NumberChars = hex.Length;
byte[] bytes = new byte[NumberChars / 2];
for (int i = 0; i < NumberChars; i += 2)
bytes[i / 2] = Convert.ToByte(hex.Substring(i, 2), 16);
return bytes;
}
finally
{
Monitor.Exit(_lock);
}
}
#endregion
}
}
If you are using .Net Framework 3.5, or 4.0 define either __FRAMEWORK_V35 or __FRAMEWORK_V40 as a value for the preprocessor. The class used is not available for framework versions less than 3.5. If you are using an earlier framework version, just do not define the preprocessor values and an earlier class will be used.
First get the name of the file.
Then use the following to encrypt the OEM password (I'd suggest writing a little tool to do this):
string fileName = "Whatever The Filename is";
string password = "Whatever the OEM supplied password is";
string encryptedValue = Simple.AES256Encryption.encryptString(password, fileName, new UTF8Encoding(), new UTF8Encoding(), stringIOType.base64EncodedString);
Save the resulting base64 encoded string from encryptString to your config file.
To recover the OEM password:
string encryptedPassword = "This is the base64 encoded string you read from your config file";
string decrytptedPassword = Simple.AES256Encryption.decryptString(encryptedPassword, fileName, new UTF8Encoding(), stringIOType.base64EncodedString, new UTF8Encoding());
An approach would be to always encrypt the settings file based on some hash of the file. Then, your software could use the same hash as the key to decrypt the file.
So you'd basically do the following:
Get the file name (and/or some other attribute of the file - size, perhaps)
Compute the hash of the file name
Use that hash to decrypt the file
Not saying this is a good approach or an approach I'd personally use in any sort of production software but at least you're not relying upon either a plaintext or ofuscated string inside the code and it gives you the flexibility to change the settings filename and rely upon a convention for the encryption.
Again, this is only maybe a step above the "store the key in the source" solution.
Related
I have limited knowledge with PGP but came across code to use BouncyCastle to take care of it. I'm able to Encrypt/Decrypt a file but was asked to send an Encrypted and Signed file to an external vendor. When I use the following code I've found it does encrypt the file but when the vendor tries to decrypt they are getting an error gpg: Can't check signature: No public key. I don't know enough about PGP to know how to troubleshoot this properly. Looking for any suggestions to try.
Here is the code I'm using. The source and encrypted file path seem to be fine. The Public Key is pointing to the vendor's Public Key they provided me. The Private Key and Private Key Phrase are the Private Key file and password from the Key that I made for my company.
PGPEncryptDecrypt.EncryptAndSign(sourceFileFullPath,
encryptedFileFullPath,
publicKeyFileName,
privateKeyFileName,
privateKeyPhrase,
true);
This is the code in the PGPEncryptDecrypt class I pulled from an example.
#region Encrypt and Sign
public static void EncryptAndSign(string inputFile, string outputFile, string publicKeyFile, string privateKeyFile, string passPhrase, bool armor)
{
PGPEncryptionKeys encryptionKeys = new PGPEncryptionKeys(publicKeyFile, privateKeyFile, passPhrase);
if (!File.Exists(inputFile))
throw new FileNotFoundException(String.Format("Input file [{0}] does not exist.", inputFile));
if (!File.Exists(publicKeyFile))
throw new FileNotFoundException(String.Format("Public Key file [{0}] does not exist.", publicKeyFile));
if (!File.Exists(privateKeyFile))
throw new FileNotFoundException(String.Format("Private Key file [{0}] does not exist.", privateKeyFile));
if (String.IsNullOrEmpty(passPhrase))
throw new ArgumentNullException("Invalid Pass Phrase.");
if (encryptionKeys == null)
throw new ArgumentNullException("Encryption Key not found.");
using (Stream outputStream = File.Create(outputFile))
{
if (armor)
using (ArmoredOutputStream armoredOutputStream = new ArmoredOutputStream(outputStream))
{
OutputEncrypted(inputFile, armoredOutputStream, encryptionKeys);
}
else
OutputEncrypted(inputFile, outputStream, encryptionKeys);
}
}
private static void OutputEncrypted(string inputFile, Stream outputStream, PGPEncryptionKeys encryptionKeys)
{
using (Stream encryptedOut = ChainEncryptedOut(outputStream, encryptionKeys))
{
FileInfo unencryptedFileInfo = new FileInfo(inputFile);
using (Stream compressedOut = ChainCompressedOut(encryptedOut))
{
PgpSignatureGenerator signatureGenerator = InitSignatureGenerator(compressedOut, encryptionKeys);
using (Stream literalOut = ChainLiteralOut(compressedOut, unencryptedFileInfo))
{
using (FileStream inputFileStream = unencryptedFileInfo.OpenRead())
{
WriteOutputAndSign(compressedOut, literalOut, inputFileStream, signatureGenerator);
inputFileStream.Close();
}
}
}
}
}
private static void WriteOutputAndSign(Stream compressedOut, Stream literalOut, FileStream inputFile, PgpSignatureGenerator signatureGenerator)
{
int length = 0;
byte[] buf = new byte[BufferSize];
while ((length = inputFile.Read(buf, 0, buf.Length)) > 0)
{
literalOut.Write(buf, 0, length);
signatureGenerator.Update(buf, 0, length);
}
signatureGenerator.Generate().Encode(compressedOut);
}
private static Stream ChainEncryptedOut(Stream outputStream, PGPEncryptionKeys m_encryptionKeys)
{
PgpEncryptedDataGenerator encryptedDataGenerator;
encryptedDataGenerator = new PgpEncryptedDataGenerator(SymmetricKeyAlgorithmTag.TripleDes, new SecureRandom());
encryptedDataGenerator.AddMethod(m_encryptionKeys.PublicKey);
return encryptedDataGenerator.Open(outputStream, new byte[BufferSize]);
}
private static Stream ChainCompressedOut(Stream encryptedOut)
{
PgpCompressedDataGenerator compressedDataGenerator = new PgpCompressedDataGenerator(CompressionAlgorithmTag.Zip);
return compressedDataGenerator.Open(encryptedOut);
}
private static Stream ChainLiteralOut(Stream compressedOut, FileInfo file)
{
PgpLiteralDataGenerator pgpLiteralDataGenerator = new PgpLiteralDataGenerator();
return pgpLiteralDataGenerator.Open(compressedOut, PgpLiteralData.Binary, file);
}
private static PgpSignatureGenerator InitSignatureGenerator(Stream compressedOut, PGPEncryptionKeys m_encryptionKeys)
{
const bool IsCritical = false;
const bool IsNested = false;
PublicKeyAlgorithmTag tag = m_encryptionKeys.SecretKey.PublicKey.Algorithm;
PgpSignatureGenerator pgpSignatureGenerator = new PgpSignatureGenerator(tag, HashAlgorithmTag.Sha1);
pgpSignatureGenerator.InitSign(PgpSignature.BinaryDocument, m_encryptionKeys.PrivateKey);
foreach (string userId in m_encryptionKeys.SecretKey.PublicKey.GetUserIds())
{
PgpSignatureSubpacketGenerator subPacketGenerator = new PgpSignatureSubpacketGenerator();
subPacketGenerator.SetSignerUserId(IsCritical, userId);
pgpSignatureGenerator.SetHashedSubpackets(subPacketGenerator.Generate());
// Just the first one!
break;
}
pgpSignatureGenerator.GenerateOnePassVersion(IsNested).Encode(compressedOut);
return pgpSignatureGenerator;
}
#endregion Encrypt and Sign
NEW ERROR AFTER INITIAL FIX
I was able to get by the initial error of the signature check (we had the wrong keys) but now the vendor is getting the following error. Keep in mind that just Encrypting the file works but EncryptAndSign is what is giving the error. Anyone have another suggestions?
gpg: WARNING: message was not integrity protected
gpg: Hint: If this message was created before the year 2003 it is
likely that this message is legitimate. This is because back
then integrity protection was not widely used.
gpg: Use the option '--ignore-mdc-error' to decrypt anyway.
gpg: decryption forced to fail!
I am using Android and I am trying to decrypt a message encrypted in a C Sharp Server.
Below is the code for the C# Cryptor, that uses 256 bit long Keys, 128 bit long IV, 5000 Iterations. It uses Rfc2898DeriveBytes Class, so that is the same as PBKDF2WithHmacSHA1 in Android.
The decrypt function of the C# Cryptor takes as its IV the (reversed) first 128 bits of the 256 bit long key.
namespace CompanyName.Framework.Encryption
{
internal class SymmetricCryptor : ISymmetricCryptor
{
internal static int KeyLengthInBytes = 32;
internal int Iterations = 5000;
#region Private Fields
// RijndaelManaged aes; old version
AesManaged aes;
int IVLength = KeyLengthInBytes >> 1;
#endregion Private Fields
#region Internal Constructors
internal SymmetricCryptor( )
{
aes = new AesManaged
{
Mode = CipherMode.CBC,
KeySize= KeyLengthInBytes<<3,
Padding = PaddingMode.PKCS7,
};
//aes.KeySize = KeyLengthInBytes << 3;
//aes.Padding = PaddingMode.Zeros; //PKCS7 can not be used with stream
}
#endregion Internal Constructors
#region Public Methods
public byte[] Decrypt(byte[] cryptedData, string password, IVMode ivmode)
{
using (MemoryStream ms = new MemoryStream(cryptedData))
{
using (MemoryStream data = new MemoryStream())
{
Decrypt(ms, data, password,ivmode);
return data.ToArray();
}
}
}
public void Encrypt(Stream data, Stream trgStream, string password, IVMode ivmode)
{
try
{
var key = GetKey(password);
var iv = (ivmode == IVMode.Auto)
?key.GetBytes(IVLength).Reverse().ToArray()
: new byte[IVLength];
var dc = aes.CreateEncryptor(key.GetBytes(KeyLengthInBytes), iv);
using (CryptoStream cryptor = new CryptoStream(trgStream, dc, CryptoStreamMode.Write))
{
data.CopyTo(cryptor);
cryptor.FlushFinalBlock();
cryptor.Close();
}
}
catch (Exception)
{
throw new InvalidOperationException("Invalid password.");
}
}
public void Decrypt(Stream cryptedData, Stream trgStream, string password, IVMode ivmode)
{
try
{
var key= GetKey(password);
var iv = (ivmode == IVMode.Auto)
? key.GetBytes(IVLength).Reverse().ToArray()
: new byte[IVLength];
var dc = aes.CreateDecryptor(key.GetBytes(KeyLengthInBytes),iv);
using (CryptoStream cryptor = new CryptoStream(cryptedData, dc, CryptoStreamMode.Read))
{
cryptor.CopyTo(trgStream);
cryptor.Close();
}
}
catch (Exception)
{
throw new InvalidOperationException("Invalid password.");
}
}
public byte[] Encrypt(byte[] data, string password, IVMode ivmode)
{
using (MemoryStream ms = new MemoryStream(data))
{
using (MemoryStream cData = new MemoryStream())
{
Encrypt(ms, cData, password,ivmode);
return cData.ToArray();
}
}
}
#endregion Public Methods
#region Private Methods
private Rfc2898DeriveBytes GetKey(string password)
{
try
{
var iv =
CompanyName.Framework.Cryptography.Digest.SHA1.Compute(password);
return new Rfc2898DeriveBytes(password, iv, Iterations);
}
catch (Exception)
{
throw;
}
}
#endregion Private Methods
}
}
My Android Cryptor, which tries to decrypt a message encrypted by the above C Sharp Cryptor looks like this, I tried to copy the Decrypt method of the C Sharp Cryptor:
public class Cryptor {
private static final String TRANSFORMATION = "AES/CBC/PKCS7Padding";
private static final String AES = "AES";
private static final String RANDOM_ALGO = "SHA1PRNG";
private static final int KEY_LENGTH_IN_BITS = 256;
private static final int IV_LENGTH = 16;
private static final int PBE_ITERATION_COUNT = 5000;
private static final int PBE_SALT_LENGTH_INT_BITS = 128;
private static final String PBE_ALGO = "PBKDF2WithHmacSHA1";
public static byte[] generateKeyFromPassword(String password, int Size) throws GeneralSecurityException {
byte[] salt = generateSalt();
KeySpec keySpec = new PBEKeySpec(password.toCharArray(), salt, PBE_ITERATION_COUNT, Size);
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(PBE_ALGO);
byte[] data = keyFactory.generateSecret(keySpec).getEncoded();
return data;
}
private static byte[] generateSalt() throws GeneralSecurityException {
return randomBytes(PBE_SALT_LENGTH_INT_BITS);
}
private static byte[] randomBytes(int length) throws GeneralSecurityException {
SecureRandom random = SecureRandom.getInstance(RANDOM_ALGO);
byte[] b = new byte[length];
random.nextBytes(b);
return b;
}
public static byte[] decrypt(byte[] cipherText, String password) throws GeneralSecurityException {
byte[] keyBytes = generateKeyFromPassword(password, 256);
byte[] ivBytes = generateKeyFromPassword(password, 128);
Cipher cipher = Cipher.getInstance(TRANSFORMATION);
ivBytes = reverse(ivBytes);
SecretKeySpec secretKey = new SecretKeySpec(keyBytes, AES);
IvParameterSpec ivSpec = new IvParameterSpec(ivBytes);
cipher.init(Cipher.DECRYPT_MODE, secretKey, ivSpec);
byte[] decrypted = cipher.doFinal(cipherText);
return decrypted;
}
public static byte[] reverse(byte[] array) {
if (array == null) {
return null;
}
int i = 0;
int j = array.length - 1;
byte tmp;
while (j > i) {
tmp = array[j];
array[j] = array[i];
array[i] = tmp;
j--;
i++;
}
return array;
}
But it is not working, When do final is called I get a
javax.crypto.BadPaddingException: error:1e06b065:Cipher functions:EVP_DecryptFinal_ex:BAD_DECRYPT
Exception. I am not sure what I am doing wrong, because my Decrypt Method in Android is doing the exact same thing as the Decrypt Method in C Sharp: First I generate a Key from the password, which is shared by the Csharp Server and me. Then I generate a random 128 bit IV, reversing it is not necessary, but C Sharp implementation reverses it, so I do it as well. Can anyone tell me what I am doing wrong? Here is the context where I use the Cryptor:
//open the client channel, read and return the response as byte[]
Channel clientChannel = new Channel(serverAddress);
byte[] result = clientChannel.execute(serviceID.toString(), data);
//result[] is encrypted data. firstTen is the shared Password
byte[] decrypted = Cryptor.decrypt(result, firstTen);
Server returns the result as Base64 encrypted, before passing it for decryption I get the result[] array through:
It comes as a Base64 String. I get the result[] array through:
Base64.decode(result, Base64.NO_WRAP);
You need to generate random salt and IV on Server side and sent it with ciphreText to Android side. Android need to use exactly the same salt and IV to derive key for decryption that that was used to derive encryption key on Server side.
I'm writing a Windows app in C# which has to interact with a Mac app (written in Cocoa). This app encrypts files in AES with CBC (IV, a key, salt, HMAC). I don't know a lot about encryption but I think that's what it does. The Cocoa library we use is RNCryptor. They have a C# version which is what I'm using on the Windows side (with a few modifications, mainly to use byte[] instead of Strings).
Now text files are decrypted correctly, but other files (for example, a PNG file), end up corrupted (the correct file on the right, and the corrupted on the left, using UTF8 encoding, you can see there's a lot of diamonds with ?s):
I believe this is due to the encoding of the file, but I tried UTF8, Default, Unicode, ASCII... and the output files are always corrupted with different file sizes, being ASCII and the default encoding (UTF16 I believe) the closest in size.
This is the RNCryptor modified code I used:
public byte[] Decrypt (byte[] encryptedBase64, string password)
{
PayloadComponents components = this.unpackEncryptedBase64Data (encryptedBase64);
if (!this.hmacIsValid (components, password)) {
return null;
}
byte[] key = this.generateKey (components.salt, password);
byte[] plaintextBytes = new byte[0];
switch (this.aesMode) {
case AesMode.CTR:
// Yes, we are "encrypting" here. CTR uses the same code in both directions.
plaintextBytes = this.encryptAesCtrLittleEndianNoPadding(components.ciphertext, key, components.iv);
break;
case AesMode.CBC:
plaintextBytes = this.decryptAesCbcPkcs7(components.ciphertext, key, components.iv);
break;
}
return plaintextBytes;
}
private byte[] decryptAesCbcPkcs7 (byte[] encrypted, byte[] key, byte[] iv)
{
var aes = Aes.Create();
aes.Mode = CipherMode.CBC;
aes.Padding = PaddingMode.PKCS7;
var decryptor = aes.CreateDecryptor(key, iv);
string plaintext;
using (MemoryStream msDecrypt = new MemoryStream(encrypted))
{
using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
{
using (StreamReader srDecrypt = new StreamReader(csDecrypt))
{
plaintext = srDecrypt.ReadToEnd();
}
}
}
return Encoding.UTF8.GetBytes(plaintext);
}
private PayloadComponents unpackEncryptedBase64Data (byte[] encryptedBase64)
{
List<byte> binaryBytes = new List<byte>();
binaryBytes.AddRange (encryptedBase64);
PayloadComponents components;
int offset = 0;
components.schema = binaryBytes.GetRange(0, 1).ToArray();
offset++;
this.configureSettings ((Schema)binaryBytes [0]);
components.options = binaryBytes.GetRange (1, 1).ToArray();
offset++;
components.salt = binaryBytes.GetRange (offset, Cryptor.saltLength).ToArray();
offset += components.salt.Length;
components.hmacSalt = binaryBytes.GetRange(offset, Cryptor.saltLength).ToArray();
offset += components.hmacSalt.Length;
components.iv = binaryBytes.GetRange(offset, Cryptor.ivLength).ToArray();
offset += components.iv.Length;
components.headerLength = offset;
components.ciphertext = binaryBytes.GetRange (offset, binaryBytes.Count - Cryptor.hmac_length - components.headerLength).ToArray();
offset += components.ciphertext.Length;
components.hmac = binaryBytes.GetRange (offset, Cryptor.hmac_length).ToArray();
return components;
}
private bool hmacIsValid (PayloadComponents components, string password)
{
byte[] generatedHmac = this.generateHmac (components, password);
if (generatedHmac.Length != components.hmac.Length) {
return false;
}
for (int i = 0; i < components.hmac.Length; i++) {
if (generatedHmac[i] != components.hmac[i]) {
return false;
}
}
return true;
}
And this is my code decrypting and writing the file:
byte[] decryptedFile = this.decryptor.Decrypt(File.ReadAllBytes(filePath), password);
File.WriteAllBytes(filePath, decryptedFile);
What can be wrong here? Thanks in advance.
The problem is in your use of StreamReader when decrypting. StreamReader reads text (UTF-8 here), not arbitrary binary data. One solution would be to read the data into a MemoryStream, and use its ToArray() method to get the resulting byte[].
I have created a few little programs that export data to a text file using StreamWriter and then I read them back in using StreamReader. This works great and does what I need it to do but I was wondering if there was a way that I could save this information without the user being able to access or modify it either intentionally or unintentionally. An example of something I would have in a text file would be if a checkbox was ticked, when you tick it it outputs "Ticked" to a text file, when the program is re - opened I know what state the form was in when it was closed. I obviously don't want to keep using text files. Does anyone have any ideas on how I can easily store this information without the user being able to modify it? Thank you very much.
The simplest way is to Base-64 encode/decode this text. This is not secure, but will prevent a casual user from modifying the data.
static public string EncodeTo64(string toEncode)
{
byte[] toEncodeAsBytes
= System.Text.ASCIIEncoding.ASCII.GetBytes(toEncode);
string returnValue
= System.Convert.ToBase64String(toEncodeAsBytes);
return returnValue;
}
static public string DecodeFrom64(string encodedData)
{
byte[] encodedDataAsBytes
= System.Convert.FromBase64String(encodedData);
string returnValue =
System.Text.ASCIIEncoding.ASCII.GetString(encodedDataAsBytes);
return returnValue;
}
EDIT: Real encryption
#region Encryption
string passPhrase = "Pasword"; // can be any string
string saltValue = "sALtValue"; // can be any string
string hashAlgorithm = "SHA1"; // can be "MD5"
int passwordIterations = 7; // can be any number
string initVector = "~1B2c3D4e5F6g7H8"; // must be 16 bytes
int keySize = 256; // can be 192 or 128
private string Encrypt(string data)
{
byte[] bytes = Encoding.ASCII.GetBytes(this.initVector);
byte[] rgbSalt = Encoding.ASCII.GetBytes(this.saltValue);
byte[] buffer = Encoding.UTF8.GetBytes(data);
byte[] rgbKey = new PasswordDeriveBytes(this.passPhrase, rgbSalt, this.hashAlgorithm, this.passwordIterations).GetBytes(this.keySize / 8);
RijndaelManaged managed = new RijndaelManaged();
managed.Mode = CipherMode.CBC;
ICryptoTransform transform = managed.CreateEncryptor(rgbKey, bytes);
MemoryStream stream = new MemoryStream();
CryptoStream stream2 = new CryptoStream(stream, transform, CryptoStreamMode.Write);
stream2.Write(buffer, 0, buffer.Length);
stream2.FlushFinalBlock();
byte[] inArray = stream.ToArray();
stream.Close();
stream2.Close();
return Convert.ToBase64String(inArray);
}
private string Decrypt(string data)
{
byte[] bytes = Encoding.ASCII.GetBytes(this.initVector);
byte[] rgbSalt = Encoding.ASCII.GetBytes(this.saltValue);
byte[] buffer = Convert.FromBase64String(data);
byte[] rgbKey = new PasswordDeriveBytes(this.passPhrase, rgbSalt, this.hashAlgorithm, this.passwordIterations).GetBytes(this.keySize / 8);
RijndaelManaged managed = new RijndaelManaged();
managed.Mode = CipherMode.CBC;
ICryptoTransform transform = managed.CreateDecryptor(rgbKey, bytes);
MemoryStream stream = new MemoryStream(buffer);
CryptoStream stream2 = new CryptoStream(stream, transform, CryptoStreamMode.Read);
byte[] buffer5 = new byte[buffer.Length];
int count = stream2.Read(buffer5, 0, buffer5.Length);
stream.Close();
stream2.Close();
return Encoding.UTF8.GetString(buffer5, 0, count);
}
#endregion
You should call ProtectedData.Protect to encrypt the data using a per-user key.
Note that it wouldn't be very hard for a skilled user to decrypt and modify the data.
Anything that your program does on the user's machine can be done by the user too.
You can add a checksum or hash to the file - if the file contents doesn't agree with the checksum, you know it was tampered with.
If it is important that users can't read the contents of the file, you can encrypt it.
I don't believe you can make a file that can't be tampered with (a savvy user could use a hex editor and change it, for example) - the best you can do is detect such tampering.
You can use the Ionic zip libraries to zip those text files. If necessary you could also use features of Ionic zip like password protection and encryption. And you'll still be able to open the file (with zipping applications like, for example, 7zip) manually yourself using the same settings you used to create it in the first place.
If a program can access the information, a user usually can too. However you can produce data the user will not immediately understand.
I would start by creating a class that holds all state information you want to save, isolating the problem. Coincidentally, the BinaryFormatter class will then allow you to easily save and load this class to/from a file. I don't know if it's results are "unreadable enough" - if not, apply Base64 encoding like Leon mentioned.
While you could base64 encode or even fully encrypt your configuration data (with SHA1 or MD5) as already suggested, I think good practice would be to work with the framework classes dealing with configuration data (Configuration under the System.Configuration namespace) and it's built in ability to encrypt data (via the ProtectSection method of the ConfigurationSection class).
First of all you should declare and initialize an instance:
using System.Configuration;
...
static void Main(string[] args)
{
Configuration config;
config = ConfigurationManager.OpenExeConfiguration(/*path to config file*/); //Use ConfigurationManager.OpenMachineConfiguration(/*path to config file*/) when opening machine configuration
...
After that you need to define a custom configuration section that defines your configuration (msdn example)
Once you've done that you just need to initialize an instance of your custom configuration section and add it to the configuration file using this code:
isTicked = config.Sections.Add("isTicked", customSection);
To encrypt the section you just added use this code (with further examples in both VB.NET and C# found here):
config.Sections["isTicked"].SectionInformation.ProtectSection("protection provider");
The "DPAPIProtectedConfigurationProvider" and "RSAProtectedConfigurationProvider" are built in by default.
Once you want to decrypt the section use this code:
config.Sections["isTicked"].SectionInformation.UnprotectSection();
To stress a point - encryption and decryption both take effect only after you save the configuration file
To save the file, use the code:
config.Save(); //config.SaveAs("string") is also available
Further information about the relevant classes and methods can be found in the msdn, starting with the Configuration class page linked above.
Try this code to encrypt and decrypt your text!
It is quite easy and strong I think...
public static class Crypto
{
private static readonly byte[] IVa = new byte[] { 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x11, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17 };
public static string Encrypt(this string text, string salt)
{
try
{
using (Aes aes = new AesManaged())
{
Rfc2898DeriveBytes deriveBytes = new Rfc2898DeriveBytes(Encoding.UTF8.GetString(IVa, 0, IVa.Length), Encoding.UTF8.GetBytes(salt));
aes.Key = deriveBytes.GetBytes(128 / 8);
aes.IV = aes.Key;
using (MemoryStream encryptionStream = new MemoryStream())
{
using (CryptoStream encrypt = new CryptoStream(encryptionStream, aes.CreateEncryptor(), CryptoStreamMode.Write))
{
byte[] cleanText = Encoding.UTF8.GetBytes(text);
System.Diagnostics.Debug.WriteLine(String.Concat("Before encryption text data size: ", text.Length.ToString()));
System.Diagnostics.Debug.WriteLine(String.Concat("Before encryption byte data size: ", cleanText.Length.ToString()));
encrypt.Write(cleanText, 0, cleanText.Length);
encrypt.FlushFinalBlock();
}
byte[] encryptedData = encryptionStream.ToArray();
string encryptedText = Convert.ToBase64String(encryptedData);
System.Diagnostics.Debug.WriteLine(String.Concat("Encrypted text data size: ", encryptedText.Length.ToString()));
System.Diagnostics.Debug.WriteLine(String.Concat("Encrypted byte data size: ", encryptedData.Length.ToString()));
return encryptedText;
}
}
}
catch(Exception e)
{
return String.Empty;
}
}
public static string Decrypt(this string text, string salt)
{
try
{
using (Aes aes = new AesManaged())
{
Rfc2898DeriveBytes deriveBytes = new Rfc2898DeriveBytes(Encoding.UTF8.GetString(IVa, 0, IVa.Length), Encoding.UTF8.GetBytes(salt));
aes.Key = deriveBytes.GetBytes(128 / 8);
aes.IV = aes.Key;
using (MemoryStream decryptionStream = new MemoryStream())
{
using (CryptoStream decrypt = new CryptoStream(decryptionStream, aes.CreateDecryptor(), CryptoStreamMode.Write))
{
byte[] encryptedData = Convert.FromBase64String(text);
System.Diagnostics.Debug.WriteLine(String.Concat("Encrypted text data size: ", text.Length.ToString()));
System.Diagnostics.Debug.WriteLine(String.Concat("Encrypted byte data size: ", encryptedData.Length.ToString()));
decrypt.Write(encryptedData, 0, encryptedData.Length);
decrypt.Flush();
}
byte[] decryptedData = decryptionStream.ToArray();
string decryptedText = Encoding.UTF8.GetString(decryptedData, 0, decryptedData.Length);
System.Diagnostics.Debug.WriteLine(String.Concat("After decryption text data size: ", decryptedText.Length.ToString()));
System.Diagnostics.Debug.WriteLine(String.Concat("After decryption byte data size: ", decryptedData.Length.ToString()));
return decryptedText;
}
}
}
catch(Exception e)
{
return String.Empty;
}
}
}
Just to add another implementation of Leon's answer, and following the
Microsoft docs
Here a class example that encrypts and decrypts strings
public static class EncryptionExample
{
#region internal consts
internal const string passPhrase = "pass";
internal const string saltValue = "salt";
internal const string hashAlgorithm = "MD5";
internal const int passwordIterations = 3; // can be any number
internal const string initVector = "0123456789abcdf"; // must be 16 bytes
internal const int keySize = 64; // can be 192 or 256
#endregion
#region public static Methods
public static string Encrypt(string data)
{
string res = string.Empty;
try
{
byte[] bytes = Encoding.ASCII.GetBytes(initVector);
byte[] rgbSalt = Encoding.ASCII.GetBytes(saltValue);
byte[] buffer = Encoding.UTF8.GetBytes(data);
byte[] rgbKey = new PasswordDeriveBytes(passPhrase, rgbSalt, hashAlgorithm, passwordIterations).GetBytes(keySize / 8);
RijndaelManaged managed = new RijndaelManaged();
managed.Mode = CipherMode.CBC;
ICryptoTransform transform = managed.CreateEncryptor(rgbKey, bytes);
byte[] inArray = null;
using (MemoryStream msEncrypt = new MemoryStream())
{
using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, transform, CryptoStreamMode.Write))
{
csEncrypt.Write(buffer, 0, buffer.Length);
csEncrypt.FlushFinalBlock();
inArray = msEncrypt.ToArray();
res = Convert.ToBase64String(inArray);
}
}
}
catch (Exception ex)
{
Console.WriteLine("Encrypt " + ex);
}
return res;
}
public static string Decrypt(string data)
{
string res = string.Empty;
try
{
byte[] bytes = Encoding.ASCII.GetBytes(initVector);
byte[] rgbSalt = Encoding.ASCII.GetBytes(saltValue);
byte[] buffer = Convert.FromBase64String(data);
byte[] rgbKey = new PasswordDeriveBytes(passPhrase, rgbSalt, hashAlgorithm, passwordIterations).GetBytes(keySize / 8);
RijndaelManaged managed = new RijndaelManaged();
managed.Mode = CipherMode.CBC;
ICryptoTransform transform = managed.CreateDecryptor(rgbKey, bytes);
using (MemoryStream msEncrypt = new MemoryStream(buffer))
{
using (CryptoStream csDecrypt = new CryptoStream(msEncrypt, transform, CryptoStreamMode.Read))
{
using (StreamReader srDecrypt = new StreamReader(csDecrypt))
{
res = srDecrypt.ReadToEnd();
}
}
}
}
catch (Exception ex)
{
Console.WriteLine("Decrypt " + ex);
}
return res;
}
}
By the way, here is the "salt value" definition that I had googled to find out what it was.
Salt value
If an attacker does not know the password, and is trying to guess it with a brute-force attack, then every password he tries has to be tried with each salt value. So, for a one-bit salt (0 or 1), this makes the encryption twice as hard to break in this way.
Preventing unintentional string modification can be done using a checksum, as pointed in this answer.
However, it's quite easy to generate such a checksum, as they are not that many widely used algorithms.
Thus that doesn't protect you against intentional modification.
To prevent that, people use digital signatures. That allows anyone to verify your data hasn't be tampered, but only you (the owner of the private secret) can generate the signature.
Here is an example in C#.
However, as others pointed out, you need to embed your private key somewhere in your binary, and a (not so) skilled programmer will be able to retrieve it, even if you obfuscate your .net dll or you make that in a separate native process.
That would be enough for most concerns though.
If you are really concerned by security, then you need to move on the cloud, and execute the code on a machine you own.
I believe when the EnterpriseLibrary tries to decrypt a RijndaelManaged encrypted string it expects the Initialization Vector to be prepended to the encrypted text. Currently with the code below. I can decrypt the message with out an exception, but I am getting weird characters like:
�猀漀椀搀㴀眀最爀甀戀攀☀甀琀挀㴀㈀ ⴀ ⴀ㈀吀㌀㨀㔀㈀㨀㌀
What do I need to do to make this work? Any help is greatly appreciated. Here is some of the code I have...
I have a C# application that decrypts data using the EnterpriseLibrary 4.1 (encryption: RijndaelManaged).
string message = "This encrypted message comes from Java Client";
Cryptographer.DecryptSymmetric("RijndaelManaged", message);
The client encryptes the message, implemented in Java.
public String encrypt(String auth) {
try {
String cipherKey = "Key as a HEX string";
byte[] rawKey = hexToBytes(cipherKey);
SecretKeySpec keySpec = new SecretKeySpec(rawKey, "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
String cipherIV = "xYzF5AqA2cKLbvbfGzsMwg==";
byte[] btCipherIV = Base64.decodeBase64(cipherIV.getBytes());
cipher.init(Cipher.ENCRYPT_MODE, keySpec, new IvParameterSpec (btCipherIV));
byte[] unencrypted = StringUtils.getBytesUtf16(auth);
byte[] encryptedData = cipher.doFinal(unencrypted);
String encryptedText = null;
byte[] entlib = new byte[btCipherIV2.length + encryptedData.length];
System.arraycopy(btCipherIV, 0, entlib, 0, btCipherIV.length);
System.arraycopy(encryptedData, 0, entlib, btCipherIV.length, encryptedData.length);
encryptedText = new String(encryptedData);
encryptedText = Base64.encodeBase64String(encryptedData);
return encryptedText;
} catch (Exception e) {
}
return "";
}
public static byte[] hexToBytes(String str) {
if (str==null) {
return null;
} else if (str.length() < 2) {
return null;
} else {
int len = str.length() / 2;
byte[] buffer = new byte[len];
for (int i=0; i<len; i++) {
buffer[i] = (byte) Integer.parseInt(
str.substring(i*2,i*2+2),16);
}
return buffer;
}
}
I found the answer. The problem in the above code:
StringUtils.getBytesUtf16(auth);
Instead the Enterprise Library is using Little Endian byte order. The function I was using doesn't. Instead I should have used:
StringUtils.getBytesUtf16Le(auth);
This solved my problem. Thanks for anyone who took a loot at this. I appreciate it!