/// <summary>
		/// Decrypts the specified encryptedData and extracts the digital signers if the content was also signed.
		/// </summary>
		/// <remarks>
		/// Decrypts the specified encryptedData and extracts the digital signers if the content was also signed.
		/// </remarks>
		/// <returns>The decrypted stream.</returns>
		/// <param name="encryptedData">The encrypted data.</param>
		/// <param name="signatures">A list of digital signatures if the data was both signed and encrypted.</param>
		/// <exception cref="System.ArgumentNullException">
		/// <paramref name="encryptedData"/> is <c>null</c>.
		/// </exception>
		/// <exception cref="PrivateKeyNotFoundException">
		/// The private key could not be found to decrypt the stream.
		/// </exception>
		/// <exception cref="System.OperationCanceledException">
		/// The user chose to cancel the password prompt.
		/// </exception>
		/// <exception cref="System.UnauthorizedAccessException">
		/// 3 bad attempts were made to unlock the secret key.
		/// </exception>
		/// <exception cref="Org.BouncyCastle.Bcpg.OpenPgp.PgpException">
		/// An OpenPGP error occurred.
		/// </exception>
		public Stream GetDecryptedStream (Stream encryptedData, out DigitalSignatureCollection signatures)
		{
			if (encryptedData == null)
				throw new ArgumentNullException ("encryptedData");

			using (var armored = new ArmoredInputStream (encryptedData)) {
				var factory = new PgpObjectFactory (armored);
				var obj = factory.NextPgpObject ();
				var list = obj as PgpEncryptedDataList;

				if (list == null) {
					// probably a PgpMarker...
					obj = factory.NextPgpObject ();

					list = obj as PgpEncryptedDataList;

					if (list == null)
						throw new PgpException ("Unexpected OpenPGP packet.");
				}

				PgpPublicKeyEncryptedData encrypted = null;
				PrivateKeyNotFoundException pkex = null;
				bool hasEncryptedPackets = false;
				PgpSecretKey secret = null;

				foreach (PgpEncryptedData data in list.GetEncryptedDataObjects ()) {
					if ((encrypted = data as PgpPublicKeyEncryptedData) == null)
						continue;

					hasEncryptedPackets = true;

					try {
						secret = GetSecretKey (encrypted.KeyId);
						break;
					} catch (PrivateKeyNotFoundException ex) {
						pkex = ex;
					}
				}

				if (!hasEncryptedPackets)
					throw new PgpException ("No encrypted packets found.");

				if (secret == null)
					throw pkex;

				factory = new PgpObjectFactory (encrypted.GetDataStream (GetPrivateKey (secret)));
				List<IDigitalSignature> onepassList = null;
				PgpSignatureList signatureList = null;
				PgpCompressedData compressed = null;
				var memory = new MemoryBlockStream ();

				obj = factory.NextPgpObject ();
				while (obj != null) {
					if (obj is PgpCompressedData) {
						if (compressed != null)
							throw new PgpException ("Recursive compression packets are not supported.");

						compressed = (PgpCompressedData) obj;
						factory = new PgpObjectFactory (compressed.GetDataStream ());
					} else if (obj is PgpOnePassSignatureList) {
						if (memory.Length == 0) {
							var onepasses = (PgpOnePassSignatureList) obj;

							onepassList = new List<IDigitalSignature> ();

							for (int i = 0; i < onepasses.Count; i++) {
								var onepass = onepasses[i];
								var pubkey = PublicKeyRingBundle.GetPublicKey (onepass.KeyId);

								if (pubkey == null) {
									// too messy, pretend we never found a one-pass signature list
									onepassList = null;
									break;
								}

								onepass.InitVerify (pubkey);

								var signature = new OpenPgpDigitalSignature (pubkey, onepass) {
									PublicKeyAlgorithm = GetPublicKeyAlgorithm (onepass.KeyAlgorithm),
									DigestAlgorithm = GetDigestAlgorithm (onepass.HashAlgorithm),
								};

								onepassList.Add (signature);
							}
						}
					} else if (obj is PgpSignatureList) {
						signatureList = (PgpSignatureList) obj;
					} else if (obj is PgpLiteralData) {
						var literal = (PgpLiteralData) obj;

						using (var stream = literal.GetDataStream ()) {
							var buffer = new byte[4096];
							int nread;

							while ((nread = stream.Read (buffer, 0, buffer.Length)) > 0) {
								if (onepassList != null) {
									// update our one-pass signatures...
									for (int index = 0; index < nread; index++) {
										byte c = buffer[index];

										for (int i = 0; i < onepassList.Count; i++) {
											var pgp = (OpenPgpDigitalSignature) onepassList[i];
											pgp.OnePassSignature.Update (c);
										}
									}
								}

								memory.Write (buffer, 0, nread);
							}
						}
					}

					obj = factory.NextPgpObject ();
				}

				memory.Position = 0;

				if (signatureList != null) {
					if (onepassList != null && signatureList.Count == onepassList.Count) {
						for (int i = 0; i < onepassList.Count; i++) {
							var pgp = (OpenPgpDigitalSignature) onepassList[i];
							pgp.CreationDate = signatureList[i].CreationTime;
							pgp.Signature = signatureList[i];
						}

						signatures = new DigitalSignatureCollection (onepassList);
					} else {
						signatures = GetDigitalSignatures (signatureList, memory);
						memory.Position = 0;
					}
				} else {
					signatures = null;
				}

				return memory;
			}
		}
		DigitalSignatureCollection GetDigitalSignatures (PgpSignatureList signatureList, Stream content)
		{
			var signatures = new List<IDigitalSignature> ();
			var buf = new byte[4096];
			int nread;

			for (int i = 0; i < signatureList.Count; i++) {
				var pubkey = PublicKeyRingBundle.GetPublicKey (signatureList[i].KeyId);
				var signature = new OpenPgpDigitalSignature (pubkey, signatureList[i]) {
					PublicKeyAlgorithm = GetPublicKeyAlgorithm (signatureList[i].KeyAlgorithm),
					DigestAlgorithm = GetDigestAlgorithm (signatureList[i].HashAlgorithm),
					CreationDate = signatureList[i].CreationTime,
				};

				if (pubkey != null)
					signatureList[i].InitVerify (pubkey);

				signatures.Add (signature);
			}

			var memory = content as MemoryStream;
			if (memory != null) {
				// We can optimize things a bit if we've got a memory stream...
				var buffer = memory.GetBuffer ();

				for (int index = (int) memory.Position; index < (int) memory.Length; index++) {
					byte c = buffer[index];

					for (int i = 0; i < signatures.Count; i++) {
						if (signatures[i].SignerCertificate != null) {
							var pgp = (OpenPgpDigitalSignature) signatures[i];
							pgp.Signature.Update (c);
						}
					}
				}
			} else {
				while ((nread = content.Read (buf, 0, buf.Length)) > 0) {
					for (int i = 0; i < signatures.Count; i++) {
						if (signatures[i].SignerCertificate != null) {
							var pgp = (OpenPgpDigitalSignature) signatures[i];
							pgp.Signature.Update (buf, 0, nread);
						}
					}
				}
			}

			return new DigitalSignatureCollection (signatures);
		}
		DigitalSignatureCollection GetDigitalSignatures (PgpSignatureList signatureList, Stream content)
		{
			var signatures = new List<IDigitalSignature> ();
			var buf = new byte[4096];
			int nread;

			for (int i = 0; i < signatureList.Count; i++) {
				var pubkey = PublicKeyRingBundle.GetPublicKey (signatureList[i].KeyId);
				var signature = new OpenPgpDigitalSignature (pubkey, signatureList[i]) {
					PublicKeyAlgorithm = GetPublicKeyAlgorithm (signatureList[i].KeyAlgorithm),
					DigestAlgorithm = GetDigestAlgorithm (signatureList[i].HashAlgorithm),
					CreationDate = signatureList[i].CreationTime,
				};

				if (pubkey != null)
					signatureList[i].InitVerify (pubkey);

				signatures.Add (signature);
			}

			while ((nread = content.Read (buf, 0, buf.Length)) > 0) {
				for (int i = 0; i < signatures.Count; i++) {
					if (signatures[i].SignerCertificate != null) {
						var pgp = (OpenPgpDigitalSignature) signatures[i];
						pgp.Signature.Update (buf, 0, nread);
					}
				}
			}

			return new DigitalSignatureCollection (signatures);
		}