java.io.IOException: Error while finalizing cipher while decrypting sub-strings - encryption

I am running into an issue that I am not sure the proper fix. I used this thread as reference:
java.lang.IllegalArgumentException: bad base-64 when decrypting string
Basically I split a string into chunks and encrypt each chunk. But when it is time to decrypt the same way it never works. It always gives me this annoying exception:
"java.io.IOException: Error while finalizing cipher"
basically I split the string as below:
static public class RSAString {
private ArrayList<String> mChunkList = new ArrayList<String>();
RSAString() {
mChunkList.clear();
}
public ArrayList<String> getChunkList() {
return mChunkList;
}
RSAString(String stringSrc) {
if (stringSrc.length() < CHUNK_SIZE) {
mChunkList.add(stringSrc);
} else {
int j = 0;
for (int i = 0; i < stringSrc.length() / CHUNK_SIZE; i++) {
String subString = stringSrc.substring(j, j + CHUNK_SIZE);
mChunkList.add(subString);
j += CHUNK_SIZE;
}
int leftOver = stringSrc.length() % CHUNK_SIZE;
if (leftOver > 0) {
String subString = stringSrc.substring(j, j + leftOver);
mChunkList.add(subString);
}
}
}
}
Then I decrypt with this code:
// This **DOES NOT** work
final String AndroidOpenSSLString = "AndroidOpenSSL";
final String AndroidKeyStoreBCWorkaroundString = "AndroidKeyStoreBCWorkaround";
mProvider = Build.VERSION.SDK_INT < Build.VERSION_CODES.M ? AndroidOpenSSLString : AndroidKeyStoreBCWorkaroundString;
Cipher outCipher = Cipher.getInstance("RSA/ECB/PKCS1Padding", mProvider);
outCipher.init(Cipher.DECRYPT_MODE, mPrivateKey);
for (String chunkEncrypted : rsaEcryptedText.getChunkList()) {
byte[] cipherText = chunkEncrypted.getBytes("UTF-8");
CipherInputStream cipherInputStream = new CipherInputStream(
new ByteArrayInputStream(Base64.decode(cipherText, Base64.NO_WRAP)), outCipher);
ArrayList<Byte> values = new ArrayList<>();
int nextByte;
while ((nextByte = cipherInputStream.read()) != -1) {
values.add((byte) nextByte);
}
byte[] bytes = new byte[values.size()];
for (int i = 0; i < bytes.length; i++) {
bytes[i] = values.get(i);
}
decryptedString += new String(bytes, 0, bytes.length, "UTF-8");
cipherInputStream.close();
cipherInputStream.reset();
}
The only work around I found was to re-initialize the cypher every new sub-string. Like the hack below:
for (String chunkEncrypted : rsaEcryptedText.getChunkList()) {
// This works, but I am re-initializing the out cypher every time!
// super slow!!! WHY DO I HAVE TO DO THIS?
Cipher outCipher = Cipher.getInstance("RSA/ECB/PKCS1Padding", mProvider);
outCipher.init(Cipher.DECRYPT_MODE, mPrivateKey);
byte[] cipherText = chunkEncrypted.getBytes("UTF-8");
CipherInputStream cipherInputStream = new CipherInputStream(
new ByteArrayInputStream(Base64.decode(cipherText, Base64.NO_WRAP)), outCipher);
ArrayList<Byte> values = new ArrayList<>();
int nextByte;
while ((nextByte = cipherInputStream.read()) != -1) {
values.add((byte) nextByte);
}
byte[] bytes = new byte[values.size()];
for (int i = 0; i < bytes.length; i++) {
bytes[i] = values.get(i);
}
decryptedString += new String(bytes, 0, bytes.length, "UTF-8");
cipherInputStream.close();
cipherInputStream.reset();
}
The problem with my fix is that it makes the decryption much slower. Also, even with my sub-strings at small sizes (sub-string int CHUNK_SIZE = 64) I keep getting the same exception.
Anyway, I wonder if anyone can point me out the correct way to decrypt a long string with RSA encryption.
and here is the encryption code--not sure if it matters in this case--but it always works:
Cipher inCipher = Cipher.getInstance("RSA/ECB/PKCS1Padding", mProvider);
inCipher.init(Cipher.ENCRYPT_MODE, mPublicKey);
RSAString rsaStringPlainText = new RSAString(plainText);
for (String chunkPlain : rsaStringPlainText.getChunkList()) {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
CipherOutputStream cipherOutputStream = new CipherOutputStream(
outputStream, inCipher);
cipherOutputStream.write(chunkPlain.getBytes("UTF-8"));
cipherOutputStream.flush();
cipherOutputStream.close();
byte[] ecryptedText = Base64.encode(outputStream.toByteArray(), Base64.NO_WRAP);
encryptedStringOut.mChunkList.add(new String(ecryptedText));
}

