public RecipientInfo Generate(KeyParameter contentEncryptionKey, SecureRandom random)
		{
			byte[] keyBytes = contentEncryptionKey.GetKey();

			string rfc3211WrapperName = Helper.GetRfc3211WrapperName(keyEncryptionKeyOID);
			IWrapper keyWrapper = Helper.CreateWrapper(rfc3211WrapperName);

			// Note: In Java build, the IV is automatically generated in JCE layer
			int ivLength = rfc3211WrapperName.StartsWith("DESEDE") ? 8 : 16;
			byte[] iv = new byte[ivLength];
			random.NextBytes(iv);

			ICipherParameters parameters = new ParametersWithIV(keyEncryptionKey, iv);
			keyWrapper.Init(true, new ParametersWithRandom(parameters, random));
        	Asn1OctetString encryptedKey = new DerOctetString(
				keyWrapper.Wrap(keyBytes, 0, keyBytes.Length));

			DerSequence seq = new DerSequence(
				new DerObjectIdentifier(keyEncryptionKeyOID),
				new DerOctetString(iv));

			AlgorithmIdentifier keyEncryptionAlgorithm = new AlgorithmIdentifier(
				PkcsObjectIdentifiers.IdAlgPwriKek, seq);

			return new RecipientInfo(new PasswordRecipientInfo(
				keyDerivationAlgorithm, keyEncryptionAlgorithm, encryptedKey));
		}
        public void Save(Stream stream, char[] password, SecureRandom random)
        {
            if (stream == null)
            {
                throw new ArgumentNullException("stream");
            }
            if (password == null)
            {
                throw new ArgumentNullException("password");
            }
            if (random == null)
            {
                throw new ArgumentNullException("random");
            }

            //
            // handle the key
            //
            var keyS = new Asn1EncodableVector();
            foreach (string name in _keys.Keys)
            {
                var kSalt = new byte[SaltSize];
                random.NextBytes(kSalt);

                var privKey = (AsymmetricKeyEntry) _keys[name];
                EncryptedPrivateKeyInfo kInfo = EncryptedPrivateKeyInfoFactory.CreateEncryptedPrivateKeyInfo(_keyAlgorithm, password, kSalt, MinIterations, privKey.Key);

                var kName = new Asn1EncodableVector();

                foreach (string oid in privKey.BagAttributeKeys)
                {
                    Asn1Encodable entry = privKey[oid];

                    // NB: Ignore any existing FriendlyName
                    if (oid.Equals(PkcsObjectIdentifiers.Pkcs9AtFriendlyName.Id))
                    {
                        continue;
                    }

                    kName.Add(new DerSequence(new DerObjectIdentifier(oid), new DerSet(entry)));
                }

                //
                // make sure we are using the local alias on store
                //
                // NB: We always set the FriendlyName based on 'name'
                //if (privKey[PkcsObjectIdentifiers.Pkcs9AtFriendlyName] == null)
                {
                    kName.Add(new DerSequence(PkcsObjectIdentifiers.Pkcs9AtFriendlyName, new DerSet(new DerBmpString(name))));
                }

                //
                // make sure we have a local key-id
                //
                if (privKey[PkcsObjectIdentifiers.Pkcs9AtLocalKeyID] == null)
                {
                    X509CertificateEntry ct = GetCertificate(name);
                    AsymmetricKeyParameter pubKey = ct.Certificate.GetPublicKey();
                    SubjectKeyIdentifier subjectKeyID = CreateSubjectKeyID(pubKey);

                    kName.Add(new DerSequence(PkcsObjectIdentifiers.Pkcs9AtLocalKeyID, new DerSet(subjectKeyID)));
                }

                var kBag = new SafeBag(PkcsObjectIdentifiers.Pkcs8ShroudedKeyBag, kInfo.ToAsn1Object(), new DerSet(kName));
                keyS.Add(kBag);
            }

            byte[] derEncodedBytes = new DerSequence(keyS).GetDerEncoded();

            var keyString = new BerOctetString(derEncodedBytes);

            //
            // certificate processing
            //
            var cSalt = new byte[SaltSize];

            random.NextBytes(cSalt);

            var certSeq = new Asn1EncodableVector();
            var cParams = new Pkcs12PbeParams(cSalt, MinIterations);
            var cAlgId = new AlgorithmIdentifier(_certAlgorithm, cParams.ToAsn1Object());
            ISet doneCerts = new HashSet();

            foreach (string name in _keys.Keys)
            {
                X509CertificateEntry certEntry = GetCertificate(name);
                var cBag = new CertBag(PkcsObjectIdentifiers.X509Certificate, new DerOctetString(certEntry.Certificate.GetEncoded()));

                var fName = new Asn1EncodableVector();

                foreach (string oid in certEntry.BagAttributeKeys)
                {
                    Asn1Encodable entry = certEntry[oid];

                    // NB: Ignore any existing FriendlyName
                    if (oid.Equals(PkcsObjectIdentifiers.Pkcs9AtFriendlyName.Id))
                    {
                        continue;
                    }

                    fName.Add(new DerSequence(new DerObjectIdentifier(oid), new DerSet(entry)));
                }

                //
                // make sure we are using the local alias on store
                //
                // NB: We always set the FriendlyName based on 'name'
                //if (certEntry[PkcsObjectIdentifiers.Pkcs9AtFriendlyName] == null)
                {
                    fName.Add(new DerSequence(PkcsObjectIdentifiers.Pkcs9AtFriendlyName, new DerSet(new DerBmpString(name))));
                }

                //
                // make sure we have a local key-id
                //
                if (certEntry[PkcsObjectIdentifiers.Pkcs9AtLocalKeyID] == null)
                {
                    AsymmetricKeyParameter pubKey = certEntry.Certificate.GetPublicKey();
                    SubjectKeyIdentifier subjectKeyID = CreateSubjectKeyID(pubKey);

                    fName.Add(new DerSequence(PkcsObjectIdentifiers.Pkcs9AtLocalKeyID, new DerSet(subjectKeyID)));
                }

                var sBag = new SafeBag(PkcsObjectIdentifiers.CertBag, cBag.ToAsn1Object(), new DerSet(fName));

                certSeq.Add(sBag);

                doneCerts.Add(certEntry.Certificate);
            }

            foreach (string certId in _certs.Keys)
            {
                var cert = (X509CertificateEntry) _certs[certId];

                if (_keys[certId] != null)
                {
                    continue;
                }

                var cBag = new CertBag(PkcsObjectIdentifiers.X509Certificate, new DerOctetString(cert.Certificate.GetEncoded()));

                var fName = new Asn1EncodableVector();

                foreach (string oid in cert.BagAttributeKeys)
                {
                    // a certificate not immediately linked to a key doesn't require
                    // a localKeyID and will confuse some PKCS12 implementations.
                    //
                    // If we find one, we'll prune it out.
                    if (oid.Equals(PkcsObjectIdentifiers.Pkcs9AtLocalKeyID.Id))
                    {
                        continue;
                    }

                    Asn1Encodable entry = cert[oid];

                    // NB: Ignore any existing FriendlyName
                    if (oid.Equals(PkcsObjectIdentifiers.Pkcs9AtFriendlyName.Id))
                    {
                        continue;
                    }

                    fName.Add(new DerSequence(new DerObjectIdentifier(oid), new DerSet(entry)));
                }

                //
                // make sure we are using the local alias on store
                //
                // NB: We always set the FriendlyName based on 'certId'
                //if (cert[PkcsObjectIdentifiers.Pkcs9AtFriendlyName] == null)
                {
                    fName.Add(new DerSequence(PkcsObjectIdentifiers.Pkcs9AtFriendlyName, new DerSet(new DerBmpString(certId))));
                }

                var sBag = new SafeBag(PkcsObjectIdentifiers.CertBag, cBag.ToAsn1Object(), new DerSet(fName));

                certSeq.Add(sBag);

                doneCerts.Add(cert.Certificate);
            }

            foreach (CertId certId in _chainCerts.Keys)
            {
                var cert = (X509CertificateEntry) _chainCerts[certId];

                if (doneCerts.Contains(cert.Certificate))
                {
                    continue;
                }

                var cBag = new CertBag(PkcsObjectIdentifiers.X509Certificate, new DerOctetString(cert.Certificate.GetEncoded()));

                var fName = new Asn1EncodableVector();

                foreach (string oid in cert.BagAttributeKeys)
                {
                    // a certificate not immediately linked to a key doesn't require
                    // a localKeyID and will confuse some PKCS12 implementations.
                    //
                    // If we find one, we'll prune it out.
                    if (oid.Equals(PkcsObjectIdentifiers.Pkcs9AtLocalKeyID.Id))
                    {
                        continue;
                    }

                    fName.Add(new DerSequence(new DerObjectIdentifier(oid), new DerSet(cert[oid])));
                }

                var sBag = new SafeBag(PkcsObjectIdentifiers.CertBag, cBag.ToAsn1Object(), new DerSet(fName));

                certSeq.Add(sBag);
            }

            derEncodedBytes = new DerSequence(certSeq).GetDerEncoded();

            byte[] certBytes = CryptPbeData(true, cAlgId, password, false, derEncodedBytes);

            var cInfo = new EncryptedData(PkcsObjectIdentifiers.Data, cAlgId, new BerOctetString(certBytes));

            ContentInfo[] info = {new ContentInfo(PkcsObjectIdentifiers.Data, keyString), new ContentInfo(PkcsObjectIdentifiers.EncryptedData, cInfo.ToAsn1Object())};

            byte[] data = new AuthenticatedSafe(info).GetEncoded(_useDerEncoding ? Asn1Encodable.Der : Asn1Encodable.Ber);

            var mainInfo = new ContentInfo(PkcsObjectIdentifiers.Data, new BerOctetString(data));

            //
            // create the mac
            //
            var mSalt = new byte[20];
            random.NextBytes(mSalt);

            byte[] mac = CalculatePbeMac(OiwObjectIdentifiers.IdSha1, mSalt, MinIterations, password, false, data);

            var algId = new AlgorithmIdentifier(OiwObjectIdentifiers.IdSha1, DerNull.Instance);
            var dInfo = new DigestInfo(algId, mac);

            var mData = new MacData(dInfo, mSalt, MinIterations);

            //
            // output the Pfx
            //
            var pfx = new Pfx(mainInfo, mData);

            DerOutputStream derOut;
            if (_useDerEncoding)
            {
                derOut = new DerOutputStream(stream);
            }
            else
            {
                derOut = new BerOutputStream(stream);
            }

            derOut.WriteObject(pfx);
        }
