I know that we can define the key explicitly using the below line. For 3des the key length should be 24 bytes, if I am not wrong.
Dim Newkey() As Byte = Convert.FromBase64String("24 bytes enter here")
something like
Dim Newkey() As Byte = Convert.FromBase64String("c:\temp\mykey.pem")
How do i make sure that the file gives back 24 bytes of data, for 3des encryption?
What/How do i generate such a file?
Simply check the length of the array using NewKey.Length after decoding
Use TripleDES.GenerateKey, retrieve the key property. This will generate keys that should be compatible with TripleDES, then call Converter.ToBase64String
Please look up a tutorial of creating/reading textual files yourself, using FromBase64String on a file name won't work.
http://msdn.microsoft.com/en-us/library/system.security.cryptography.symmetricalgorithm.generatekey.aspx#Y0
Note that 16 byte keys may be used for TripleDES (ABA keys) as well as 24 byte (ABC) keys. Also note that there are weak keys in DES, and that keys include parity bits. Most implementations simply ignore the parity bits, but it is better to use a special key generation function to be sure they are set correctly.
using System;
using System.Text;
using System.Security.Cryptography;
namespace Crypto
{
public class KeyCreator
{
public static void Main(String[] args)
{
String[] commandLineArgs = System.Environment.GetCommandLineArgs();
string decryptionKey = CreateKey(System.Convert.ToInt32(commandLineArgs[1]));
string validationKey = CreateKey(System.Convert.ToInt32(commandLineArgs[2]));
Console.WriteLine("<machineKey validationKey=\"{0}\" decryptionKey=\"{1}\" validation=\"SHA1\"/>", validationKey, decryptionKey);
}
static String CreateKey(int numBytes)
{
RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
byte[] buff = new byte[numBytes];
rng.GetBytes(buff);
return BytesToHexString(buff);
}
static String BytesToHexString(byte[] bytes)
{
StringBuilder hexString = new StringBuilder(64);
for (int counter = 0; counter < bytes.Length; counter++)
{
hexString.Append(String.Format("{0:X2}", bytes[counter]));
}
return hexString.ToString();
}
}
}
Related
I am working with encryption using AES. My customer is encrypting some of the sensitive data while posting the data to my web API. And my code will decrypt these fields before insert them to the database.
Originally we agree to use a fixed secret key. Below is the code:
public class AESEncryptor {
private static final String ALGO = "AES";
private static final String keyVal = "!5Po4#j82Adsu39/*na3n5";
public static String encrypt(String data) {
try {
Key key = genKey();
Cipher c = Cipher.getInstance(ALGO);
c.init(Cipher.ENCRYPT_MODE, key);
byte[] encVal = c.doFinal(data.getBytes());
return Base64.encodeBase64String(encVal);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public static String decrypt (String encryptedData) throws Exception{
Key key = genKey();
Cipher c = Cipher.getInstance(ALGO);
c.init(Cipher.DECRYPT_MODE, key);
byte[] data = Base64.decodeBase64(encryptedData);
byte[] decByptes = c.doFinal(data);
return new String(decByptes);
}
private static Key genKey() throws Exception {
fixKeyLength();
return new SecretKeySpec(keyVal.getBytes(), ALGO);
}
}
Then the other party suggested we should switch to KeyGenerator to generate a random secure key. Something like the following.
KeyGenerator keyGen = KeyGenerator.getInstance("AES");
keyGen.init(256);
SecretKey key = keyGen.generateKey();
final byte[] nonce = new byte[32];
SecureRandom random = SecureRandom.getInstanceStrong();
random.nextBytes(nonce);
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
GCMParameterSpec spec = new GCMParameterSpec(16 * 8, nonce);
cipher.init(Cipher.ENCRYPT_MODE, key, spec);
I am not sure that is possible. Because the correct decryption relies on the same key for encryption. If the key is random, how would my API know what key to use every time? Or is there a solution to handle this situation?
There is no solution to handle this problem. Symmetric encryption requires that both parties know the key in order to encrypt and decrypt. If the key is random each time, then you need a way to communicate the key.
The scheme you have designed is quite poor, since a fixed key means that the key being compromised will bring down the whole system. You're also using ECB mode, which is inherently insecure. No authentication either.
If you want to communicate data securely from one party to another, use TLS with client authentication. This is the industry standard way to solve this problem and you don't have to get your hands dirty with the crypto.
I have some account info that is being encrypted and written to a file like this:
//imports here
public class Main
public static void main(String[] args) {
try {
String text = "this text will be encrypted";
String key = "Bar12345Bar12345";
//Create key and cipher
Key aesKey = new SecretKeySpec(key.getBytes(), "AES");
Cipher cipher = Cipher.getInstance("AES");
//encrypt text
cipher.init(Cipher.ENCRYPT_MODE, aesKey);
byte[] encrypted = cipher.doFinal(text.getBytes());
write(new String(encrypted));
} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | IllegalBlockSizeException | BadPaddingException e) {
e.printStackTrace();
}
}
public static void write(String message) {
BufferedWriter bw = null;
FileWriter fw = null;
try {
String data = message;
File file = new File(FILENAME);
if (!file.exists()) {
file.createNewFile();
}
fw = new FileWriter(file.getAbsoluteFile(), true);
bw = new BufferedWriter(fw);
bw.write(data);
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (bw != null)
bw.close();
if (fw != null)
fw.close();
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
}
So the contents of the file is one string without any breaks in between. If I wanted to decrypt the string, I would do this:
String key = "Bar12345Bar12345";
Key aesKey = new SecretKeySpec(key.getBytes(), "AES");
Cipher cipher = Cipher.getInstance("AES");
byte[] encrypted = text.getBytes();
cipher.init(Cipher.DECRYPT_MODE, aesKey);
String decrypted = new String(cipher.doFinal(encrypted));
System.err.println(decrypted);
This works fine as long as byte[] encrypted is the same as used as in the encrypting process, but when I try to read the encrypted text from the file using a FileReader and a BufferedReader and change it into a byte using lines.getByte() it throws the exception
javax.crypto.IllegalBlockSizeException: Input length must be multiple of 16 when decrypting with padded cipher
Compare the cipher text from the encrypting process with the cipher text from lines.getByte() before you try to do any decryption. They are most likely different. Try reading the entire file into a byte[] first before decrypting it. Symmetric ciphers need to do their work on blocks of the same size, in this case 16 bytes.
I would also be remiss if I didn't comment on some of the poor protocol choices.
Hard coded key - Your encryption key should never be hard-coded in your application. If you ever need to change your encryption key, you are not able to do so. If your application is distributed to end users, it's easy for them to recover the key using an application like strings.
You are using ECB as your mode of operation. ECB has several ways it can be attacked, such as block shuffling, which can allow an attacker to decrypt data without having the encryption key.
Encryption is hard to get right on your own. Consider solving your problem a different way.
You are trying to treat your encrypted array contents as (platform encoded) text, while it is not - it consists of random bytes.
You either need to create a binary file by writing the contents of encrypted to it directly. That or you can create a text file by first encoding encrypted to base64.
Currently you are trying to read lines, but there aren't any. And if there are some line endings in there they will be stripped from the ciphertext before those bytes can be decrypted.
If you perform new String(encrypted) then it is also possible that you lose part of your data, as unsupported encodings are removed from the string without warning.
Note that the word "ciphertext" is a bit misleading; modern ciphers such as AES handle binary data, not text.
I would like to use encryption to send traffic to my F5 BIG IP load balancer and have it use its own native CRYPTO:: methods to decrypt a base64 encoded string.
I am able to encrypt and decrypt a string within the appliance and within a Visual Studio 2012 console application but I cannot decrypt an encrypted string in the opposing environment.
Any suggestion here as to how to get the following keys in a compatible format that CRYPTO or C# understands would go a long way!
// C# key and vector declaration:
private const string AesIV = #"!QAZ2WSX#EDC4RFV";
private const string AesKey = #"5TGB&YHN7UJM(IK<";
It appears that in CRYPTO:: it needs it in hex format, I tried to convert it as seen below but that didnt help me.
C# console app code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.Security.Cryptography;
using System.Threading;
namespace ssoconsole_encrypt
{
class Program
{
private const string AesIV = #"!QAZ2WSX#EDC4RFV";
private const string AesKey = #"5TGB&YHN7UJM(IK<";
// set key "abed1ddc04fbb05856bca4a0ca60f21e"
//set iv "d78d86d9084eb9239694c9a733904037"
// set key "56bca4a0ca60f21e"
// set iv "39694c9a73390403"
/// <summary>
/// AES Encryption
/// </summary>
///
static public string Encrypt(string text)
{
// AesCryptoServiceProvider
AesCryptoServiceProvider aes = new AesCryptoServiceProvider();
aes.BlockSize = 128;
aes.KeySize = 256;
aes.IV = Encoding.UTF8.GetBytes(AesIV);
aes.Key = Encoding.UTF8.GetBytes(AesKey);
string keyme = BitConverter.ToString(aes.Key);
string ivme = BitConverter.ToString(aes.IV);
Console.WriteLine(string.Format("The converted key is: {0}",keyme));
Console.WriteLine(string.Format("The converted iv is: {0}", ivme));
Console.WriteLine(System.Text.Encoding.UTF8.GetString(aes.Key));
// Thread.Sleep(10000);
//Console.WriteLine(aes.Key.ToString());
aes.Mode = CipherMode.CBC;
aes.Padding = PaddingMode.PKCS7;
// aes.Padding = PaddingMode.Zeros;
// Convert string to byte array
//byte[] src = Encoding.Unicode.GetBytes(text);
byte[] src = Encoding.UTF8.GetBytes(text);
// encryption
using (ICryptoTransform encrypt = aes.CreateEncryptor())
{
byte[] dest = encrypt.TransformFinalBlock(src, 0, src.Length);
// Convert byte array to Base64 strings
return Convert.ToBase64String(dest);
}
}
/// <summary>
/// AES decryption
/// </summary>
static public string Decrypt(string text)
{
// AesCryptoServiceProvider
AesCryptoServiceProvider aes = new AesCryptoServiceProvider();
aes.BlockSize = 128;
aes.KeySize = 256;
aes.IV = Encoding.UTF8.GetBytes(AesIV);
aes.Key = Encoding.UTF8.GetBytes(AesKey);
//aes.IV = Encoding.UTF8.GetBytes(#"01020304050607080900010203040506");
//aes.Key = Encoding.UTF8.GetBytes(#"01020304050607080900010203040506");
aes.Mode = CipherMode.CBC;
aes.Padding = PaddingMode.PKCS7;
// Convert Base64 strings to byte array
byte[] src = System.Convert.FromBase64String(text);
try
{
// decryption
using (ICryptoTransform decrypt = aes.CreateDecryptor())
{
byte[] dest = decrypt.TransformFinalBlock(src, 0, src.Length);
// return Encoding.Unicode.GetString(dest);
return Encoding.UTF8.GetString(dest);
}
}
catch (CryptographicException e)
{
return e.ToString();
}
}
static void Main()
{
string username = "jschoombee";
string encrypted = Encrypt(username);
string decrypted = Decrypt(encrypted);
// string decrypted = Decrypt("epvhTN55JnnVV9DBn1Cbsg==");
// string decrypted = Decrypt(encrypted);
Console.WriteLine(string.Format("jschoombee encrypted is : {0}",encrypted));
Console.WriteLine(string.Format("the Decrypted username for jp is : {0}", decrypted));
Thread.Sleep(1000000);
}
}
}
This is the Console Output:
The converted key is: 35-54-47-42-26-59-48-4E-37-55-4A-4D-28-49-4B-3C
The converted iv is: 21-51-41-5A-32-57-53-58-23-45-44-43-34-52-46-56
5TGB&YHN7UJM(IK<
jschoombee encrypted is : tGG9Un6VqcAOTQawlxwRXg==
the Decrypted username for jp is : jschoombee
This it the F5 / TCL code:
when RULE_INIT {
set static::hexkey "355447422659484E37554A4D28494B3C"
log local0.info"====Rule_Init===="
log local0.info "Key is $static::hexkey"
log local0.info"================="
}
when HTTP_REQUEST_DATA {
set iv "2151415A325753582345444334524656"
set text_to_encrypt "jschoombee"
set enc_out_no_binary [CRYPTO::encrypt -alg aes-256-cbc -keyhex $static::hexkey -ivhex $iv $text_to_encrypt]
set dec_in [CRYPTO::decrypt -alg aes-256-cbc -keyhex $static::hexkey -ivhex $iv $enc_out_no_binary]
log local0.info "The decrypted NO binary $dec_in"
log local0.info "The Encrypted NO binary Base64 is: [b64encode "$enc_out_no_binary"]"
binary scan $enc_out_no_binary H* enc_hex
log local0.info "The Encrypted NO binary Hex is: $enc_hex"
log local0.info "This is the IV $iv"
HTTP::release
}
The F5/TCL Output Log File:
Feb 11 13:05:45 AMS4-LB-01 info tmm1[9650]: <HTTP_REQUEST_DATA>: The decrypted NO binary jschoombee
Feb 11 13:05:45 AMS4-LB-01 info tmm1[9650]: <HTTP_REQUEST_DATA>: The Encrypted NO binary Base64 is: Rlz4cC9SlpRyON4cZI+dtQ==
Feb 11 13:05:45 AMS4-LB-01 info tmm1[9650]: <HTTP_REQUEST_DATA>: The Encrypted NO binary Hex is: 465cf8702f5296947238de1c648f9db5
Feb 11 13:05:45 AMS4-LB-01 info tmm1[9650]: <HTTP_REQUEST_DATA>: This is the IV 2151415A325753582345444334524656
There are some very strange things happening in your code regarding the key and IV.
First of all the key you've specified is 16 characters. In UTF-8 those result in 16 bytes. You are however specifying a key of 32 bytes (256 bits) in your C# code. Also be warned that many libraries (incorrectly) use AES-256 to mean Rijndael with a 256 bit block size. It's probably better to just use AES-128 and focus on making your protocol and code secure.
Second, a key can never be a character string. A character string normally is restricted with regards to which values can be used. E.g. control codes cannot be entered. This means that your key will never reach its intended strength. If you want to use a static key, you should specify it in hexadecimals as you do in your F5 code.
A static IV does not make much sense. The whole idea of the IV is to make sure that you will generate a different ciphertext if you encrypt a block with a value already processed. So please use a random IV, and place it in front of your ciphertext.
You seem to have the hang on using encoding/decoding on your plaintext (UTF-8) and ciphertext (Base 64). So please try and follow the advice given above and try again.
I need to encrypt some files in ASP.net and decrypt them in a flash application built with Action Script 3.
AS3 developer found a lib call AS3crypto which seems like a good one for AS3. The idea is encrypt and decrypt using same key. Symmetrical Encryption?
But I am struggling to find .Net equivalent that would use same algorithm for encryption.
I have tried RC4 example from 4guysfromrolla blog which works too slow for me.
I have tried AES on this example (http://msdn.microsoft.com/en-us/library/system.security.cryptography.rijndaelmanaged(v=vs.100).aspx) which works great on .Net but I can't seem to decrypt using AS3crypto to get the same file back. AS3crypto doesn't seem to like to have IV for decryption. I can only supply one key.
So far I am lost. How can I encrypt a file in .Net and decrypt it back in AS3 to get the same file back?
Notice: use 16 char length for both Key and IV, ex: Key: 1234567890123456 and IV: 9876543210654321
Here is C# code
public byte[] Encrypt(byte[] someData, string KEY, string IV)
{
//preparing
byte[] keyBytes = Encoding.UTF8.GetBytes(KEY);
byte[] ivBytes = Encoding.UTF8.GetBytes(IV);
//here goes encryption
RijndaelManaged rijndaelManaged = new RijndaelManaged();
rijndaelManaged.Key = keyBytes;
rijndaelManaged.IV = ivBytes;
rijndaelManaged.BlockSize = 128;
rijndaelManaged.Mode = CipherMode.CBC;
ICryptoTransform encryptor = rijndaelManaged.CreateEncryptor(rijndaelManaged.Key, rijndaelManaged.IV);
byte[] result = null;
using (MemoryStream memoryStream = new MemoryStream())
{
using (CryptoStream cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write))
{
cryptoStream.Write(someData, 0, someData.Length);
cryptoStream.FlushFinalBlock();
result = memoryStream.ToArray();
}
}
return result;
}
And here is AS3 code using AS3Crypto library
private function decrypt(input:ByteArray, decrKey:String, decrIV:String):ByteArray
{
var key:ByteArray = Hex.toArray(Hex.fromString(decrKey));
var pad:IPad = new NullPad();
var aes:ICipher = Crypto.getCipher("aes-cbc", key, pad);
var ivmode:IVMode = aes as IVMode;
ivmode.IV = Hex.toArray(Hex.fromString(decrIV));
aes.decrypt(input);
return input;
}
I am seeing the "Padding is invalid and cannot be removed" error when I call the method below to decrypt the string from a windows application. String was encrypted from an asp.net application. Both application references the same assembly. I am able encrypt and decrypt with out any problem from the asp.net application. Here is the main code where I do the encryption and decryption.
private static byte[] EncryptHelper(byte[] arrData, string Password, bool Encrypt)
{
//Create the SymetricAlgorithem object
SymmetricAlgorithm myAlg = new RijndaelManaged();
//define a salt value to derive the key.
byte[] salt = System.Text.Encoding.ASCII.GetBytes("hjkhj877ffasah");
//Instantiate Rfc2898DeriveBytes with the password and salt.
Rfc2898DeriveBytes key = new Rfc2898DeriveBytes(Password, salt);
myAlg.Key = key.GetBytes(myAlg.KeySize / 8);
myAlg.IV = key.GetBytes(myAlg.BlockSize / 8);
myAlg.Padding = PaddingMode.PKCS7;
//Create the ICryptoTransform Object
ICryptoTransform encrytptor = Encrypt ? myAlg.CreateEncryptor() : myAlg.CreateDecryptor();
//Create Memorystream to write the encrypted data
using (MemoryStream aStream = new MemoryStream())
{
//Create the CryptoStream Ojbect using the aStream object
using (CryptoStream encryptStream = new CryptoStream(aStream, encrytptor, CryptoStreamMode.Write))
{
//Write the contents to crypto stream
encryptStream.Write(arrData, 0, arrData.Length);
//Flush the cryptostream
encryptStream.FlushFinalBlock();
//Reposition the memorystream to write the contents to an array.
aStream.Position = 0;
}
aStream.Flush();
//Convert to an array and return
return aStream.ToArray();
}
}
This is the method I use to convert the plain text from/to byte array
private static byte[] GetBytes(string str)
{
byte[] bytes = new byte[str.Length * sizeof(char)];
System.Buffer.BlockCopy(str.ToCharArray(), 0, bytes, 0, bytes.Length);
return bytes;
}
private static string GetString(byte[] bytes)
{
char[] chars = new char[bytes.Length / sizeof(char)];
System.Buffer.BlockCopy(bytes, 0, chars, 0, bytes.Length);
return new string(chars);
}
For persist the cipher text to database I use Convert.ToBase64String() and Convert.FromBase64String. Is the problem is with the way I use Rfc2898DeriveBytes class?
Well I think it's important to mention that from a security perspective, you are going to have the same IV for every message with the same password, and a predictable IV is a really big no no.
After that point I kinda don't want to look at it more to see what's going wrong, there are a lot of really bad cut and paste C# encryption on stackoverflow, and they just sit there with no mechanism for update, no one looking at them again except for people finding them to cut and paste again.
Look at Modern Examples of Symmetric Authenticated Encryption of a string. c#.
I try to keep it up to date and reviewed.