RSA is not suited for bulk encryption as it's quite slow (more than a factor 1000 when compared to AES). Instead use a symmetric encryption algorithm like AES if you can. If you need the two key's of RSA, use Hybrid encryption where you encrypt the data with a random symmetric key, and then encrypt that key with the RSA key.
Another benefit of symmetric encryption is that libraries automatically supports bulk encryption, where you don't need to handle chopping your data up into small blocks before encryption.

Related

Decrypt RSA encrypted value generated from .net in Android [duplicate]

I have gone through many posts here but did not find the right solution. I want to decrypt a value encrypted in c# .net from Android.
I have successfully decrypted in .net platform using the following code snippet
public static void Main()
{
string _privateKey = Base64Decode("myprivatekey");
var rsa = new RSACryptoServiceProvider();
string data = "198,47,144,175,154,47,194,175,242,41,212,150,220,177,198,161,236,36,197,62,18,111,21,244,232,245,90,234,195,169,141,195,139,199,131,163,26,124,246,50,102,229,73,148,18,110,170,145,112,237,23,123,226,135,158,206,71,116,9,219,56,96,140,19,180,192,80,29,63,160,43,127,204,135,155,67,46,160,225,12,85,161,107,214,104,218,6,220,252,73,252,92,152,235,214,126,245,126,129,150,49,68,162,120,237,246,27,25,45,225,106,201,251,128,243,213,250,172,26,28,176,219,198,194,7,202,34,210";
var dataArray = data.Split(new char[] { ',' });
byte[] dataByte = new byte[dataArray.Length];
for (int i = 0; i < dataArray.Length; i++)
{
dataByte[i] = Convert.ToByte(dataArray[i]);
}
rsa.FromXmlString(_privateKey);
var decryptedByte = rsa.Decrypt(dataByte, false);
Console.WriteLine(_encoder.GetString(decryptedByte));
}
Now I want to do the same process in Android app. Please can somebody guide me through this?
I have tried the following code but its throwing javax.crypto.IllegalBlockSizeException: input must be under 128 bytes exception
String modulusString = "hm2oRCtP6usJKYpq7o1K20uUuL11j5xRrbV4FCQhn/JeXLT21laKK9901P69YUS3bLo64x8G1PkCfRtjbbZCIaa1Ci/BCQX8nF2kZVfrPyzcmeAkq4wsDthuZ+jPInknzUI3TQPAzdj6gim97E731i6WP0MHFqW6ODeQ6Dsp8pc=";
String publicExponentString = "AQAB";
byte[] modulusBytes = Base64.decode(modulusString, DEFAULT);
byte[] exponentBytes = Base64.decode(publicExponentString, DEFAULT);
BigInteger modulus = new BigInteger(1, modulusBytes);
BigInteger publicExponent = new BigInteger(1, exponentBytes);
RSAPrivateKeySpec rsaPubKey = new RSAPrivateKeySpec(modulus, publicExponent);
KeyFactory fact = KeyFactory.getInstance("RSA");
PrivateKey pubKey = fact.generatePrivate(rsaPubKey);
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1PADDING");
cipher.init(Cipher.ENCRYPT_MODE, pubKey);
byte[] plainBytes = result.getBytes("UTF-16LE");
byte[] cipherData = cipher.doFinal(plainBytes);
String encryptedStringBase64 = Base64.decode(cipherData, DEFAULT).toString();