//		private string GetHexEncoded(byte[] bytes)
//		{
//			bytes = Hex.Encode(bytes);
//
//			char[] chars = new char[bytes.Length];
//
//			for (int i = 0; i != bytes.Length; i++)
//			{
//				chars[i] = (char)bytes[i];
//			}
//
//			return new string(chars);
//		}

		private PemObject CreatePemObject(
			object			obj,
			string			algorithm,
			char[]			password,
			SecureRandom	random)
		{
			if (obj == null)
				throw new ArgumentNullException("obj");
			if (algorithm == null)
				throw new ArgumentNullException("algorithm");
			if (password == null)
				throw new ArgumentNullException("password");
			if (random == null)
				throw new ArgumentNullException("random");

			if (obj is AsymmetricCipherKeyPair)
			{
				return CreatePemObject(((AsymmetricCipherKeyPair)obj).Private, algorithm, password, random);
			}

			string type = null;
			byte[] keyData = null;

			if (obj is AsymmetricKeyParameter)
			{
				AsymmetricKeyParameter akp = (AsymmetricKeyParameter) obj;
				if (akp.IsPrivate)
				{
					string keyType;
					keyData = EncodePrivateKey(akp, out keyType);

					type = keyType + " PRIVATE KEY";
				}
			}

			if (type == null || keyData == null)
			{
				// TODO Support other types?
				throw new PemGenerationException("Object type not supported: " + obj.GetType().FullName);
			}


			string dekAlgName = algorithm.ToUpperInvariant();

			// Note: For backward compatibility
			if (dekAlgName == "DESEDE")
			{
				dekAlgName = "DES-EDE3-CBC";
			}

			int ivLength = dekAlgName.StartsWith("AES-") ? 16 : 8;

			byte[] iv = new byte[ivLength];
			random.NextBytes(iv);

			byte[] encData = PemUtilities.Crypt(true, keyData, password, dekAlgName, iv);

			IList headers = Platform.CreateArrayList(2);

			headers.Add(new PemHeader("Proc-Type", "4,ENCRYPTED"));
			headers.Add(new PemHeader("DEK-Info", dekAlgName + "," + Hex.ToHexString(iv)));

			return new PemObject(type, headers, encData);
		}
		public static KeyParameter MakeRandomKey(
            SymmetricKeyAlgorithmTag	algorithm,
            SecureRandom				random)
        {
            int keySize = GetKeySize(algorithm);
            byte[] keyBytes = new byte[(keySize + 7) / 8];
            random.NextBytes(keyBytes);
			return MakeKey(algorithm, keyBytes);
        }
		private static byte[] CreateIV(
			SecureRandom	random,
			int				ivLength)
		{
			byte[] iv = new byte[ivLength];
			random.NextBytes(iv);
			return iv;
		}
		private static byte[] EncryptKeyData(
			byte[]						rawKeyData,
			SymmetricKeyAlgorithmTag	encAlgorithm,
			char[]						passPhrase,
			SecureRandom				random,
			out S2k						s2k,
			out byte[]					iv)
		{
			IBufferedCipher c;
			try
			{
				string cName = PgpUtilities.GetSymmetricCipherName(encAlgorithm);
				c = CipherUtilities.GetCipher(cName + "/CFB/NoPadding");
			}
			catch (Exception e)
			{
				throw new PgpException("Exception creating cipher", e);
			}

			byte[] s2kIV = new byte[8];
			random.NextBytes(s2kIV);
			s2k = new S2k(HashAlgorithmTag.Sha1, s2kIV, 0x60);

			KeyParameter kp = PgpUtilities.MakeKeyFromPassPhrase(encAlgorithm, s2k, passPhrase);

			iv = new byte[c.GetBlockSize()];
			random.NextBytes(iv);

			c.Init(true, new ParametersWithRandom(new ParametersWithIV(kp, iv), random));

			return c.DoFinal(rawKeyData);
		}