/**
        * Method init
        *
        * @param forWrapping
        * @param param
        */
        public void Init(
			bool				forWrapping,
			ICipherParameters	parameters)
        {
            this.forWrapping = forWrapping;
            this.engine = new CbcBlockCipher(new DesEdeEngine());

			SecureRandom sr;
			if (parameters is ParametersWithRandom)
			{
				ParametersWithRandom pr = (ParametersWithRandom) parameters;
				parameters = pr.Parameters;
				sr = pr.Random;
			}
			else
			{
				sr = new SecureRandom();
			}

			if (parameters is KeyParameter)
            {
                this.param = (KeyParameter) parameters;
                if (this.forWrapping)
				{
                    // Hm, we have no IV but we want to wrap ?!?
                    // well, then we have to create our own IV.
                    this.iv = new byte[8];
					sr.NextBytes(iv);

					this.paramPlusIV = new ParametersWithIV(this.param, this.iv);
                }
            }
            else if (parameters is ParametersWithIV)
            {
				if (!forWrapping)
					throw new ArgumentException("You should not supply an IV for unwrapping");

				this.paramPlusIV = (ParametersWithIV) parameters;
                this.iv = this.paramPlusIV.GetIV();
                this.param = (KeyParameter) this.paramPlusIV.Parameters;

				if (this.iv.Length != 8)
					throw new ArgumentException("IV is not 8 octets", "parameters");
            }
        }
		public static IBufferedCipher GetCipher(
			string algorithm)
		{
			if (algorithm == null)
				throw new ArgumentNullException("algorithm");

			algorithm = algorithm.ToUpperInvariant();

			string aliased = (string) algorithms[algorithm];

			if (aliased != null)
				algorithm = aliased;



			IBasicAgreement iesAgreement = null;
			if (algorithm == "IES")
			{
				iesAgreement = new DHBasicAgreement();
			}
			else if (algorithm == "ECIES")
			{
				iesAgreement = new ECDHBasicAgreement();
			}

			if (iesAgreement != null)
			{
				return new BufferedIesCipher(
					new IesEngine(
					iesAgreement,
					new Kdf2BytesGenerator(
					new Sha1Digest()),
					new HMac(
					new Sha1Digest())));
			}



			if (algorithm.StartsWith("PBE"))
			{
				if (algorithm.EndsWith("-CBC"))
				{
					if (algorithm == "PBEWITHSHA1ANDDES-CBC")
					{
						return new PaddedBufferedBlockCipher(
							new CbcBlockCipher(new DesEngine()));
					}
					else if (algorithm == "PBEWITHSHA1ANDRC2-CBC")
					{
						return new PaddedBufferedBlockCipher(
							new CbcBlockCipher(new RC2Engine()));
					}
					else if (Strings.IsOneOf(algorithm,
						"PBEWITHSHAAND2-KEYTRIPLEDES-CBC", "PBEWITHSHAAND3-KEYTRIPLEDES-CBC"))
					{
						return new PaddedBufferedBlockCipher(
							new CbcBlockCipher(new DesEdeEngine()));
					}
					else if (Strings.IsOneOf(algorithm,
						"PBEWITHSHAAND128BITRC2-CBC", "PBEWITHSHAAND40BITRC2-CBC"))
					{
						return new PaddedBufferedBlockCipher(
							new CbcBlockCipher(new RC2Engine()));
					}
				}
				else if (algorithm.EndsWith("-BC") || algorithm.EndsWith("-OPENSSL"))
				{
					if (Strings.IsOneOf(algorithm,
						"PBEWITHSHAAND128BITAES-CBC-BC",
						"PBEWITHSHAAND192BITAES-CBC-BC",
						"PBEWITHSHAAND256BITAES-CBC-BC",
						"PBEWITHSHA256AND128BITAES-CBC-BC",
						"PBEWITHSHA256AND192BITAES-CBC-BC",
						"PBEWITHSHA256AND256BITAES-CBC-BC",
						"PBEWITHMD5AND128BITAES-CBC-OPENSSL",
						"PBEWITHMD5AND192BITAES-CBC-OPENSSL",
						"PBEWITHMD5AND256BITAES-CBC-OPENSSL"))
					{
						return new PaddedBufferedBlockCipher(
							new CbcBlockCipher(new AesFastEngine()));
					}
				}
			}



			string[] parts = algorithm.Split('/');

			IBlockCipher blockCipher = null;
			IAsymmetricBlockCipher asymBlockCipher = null;
			IStreamCipher streamCipher = null;

			string algorithmName = parts[0];
			CipherAlgorithm cipherAlgorithm;
			try
			{
				cipherAlgorithm = (CipherAlgorithm)Enums.GetEnumValue(typeof(CipherAlgorithm), algorithmName);
			}
			catch (ArgumentException)
			{
				throw new SecurityUtilityException("Cipher " + algorithm + " not recognised.");
			}

			switch (cipherAlgorithm)
			{
				case CipherAlgorithm.AES:
					blockCipher = new AesFastEngine();
					break;
				case CipherAlgorithm.ARC4:
					streamCipher = new RC4Engine();
					break;
				case CipherAlgorithm.BLOWFISH:
					blockCipher = new BlowfishEngine();
					break;
				case CipherAlgorithm.CAMELLIA:
					blockCipher = new CamelliaEngine();
					break;
				case CipherAlgorithm.CAST5:
					blockCipher = new Cast5Engine();
					break;
				case CipherAlgorithm.CAST6:
					blockCipher = new Cast6Engine();
					break;
				case CipherAlgorithm.DES:
					blockCipher = new DesEngine();
					break;
				case CipherAlgorithm.DESEDE:
					blockCipher = new DesEdeEngine();
					break;
				case CipherAlgorithm.ELGAMAL:
					asymBlockCipher = new ElGamalEngine();
					break;
				case CipherAlgorithm.GOST28147:
					blockCipher = new Gost28147Engine();
					break;
				case CipherAlgorithm.HC128:
					streamCipher = new HC128Engine();
					break;
				case CipherAlgorithm.HC256:
					streamCipher = new HC256Engine();
					break;
#if INCLUDE_IDEA
				case CipherAlgorithm.IDEA:
					blockCipher = new IdeaEngine();
					break;
#endif
				case CipherAlgorithm.NOEKEON:
					blockCipher = new NoekeonEngine();
					break;
				case CipherAlgorithm.PBEWITHSHAAND128BITRC4:
				case CipherAlgorithm.PBEWITHSHAAND40BITRC4:
					streamCipher = new RC4Engine();
					break;
				case CipherAlgorithm.RC2:
					blockCipher = new RC2Engine();
					break;
				case CipherAlgorithm.RC5:
					blockCipher = new RC532Engine();
					break;
				case CipherAlgorithm.RC5_64:
					blockCipher = new RC564Engine();
					break;
				case CipherAlgorithm.RC6:
					blockCipher = new RC6Engine();
					break;
				case CipherAlgorithm.RIJNDAEL:
					blockCipher = new RijndaelEngine();
					break;
				case CipherAlgorithm.RSA:
					asymBlockCipher = new RsaBlindedEngine();
					break;
				case CipherAlgorithm.SALSA20:
					streamCipher = new Salsa20Engine();
					break;
				case CipherAlgorithm.SEED:
					blockCipher = new SeedEngine();
					break;
				case CipherAlgorithm.SERPENT:
					blockCipher = new SerpentEngine();
					break;
				case CipherAlgorithm.SKIPJACK:
					blockCipher = new SkipjackEngine();
					break;
				case CipherAlgorithm.TEA:
					blockCipher = new TeaEngine();
					break;
				case CipherAlgorithm.TWOFISH:
					blockCipher = new TwofishEngine();
					break;
				case CipherAlgorithm.VMPC:
					streamCipher = new VmpcEngine();
					break;
				case CipherAlgorithm.VMPC_KSA3:
					streamCipher = new VmpcKsa3Engine();
					break;
				case CipherAlgorithm.XTEA:
					blockCipher = new XteaEngine();
					break;
				default:
					throw new SecurityUtilityException("Cipher " + algorithm + " not recognised.");
			}

			if (streamCipher != null)
			{
				if (parts.Length > 1)
					throw new ArgumentException("Modes and paddings not used for stream ciphers");

				return new BufferedStreamCipher(streamCipher);
			}


			bool cts = false;
			bool padded = true;
			IBlockCipherPadding padding = null;
			IAeadBlockCipher aeadBlockCipher = null;

			if (parts.Length > 2)
			{
				if (streamCipher != null)
					throw new ArgumentException("Paddings not used for stream ciphers");

				string paddingName = parts[2];

				CipherPadding cipherPadding;
				if (paddingName == "")
				{
					cipherPadding = CipherPadding.RAW;
				}
				else if (paddingName == "X9.23PADDING")
				{
					cipherPadding = CipherPadding.X923PADDING;
				}
				else
				{
					try
					{
						cipherPadding = (CipherPadding)Enums.GetEnumValue(typeof(CipherPadding), paddingName);
					}
					catch (ArgumentException)
					{
						throw new SecurityUtilityException("Cipher " + algorithm + " not recognised.");
					}
				}

				switch (cipherPadding)
				{
					case CipherPadding.NOPADDING:
						padded = false;
						break;
					case CipherPadding.RAW:
						break;
					case CipherPadding.ISO10126PADDING:
					case CipherPadding.ISO10126D2PADDING:
					case CipherPadding.ISO10126_2PADDING:
						padding = new ISO10126d2Padding();
						break;
					case CipherPadding.ISO7816_4PADDING:
					case CipherPadding.ISO9797_1PADDING:
						padding = new ISO7816d4Padding();
						break;
					case CipherPadding.ISO9796_1:
					case CipherPadding.ISO9796_1PADDING:
						asymBlockCipher = new ISO9796d1Encoding(asymBlockCipher);
						break;
					case CipherPadding.OAEP:
					case CipherPadding.OAEPPADDING:
						asymBlockCipher = new OaepEncoding(asymBlockCipher);
						break;
					case CipherPadding.OAEPWITHMD5ANDMGF1PADDING:
						asymBlockCipher = new OaepEncoding(asymBlockCipher, new MD5Digest());
						break;
					case CipherPadding.OAEPWITHSHA1ANDMGF1PADDING:
					case CipherPadding.OAEPWITHSHA_1ANDMGF1PADDING:
						asymBlockCipher = new OaepEncoding(asymBlockCipher, new Sha1Digest());
						break;
					case CipherPadding.OAEPWITHSHA224ANDMGF1PADDING:
					case CipherPadding.OAEPWITHSHA_224ANDMGF1PADDING:
						asymBlockCipher = new OaepEncoding(asymBlockCipher, new Sha224Digest());
						break;
					case CipherPadding.OAEPWITHSHA256ANDMGF1PADDING:
					case CipherPadding.OAEPWITHSHA_256ANDMGF1PADDING:
						asymBlockCipher = new OaepEncoding(asymBlockCipher, new Sha256Digest());
						break;
					case CipherPadding.OAEPWITHSHA384ANDMGF1PADDING:
					case CipherPadding.OAEPWITHSHA_384ANDMGF1PADDING:
						asymBlockCipher = new OaepEncoding(asymBlockCipher, new Sha384Digest());
						break;
					case CipherPadding.OAEPWITHSHA512ANDMGF1PADDING:
					case CipherPadding.OAEPWITHSHA_512ANDMGF1PADDING:
						asymBlockCipher = new OaepEncoding(asymBlockCipher, new Sha512Digest());
						break;
					case CipherPadding.PKCS1:
					case CipherPadding.PKCS1PADDING:
						asymBlockCipher = new Pkcs1Encoding(asymBlockCipher);
						break;
					case CipherPadding.PKCS5:
					case CipherPadding.PKCS5PADDING:
					case CipherPadding.PKCS7:
					case CipherPadding.PKCS7PADDING:
						padding = new Pkcs7Padding();
						break;
					case CipherPadding.TBCPADDING:
						padding = new TbcPadding();
						break;
					case CipherPadding.WITHCTS:
						cts = true;
						break;
					case CipherPadding.X923PADDING:
						padding = new X923Padding();
						break;
					case CipherPadding.ZEROBYTEPADDING:
						padding = new ZeroBytePadding();
						break;
					default:
						throw new SecurityUtilityException("Cipher " + algorithm + " not recognised.");
				}
			}

			string mode = "";
			if (parts.Length > 1)
			{
				mode = parts[1];

				int di = GetDigitIndex(mode);
				string modeName = di >= 0 ? mode.Substring(0, di) : mode;

				try
				{
					CipherMode cipherMode = modeName == ""
						? CipherMode.NONE
						: (CipherMode)Enums.GetEnumValue(typeof(CipherMode), modeName);

					switch (cipherMode)
					{
						case CipherMode.ECB:
						case CipherMode.NONE:
							break;
						case CipherMode.CBC:
							blockCipher = new CbcBlockCipher(blockCipher);
							break;
						case CipherMode.CCM:
							aeadBlockCipher = new CcmBlockCipher(blockCipher);
							break;
						case CipherMode.CFB:
						{
							int bits = (di < 0)
								?	8 * blockCipher.GetBlockSize()
								:	int.Parse(mode.Substring(di));
	
							blockCipher = new CfbBlockCipher(blockCipher, bits);
							break;
						}
						case CipherMode.CTR:
							blockCipher = new SicBlockCipher(blockCipher);
							break;
						case CipherMode.CTS:
							cts = true;
							blockCipher = new CbcBlockCipher(blockCipher);
							break;
						case CipherMode.EAX:
							aeadBlockCipher = new EaxBlockCipher(blockCipher);
							break;
						case CipherMode.GCM:
							aeadBlockCipher = new GcmBlockCipher(blockCipher);
							break;
						case CipherMode.GOFB:
							blockCipher = new GOfbBlockCipher(blockCipher);
							break;
						case CipherMode.OFB:
						{
							int bits = (di < 0)
								?	8 * blockCipher.GetBlockSize()
								:	int.Parse(mode.Substring(di));
	
							blockCipher = new OfbBlockCipher(blockCipher, bits);
							break;
						}
						case CipherMode.OPENPGPCFB:
							blockCipher = new OpenPgpCfbBlockCipher(blockCipher);
							break;
						case CipherMode.SIC:
							if (blockCipher.GetBlockSize() < 16)
							{
								throw new ArgumentException("Warning: SIC-Mode can become a twotime-pad if the blocksize of the cipher is too small. Use a cipher with a block size of at least 128 bits (e.g. AES)");
							}
							blockCipher = new SicBlockCipher(blockCipher);
							break;
						default:
							throw new SecurityUtilityException("Cipher " + algorithm + " not recognised.");
					}
				}
				catch (ArgumentException)
				{
					throw new SecurityUtilityException("Cipher " + algorithm + " not recognised.");
				}
			}

			if (aeadBlockCipher != null)
			{
				if (cts)
					throw new SecurityUtilityException("CTS mode not valid for AEAD ciphers.");
				if (padded && parts.Length > 2 && parts[2] != "")
					throw new SecurityUtilityException("Bad padding specified for AEAD cipher.");

				return new BufferedAeadBlockCipher(aeadBlockCipher);
			}

			if (blockCipher != null)
			{
				if (cts)
				{
					return new CtsBlockCipher(blockCipher);
				}

				if (padding != null)
				{
					return new PaddedBufferedBlockCipher(blockCipher, padding);
				}

				if (!padded || blockCipher.IsPartialBlockOkay)
				{
					return new BufferedBlockCipher(blockCipher);
				}

				return new PaddedBufferedBlockCipher(blockCipher);
			}

			if (asymBlockCipher != null)
			{
				return new BufferedAsymmetricBlockCipher(asymBlockCipher);
			}

			throw new SecurityUtilityException("Cipher " + algorithm + " not recognised.");
		}
		public Rfc3211WrapEngine(
			IBlockCipher engine)
		{
			this.engine = new CbcBlockCipher(engine);
		}