Migrating from Membership to Identity when passwordFormat = Encrypted and decryption = AES

Recently I began developing a new MVC app and needed to take an older existing asp.net membership database and convert it into the new(er) Identity system.
If you've found yourself in a similar situation, then likely you've come upon this helpful post from microsoft which gives you great guidance and scripts on converting the database over to the new schema, including the passwords.
To handle the differences in hashing/encryption of passwords between the two systems, they include a custom password hasher, SqlPasswordHasher, which parses the password field (which has been combined into Password|PasswordFormat|Salt) and attempts to duplicate the logic found inside SqlMembershipProvider to compare the incoming password with the stored version.
However, as I (and another commenter on that post) noticed, this handy hasher they provided doesn't handle Encrypted passwords (despite the confusing lingo they use in the post that seems to indicate that it does). It seems like it should, considering they do bring across the password format into the database, but then curiously the code doesn't use it, instead having
int passwordformat = 1;
which is for hashed passwords. What I needed was one that would handle my scenario which is encrypted passwords using the System.Web/MachineKey configuration element's decryptionKey.
If you also are in such a predicament, and are using the AES algorithm (as defined in the decryption property of the machineKey) then my answer below should come to your rescue.
First, let's talk real quick about what the SqlMembershipProvider is doing under the hood. The provider combines the salt, converted to a byte[] with the password, encoded as a unicode byte array, into a single larger byte array, by concatenating the two together. Pretty straightforward. Then it passes this off, through an abstraction (MembershipAdapter) to the MachineKeySection where the real work is done.
The important part about that handoff is that it instructs the MachineKeySection to use an empty IV (intialization vector) and also to perform no signing. That empty IV is the real lynchpin, because the machineKey element has no IV property, so if you've scratched your head and wondered how the providers were handling this aspect, that's how. Once you know that (from digging around the source code) then you can distill down the encryption code in the MachineKeySection code and combine it with the membership provider's code to arrive at a more complete hasher. Full source:
public class SQLPasswordHasher : PasswordHasher
{
public override string HashPassword(string password)
{
return base.HashPassword(password);
}
public override PasswordVerificationResult VerifyHashedPassword(string hashedPassword, string providedPassword)
{
string[] passwordProperties = hashedPassword.Split('|');
if (passwordProperties.Length != 3)
{
return base.VerifyHashedPassword(hashedPassword, providedPassword);
}
else
{
string passwordHash = passwordProperties[0];
int passwordformat = int.Parse(passwordProperties[1]);
string salt = passwordProperties[2];
if (String.Equals(EncryptPassword(providedPassword, passwordformat, salt), passwordHash, StringComparison.CurrentCultureIgnoreCase))
{
return PasswordVerificationResult.SuccessRehashNeeded;
}
else
{
return PasswordVerificationResult.Failed;
}
}
}
//This is copied from the existing SQL providers and is provided only for back-compat.
private string EncryptPassword(string pass, int passwordFormat, string salt)
{
if (passwordFormat == 0) // MembershipPasswordFormat.Clear
return pass;
byte[] bIn = Encoding.Unicode.GetBytes(pass);
byte[] bSalt = Convert.FromBase64String(salt);
byte[] bRet = null;
if (passwordFormat == 1)
{ // MembershipPasswordFormat.Hashed
HashAlgorithm hm = HashAlgorithm.Create("SHA1");
if (hm is KeyedHashAlgorithm)
{
KeyedHashAlgorithm kha = (KeyedHashAlgorithm)hm;
if (kha.Key.Length == bSalt.Length)
{
kha.Key = bSalt;
}
else if (kha.Key.Length < bSalt.Length)
{
byte[] bKey = new byte[kha.Key.Length];
Buffer.BlockCopy(bSalt, 0, bKey, 0, bKey.Length);
kha.Key = bKey;
}
else
{
byte[] bKey = new byte[kha.Key.Length];
for (int iter = 0; iter < bKey.Length;)
{
int len = Math.Min(bSalt.Length, bKey.Length - iter);
Buffer.BlockCopy(bSalt, 0, bKey, iter, len);
iter += len;
}
kha.Key = bKey;
}
bRet = kha.ComputeHash(bIn);
}
else
{
byte[] bAll = new byte[bSalt.Length + bIn.Length];
Buffer.BlockCopy(bSalt, 0, bAll, 0, bSalt.Length);
Buffer.BlockCopy(bIn, 0, bAll, bSalt.Length, bIn.Length);
bRet = hm.ComputeHash(bAll);
}
}
else //MembershipPasswordFormat.Encrypted, aka 2
{
byte[] bEncrypt = new byte[bSalt.Length + bIn.Length];
Buffer.BlockCopy(bSalt, 0, bEncrypt, 0, bSalt.Length);
Buffer.BlockCopy(bIn, 0, bEncrypt, bSalt.Length, bIn.Length);
// Distilled from MachineKeyConfigSection EncryptOrDecryptData function, assuming AES algo and paswordCompatMode=Framework20 (the default)
using (var stream = new MemoryStream())
{
var aes = new AesCryptoServiceProvider();
aes.Key = HexStringToByteArray(MachineKey.DecryptionKey);
aes.GenerateIV();
aes.IV = new byte[aes.IV.Length];
using (var transform = aes.CreateEncryptor())
{
using (var stream2 = new CryptoStream(stream, transform, CryptoStreamMode.Write))
{
stream2.Write(bEncrypt, 0, bEncrypt.Length);
stream2.FlushFinalBlock();
bRet = stream.ToArray();
}
}
}
}
return Convert.ToBase64String(bRet);
}
public static byte[] HexStringToByteArray(String hex)
{
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;
}
private static MachineKeySection MachineKey
{
get
{
//Get encryption and decryption key information from the configuration.
System.Configuration.Configuration cfg = WebConfigurationManager.OpenWebConfiguration(System.Web.Hosting.HostingEnvironment.ApplicationVirtualPath);
return cfg.GetSection("system.web/machineKey") as MachineKeySection;
}
}
}
If you have a different algorithm, then the steps will be very close to the same, but you may want to first dive into the source for MachineKeySection and carefully walkthrough how they're initializing things. Happy Coding!

AES Encrypt / Decrypt byte[]

I am trying to encrypt a byte[] using the following methods but when I decrypt it my byte[] is bigger than when I started and I think its to do with padding but I am not sure how to solve it.
The method isnt finished yet (I know its bad to append the key + iv like my example but its for testing purpose to get it working before I move on).
So when I try to open the file afterwards (tested with MS Word file) I get a message saying the file is damaged and would I like to repair it.
Encrypt Method
public byte[] Encrypt(byte[] dataToEncrypt) {
// Check arguments.
if (dataToEncrypt == null || dataToEncrypt.Length <= 0) {
throw new ArgumentNullException("dataToEncrypt");
}
byte[] encryptedData;
byte[] key;
byte[] iv;
// Create an Aes object
using (Aes aesAlg = Aes.Create()) {
key = aesAlg.Key;
iv = aesAlg.IV;
// Create a encrytor to perform the stream transform.
ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);
// Create the streams used for encryption.
using (MemoryStream memoryStream = new MemoryStream()) {
using (CryptoStream cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write)) {
cryptoStream.Write(dataToEncrypt, 0, dataToEncrypt.Length);
cryptoStream.FlushFinalBlock();
encryptedData = memoryStream.ToArray();
}
}
}
byte[] result = new byte[encryptedData.Length + KEY_SIZE + IV_SIZE];
Buffer.BlockCopy(key, 0, result, 0, KEY_SIZE);
Buffer.BlockCopy(iv, 0, result, KEY_SIZE, IV_SIZE);
Buffer.BlockCopy(encryptedData, 0, result, KEY_SIZE + IV_SIZE, encryptedData.Length);
return result;
}
Decrypt Method
public byte[] Decrypt(byte[] encryptedData) {
// Check arguments.
if (encryptedData == null || encryptedData.Length <= 0) {
throw new ArgumentNullException("encryptedData");
}
byte[] storedKey = new byte[KEY_SIZE];
byte[] storedIV = new byte[IV_SIZE];
byte[] dataToDecrypt = new byte[encryptedData.Length - (KEY_SIZE + IV_SIZE)];
Buffer.BlockCopy(encryptedData, 0, storedKey, 0, KEY_SIZE);
Buffer.BlockCopy(encryptedData, KEY_SIZE, storedIV, 0, IV_SIZE);
Buffer.BlockCopy(encryptedData, KEY_SIZE + IV_SIZE, dataToDecrypt, 0, encryptedData.Length - (KEY_SIZE + IV_SIZE));
byte[] decryptedData = null;
// Create an AesCryptoServiceProvider object
// with the specified key and IV.
using (Aes aesAlg = Aes.Create()) {
aesAlg.Key = storedKey;
aesAlg.IV = storedIV;
// Create a decrytor to perform the stream transform.
ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV);
// Create the streams used for decryption.
using (MemoryStream memoryStream = new MemoryStream(dataToDecrypt)) {
using (CryptoStream cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read)) {
cryptoStream.Read(dataToDecrypt, 0, dataToDecrypt.Length);
decryptedData = memoryStream.ToArray();
}
}
}
return decryptedData;
}
You are assuming that the entire buffer is plaintext data as well. You should only return that part of the buffer that contains the plaintext data (using the response of Read to see how much bytes are returned). The encrypted data is usually larger because of the padding.
As a single read method isn't good practice with regards to stream handling. You need to read until the end of the stream is reached. Otherwise you may go from having too much data to having too little.

Am I using RSA encryption and signing correctly?

I like to make sure I got the encrypting part right in my app. I plan to open source this code. You can get the two related files at: https://gist.github.com/lameguy7quick/1e998aad673354d2661b.
Have I made any mistakes? I know I didn't understand HMAC at the time of writing.
The idea is simple. I load the recipient public key. Encrypt a randomly made AES key. Encode the message with said AES then stuff it into a tcp connection. It seems to work correctly have I overlooked anything? I have a feeling maybe the aes should have a randomly made IV but the key itself is randomly generated so maybe I don't need to?
I also use SHA1CryptoServiceProvider I think I should use SHA512CryptoServiceProvider.
Did I sign correctly? It says it is signed but I'm not sure if there is an attack on it
public byte[] SendMessage(byte[] recipient_pubkey, byte[] replyTo, string txt, byte[] prvkey, byte[] pubkey)
{
RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
var msgid = new byte[16];
rng.GetBytes(msgid);
using (var aes = new RijndaelManaged())
{
byte[] rsa_aes_key;
RSAParameters recipient_rsap;
Shared.LoadKey2(Shared.pubToPem(recipient_pubkey), null, out recipient_rsap);
using (var rsa = new RSACryptoServiceProvider())
{
rsa.ImportParameters(recipient_rsap);
rsa_aes_key = rsa.Encrypt(aes.Key, false);
}
var aesmsg = EncodeMessage(recipient_pubkey, msgid, replyTo, txt, prvkey, pubkey, aes.Key, Shared.FixedIV_16bytes);
if (rsa_aes_key.Length + aesmsg.Length > 1024 * 15) throw new Exception();
sw.WriteByte((byte)ClientServerCmd.SendMessage);
sw.WriteShort((short)recipient_pubkey.Length);
sw.Write(recipient_pubkey, 0, recipient_pubkey.Length);
sw.WriteShort(rsa_aes_key.Length + aesmsg.Length);
sw.Write(rsa_aes_key, 0, rsa_aes_key.Length);
sw.Write(aesmsg, 0, aesmsg.Length);
sw.Flush();
var resp = sr.ReadByte();
if (resp != (byte)ClientServerCmd.KeyLenOk)
throw new Exception();
resp = sr.ReadByte();
if (resp == (byte)ClientServerCmd.NotRegistered)
throw new MyException("User you're writing to does not exist");
if (resp != (byte)ClientServerCmd.Success)
throw new Exception();
}
return msgid;
}
byte[] EncodeMessage(byte[] recipient_pubkey, byte[]msgid, byte[] replyTo, string txt, byte[] prvkey, byte[] pubkey, byte[] aes_key, byte[] aes_iv)
{
if (replyTo == null)
{
replyTo = new byte[16];
}
var txtbuf = Encoding.UTF8.GetBytes(txt);
var SignMessage = prvkey != null;
byte[] hash = null;
if (SignMessage)
{
using (var rsa = new RSACryptoServiceProvider())
{
RSAParameters rsap;
Shared.LoadKey2(Shared.prvToPem(prvkey), null, out rsap);
rsa.ImportParameters(rsap);
using (var ms = new MemoryStream()) //sign
{
ms.Write(msgid, 0, msgid.Length);
ms.Write(replyTo, 0, replyTo.Length);
ms.WriteShort((short)txtbuf.Length);
ms.Write(txtbuf, 0, txtbuf.Length);
ms.WriteShort((short)pubkey.Length);
ms.Write(pubkey, 0, pubkey.Length);
ms.WriteShort((short)recipient_pubkey.Length);
ms.Write(recipient_pubkey, 0, recipient_pubkey.Length);
ms.Position = 0;
hash = rsa.SignData(ms, new SHA1CryptoServiceProvider());
}
}
}
byte[] c1;
using (var ms1 = new MemoryStream())
using (var ms = new BZip2OutputStream(ms1))
{
ms.Write(txtbuf, 0, txtbuf.Length);
ms.Close();
c1 = ms1.ToArray();
}
var compressText = c1.Length < txtbuf.Length;
byte[] aesmsg;
byte[] aeskey;
using (var aes = new RijndaelManaged())
{
aeskey = aes.Key;
aes.IV = Shared.FixedIV_16bytes;
using (MemoryStream msEncrypt = new MemoryStream())
{
using (var encryptor = aes.CreateEncryptor(aes_key, aes_iv))
using (CryptoStream sw2 = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
{
sw2.WriteByte((Byte)((compressText ? 1 : 0) | (SignMessage ? 2 : 0)));
sw2.Write(msgid, 0, msgid.Length);
sw2.Write(replyTo, 0, replyTo.Length);
if (compressText)
{
sw2.WriteShort((short)c1.Length);
sw2.Write(c1, 0, c1.Length);
}
else
{
sw2.WriteShort((short)txtbuf.Length);
sw2.Write(txtbuf, 0, txtbuf.Length);
}
if (SignMessage)
{
sw2.WriteShort((short)pubkey.Length);
sw2.Write(pubkey, 0, pubkey.Length);
sw2.WriteShort((short)hash.Length);
sw2.Write(hash, 0, hash.Length);
}
}
msEncrypt.Flush();
aesmsg = msEncrypt.ToArray();
}
}
return aesmsg;
}
Fixed IV is a certainly not right.
IV for AES CBC should not be predictable. Typically you make it random and include it with the ciphertext.
A mac is important to avoid chosen ciphertext attacks, you are reading and writing your own format you have to worry about manipulation of your ciphertext allowing something to be exposed, your aes code likely will throw a padding exception that could be used to recover the plaintext by sending modified ciphertext to your receiver.
It's good that your code will be open source, opening it up to analysis and patches, but you should be aware the applying cryptography correctly is difficult and easy to make mistakes.
If you can adapt a high level library, like Keyczar (I ported it to c#), you'll be in better shape, though nothing's perfect.

Multiple Encryption with Bouncycastle

Is it possible to encrypt data with multiple encryption algorithms using the BouncyCastle open source library? I have written the code below to pass the output from one cipher as input to the other cipher but I am always getting the same output bytes.
Thanks in advance,
Ahmad Yassin
Boolean ProcessFile(
PaddedBufferedBlockCipher aesCipher,
PaddedBufferedBlockCipher serpentCipher,
PaddedBufferedBlockCipher twofishCipher,
FileStream fin,
FileStream fout,
int addOnLength)
{
int inputSize = aesCipher.GetBlockSize();
int outputSize = aesCipher.GetOutputSize(inputSize);
byte[] inputBuffer = new byte[inputSize];
byte[] outBuffer = new byte[outputSize];
long fileLenght = fin.Length - addOnLength;
long progressStep = fileLenght >= 100 ? fileLenght / 100 : fileLenght;
long totalLength = 0;
int inLength = 0;
int outLength;
try
{
while ((inLength = fin.Read(inputBuffer, 0, inputSize)) != 0)
{
outLength = aesCipher.ProcessBytes(inputBuffer, 0, inLength, outBuffer, 0);
outLength = serpentCipher.ProcessBytes(outBuffer, 0, outLength, outBuffer, 0);
outLength = twofishCipher.ProcessBytes(outBuffer, 0, outLength, outBuffer, 0);
fout.Write(outBuffer, 0, outLength);
totalLength += inLength;
if (totalLength >= progressStep)
{
DoProgressChanged();
totalLength = totalLength % progressStep;
}
}
outLength = aesCipher.DoFinal(outBuffer, 0);
fout.Write(outBuffer, 0, outLength);
fout.Close();
fin.Close();
return true;
}
catch (IOException e)
{
throw new IOException(e.Message);
}
}
public event EventHandler ProgressChanged;
private void DoProgressChanged()
{
if (this.ProgressChanged != null)
this.ProgressChanged(this, new EventArgs());
}
enum Mode { Encription = 1, Decription = 0 }
enum Algorithm { AES = 1, Serpent = 2, Twofish = 3 }
PaddedBufferedBlockCipher GetCipher(Algorithm algorithm, Mode mode, byte[] key, byte[] iv)
{
IBlockCipher blockCipher;
ICipherParameters parameters = new ParametersWithIV(new KeyParameter(key), iv);
switch (algorithm)
{
case Algorithm.AES:
blockCipher = new AesFastEngine();
break;
case Algorithm.Serpent:
blockCipher = new SerpentEngine();
break;
case Algorithm.Twofish:
blockCipher = new TwofishEngine();
break;
default:
blockCipher = new AesFastEngine();
break;
}
IBlockCipher cbcblockCipher = new CbcBlockCipher(blockCipher);
PaddedBufferedBlockCipher cipher = new PaddedBufferedBlockCipher(cbcblockCipher, new Pkcs7Padding());
cipher.Init(mode == Mode.Encription, parameters);
return cipher;
}
Of course it is possible, but I would strongly suggest you use some kind of connected streams, or perform three passes over the buffer (first AES, then Serpent, then TwoFish or any other order). Don't perform a block size encryption for every cipher as you do now, as block sizes and other parameters may differ.
Of course, the usefulness of using three separate encryption algorithms is debatable.

Resources