Generator for PGP signatures.
Beispiel #1
1
        /// <summary>
        /// Attempt to encrypt a message using PGP with the specified public key(s).
        /// </summary>
        /// <param name="messageStream">Stream containing the message to encrypt.</param>
        /// <param name="fileName">File name of for the message.</param>
        /// <param name="signedAndEncryptedMessageStream">Stream to write the encrypted message into.</param>
        /// <param name="senderPublicKey">The BouncyCastle public key associated with the signature.</param>
        /// <param name="senderPrivateKey">The BouncyCastle private key to be used for signing.</param>
        /// <param name="recipientPublicKeys">Collection of BouncyCastle public keys to be used for encryption.</param>
        /// <param name="hashAlgorithmTag">The hash algorithm tag to use for signing.</param>
        /// <param name="symmetricKeyAlgorithmTag">The symmetric key algorithm tag to use for encryption.</param>
        /// <param name="armor">Whether to wrap the message with ASCII armor.</param>
        /// <returns>Whether the encryption completed successfully.</returns>
        public static bool SignAndEncrypt(Stream messageStream, string fileName, Stream signedAndEncryptedMessageStream, PgpPublicKey senderPublicKey, PgpPrivateKey senderPrivateKey, IEnumerable<PgpPublicKey> recipientPublicKeys, HashAlgorithmTag hashAlgorithmTag = HashAlgorithmTag.Sha256, SymmetricKeyAlgorithmTag symmetricKeyAlgorithmTag = SymmetricKeyAlgorithmTag.TripleDes, bool armor = true)
        {
            // Create a signature generator.
            PgpSignatureGenerator signatureGenerator = new PgpSignatureGenerator(senderPublicKey.Algorithm, hashAlgorithmTag);
            signatureGenerator.InitSign(PgpSignature.BinaryDocument, senderPrivateKey);

            // Add the public key user ID.
            foreach (string userId in senderPublicKey.GetUserIds())
            {
                PgpSignatureSubpacketGenerator signatureSubGenerator = new PgpSignatureSubpacketGenerator();
                signatureSubGenerator.SetSignerUserId(false, userId);
                signatureGenerator.SetHashedSubpackets(signatureSubGenerator.Generate());
                break;
            }

            // Allow any of the corresponding keys to be used for decryption.
            PgpEncryptedDataGenerator encryptedDataGenerator = new PgpEncryptedDataGenerator(SymmetricKeyAlgorithmTag.TripleDes, true, new SecureRandom());
            foreach (PgpPublicKey publicKey in recipientPublicKeys)
            {
                encryptedDataGenerator.AddMethod(publicKey);
            }

            // Handle optional ASCII armor.
            if (armor)
            {
                using (Stream armoredStream = new ArmoredOutputStream(signedAndEncryptedMessageStream))
                {
                    using (Stream encryptedStream = encryptedDataGenerator.Open(armoredStream, new byte[Constants.LARGEBUFFERSIZE]))
                    {
                        PgpCompressedDataGenerator compressedDataGenerator = new PgpCompressedDataGenerator(CompressionAlgorithmTag.Uncompressed);
                        using (Stream compressedStream = compressedDataGenerator.Open(encryptedStream))
                        {
                            signatureGenerator.GenerateOnePassVersion(false).Encode(compressedStream);

                            PgpLiteralDataGenerator literalDataGenerator = new PgpLiteralDataGenerator();
                            using (Stream literalStream = literalDataGenerator.Open(compressedStream, PgpLiteralData.Binary,
                                fileName, DateTime.Now, new byte[Constants.LARGEBUFFERSIZE]))
                            {
                                // Process each character in the message.
                                int messageChar;
                                while ((messageChar = messageStream.ReadByte()) >= 0)
                                {
                                    literalStream.WriteByte((byte)messageChar);
                                    signatureGenerator.Update((byte)messageChar);
                                }
                            }

                            signatureGenerator.Generate().Encode(compressedStream);
                        }
                    }
                }
            }
            else
            {
                using (Stream encryptedStream = encryptedDataGenerator.Open(signedAndEncryptedMessageStream, new byte[Constants.LARGEBUFFERSIZE]))
                {
                    PgpCompressedDataGenerator compressedDataGenerator = new PgpCompressedDataGenerator(CompressionAlgorithmTag.Uncompressed);
                    using (Stream compressedStream = compressedDataGenerator.Open(encryptedStream))
                    {
                        signatureGenerator.GenerateOnePassVersion(false).Encode(compressedStream);

                        PgpLiteralDataGenerator literalDataGenerator = new PgpLiteralDataGenerator();
                        using (Stream literalStream = literalDataGenerator.Open(compressedStream, PgpLiteralData.Binary,
                            fileName, DateTime.Now, new byte[Constants.LARGEBUFFERSIZE]))
                        {
                            // Process each character in the message.
                            int messageChar;
                            while ((messageChar = messageStream.ReadByte()) >= 0)
                            {
                                literalStream.WriteByte((byte)messageChar);
                                signatureGenerator.Update((byte)messageChar);
                            }
                        }

                        signatureGenerator.Generate().Encode(compressedStream);
                    }
                }
            }

            return true;
        }
Beispiel #2
0
        /// <summary>
        /// 加密并签名
        /// 使用接受方的公钥进行加密
        /// 使用发送方的私钥进行签名
        /// 先压缩,再加密,再签名
        /// </summary>
        /// <param name="kp"></param>
        /// <param name="cfg"></param>
        /// <param name="inputFile"></param>
        /// <param name="outputStream">普通的stream,或者Org.BouncyCastle.Bcpg.ArmoredOutputStream(如果使用加密文件使用ASCII)</param>
        public static void EncryptAndSign(System.IO.FileInfo inputFile, System.IO.Stream outputStream, GpgKeyPair kp, GpgEncryptSignCfg cfg)
        {
            var sr = new Org.BouncyCastle.Security.SecureRandom();
            var pgpEncryptedDataGenerator = new Org.BouncyCastle.Bcpg.OpenPgp.PgpEncryptedDataGenerator(cfg.SymmetricKeyAlgorithmTag, cfg.IntegrityProtected, sr);

            pgpEncryptedDataGenerator.AddMethod(kp.PublickKey);
            var pgpCompressedDataGenerator = new Org.BouncyCastle.Bcpg.OpenPgp.PgpCompressedDataGenerator(cfg.CompressionAlgorithmTag);
            var pgpLiteralDataGenerator    = new Org.BouncyCastle.Bcpg.OpenPgp.PgpLiteralDataGenerator();

            using (var fs = inputFile.OpenRead())
                using (var outputStreamEncrypted = pgpEncryptedDataGenerator.Open(outputStream, new byte[cfg.BufferSize]))
                    using (var outputStreamEncryptedCompressed = pgpCompressedDataGenerator.Open(outputStreamEncrypted))
                        using (var outputStreamEncryptedCompressedLiteral = pgpLiteralDataGenerator.Open(outputStreamEncryptedCompressed,
                                                                                                         Org.BouncyCastle.Bcpg.OpenPgp.PgpLiteralData.Binary, inputFile.Name, inputFile.Length, inputFile.LastWriteTime))
                        {
                            var pgpSignatureGenerator = new Org.BouncyCastle.Bcpg.OpenPgp.PgpSignatureGenerator(kp.PrivateKeySecreted.PublicKey.Algorithm,
                                                                                                                Org.BouncyCastle.Bcpg.HashAlgorithmTag.Sha256);
                            pgpSignatureGenerator.InitSign(Org.BouncyCastle.Bcpg.OpenPgp.PgpSignature.BinaryDocument, kp.PrivateKey);
                            var userId = kp.PrivateKeySecreted.PublicKey.GetUserIds().Cast <string>().First();
                            var pgpSignatureSubpacketGenerator = new Org.BouncyCastle.Bcpg.OpenPgp.PgpSignatureSubpacketGenerator();
                            pgpSignatureSubpacketGenerator.SetSignerUserId(cfg.IsCritical, userId);
                            pgpSignatureGenerator.SetHashedSubpackets(pgpSignatureSubpacketGenerator.Generate());
                            pgpSignatureGenerator.GenerateOnePassVersion(cfg.IsNested).Encode(outputStreamEncryptedCompressedLiteral);

                            int dataLenght = 0;
                            var buffer     = new byte[cfg.BufferSize];
                            while ((dataLenght = fs.Read(buffer, 0, buffer.Length)) > 0)
                            {
                                outputStreamEncryptedCompressedLiteral.Write(buffer, 0, dataLenght);
                                pgpSignatureGenerator.Update(buffer, 0, dataLenght);
                            }
                            pgpSignatureGenerator.Generate().Encode(outputStreamEncryptedCompressedLiteral);
                        }
        }
Beispiel #3
0
		private string signEnvelopeData(string msg)
		{
			Stream privateKeyStream = getPrivateKeyStream(_privateKey);
			
			MemoryStream result = new MemoryStream();
			ArmoredOutputStream aOut = new ArmoredOutputStream(result);
			BcpgOutputStream bOut = null;
			char[] privateKeyPassword = _passPhrase.ToCharArray();
			var utf8Encoding = new System.Text.UTF8Encoding();
			try
			{
				PgpSecretKey sk = readSecretKey(privateKeyStream);
				PgpPrivateKey pk = sk.ExtractPrivateKey(privateKeyPassword);
				PgpSignatureGenerator sigGen = new PgpSignatureGenerator(sk.PublicKey.Algorithm,HashAlgorithmTag.Sha256);
				PgpSignatureSubpacketGenerator spGen = new PgpSignatureSubpacketGenerator();
				                                                        
				var enumerator = sk.PublicKey.GetUserIds().GetEnumerator();
				if(enumerator.MoveNext())
				{
					spGen.SetSignerUserId(false, (string)enumerator.Current);
					sigGen.SetHashedSubpackets(spGen.Generate());
				}
				
				aOut.BeginClearText(HashAlgorithmTag.Sha256);
				sigGen.InitSign(PgpSignature.CanonicalTextDocument, pk);
				byte[] msgBytes = utf8Encoding.GetBytes(msg);
				sigGen.Update(msgBytes, 0, msgBytes.Length);
				aOut.Write(msgBytes, 0, msgBytes.Length);
				bOut = new BcpgOutputStream(aOut);
				aOut.EndClearText();
				sigGen.Generate().Encode(bOut);
				using (BinaryReader br = new BinaryReader(result))
				{
					br.BaseStream.Position = 0;
					return utf8Encoding.GetString(br.ReadBytes((int)result.Length));
				}
			}
			catch (Exception e)
			{	Console.WriteLine("This happened: " + e.Message);
				throw new Exception("Signing Failed: " + e.Message);
			}
			finally
			{
				try
				{
					if (privateKeyStream != null)
						privateKeyStream.Close();
					//if(bOut != null)
						//bOut.Close();
					//aOut.Close();
					result.Close();
				} catch (IOException) {}
			}
		}
Beispiel #4
0
        public PgpSecretKey(
			int							certificationLevel,
			PgpKeyPair					keyPair,
			string						id,
			SymmetricKeyAlgorithmTag	encAlgorithm,
			char[]						passPhrase,
			bool						useSHA1,
			PgpSignatureSubpacketVector	hashedPackets,
			PgpSignatureSubpacketVector	unhashedPackets,
			SecureRandom				rand)
            : this(keyPair, encAlgorithm, passPhrase, useSHA1, rand)
        {
            try
            {
                this.trust = null;
                this.ids = new ArrayList();
                ids.Add(id);

                this.idTrusts = new ArrayList();
                idTrusts.Add(null);

                this.idSigs = new ArrayList();

                PgpSignatureGenerator sGen = new PgpSignatureGenerator(
                    keyPair.PublicKey.Algorithm, HashAlgorithmTag.Sha1);

                //
                // Generate the certification
                //
                sGen.InitSign(certificationLevel, keyPair.PrivateKey);

                sGen.SetHashedSubpackets(hashedPackets);
                sGen.SetUnhashedSubpackets(unhashedPackets);

                PgpSignature certification = sGen.GenerateCertification(id, keyPair.PublicKey);
                this.pub = PgpPublicKey.AddCertification(keyPair.PublicKey, id, certification);

                ArrayList sigList = new ArrayList();
                sigList.Add(certification);
                idSigs.Add(sigList);
            }
            catch (PgpException e)
            {
                throw e;
            }
            catch (Exception e)
            {
                throw new PgpException("Exception encrypting key", e);
            }
        }
        public static void SignFile(Stream input, Stream outputStream, Stream keyIn, char[] pass)
        {
            var secretKey = ReadSecretKey(keyIn);
            var privateKey = secretKey.ExtractPrivateKey(pass);

            var signatureGenerator = new PgpSignatureGenerator(secretKey.PublicKey.Algorithm, HashAlgorithmTag.Sha1);
            var subpacketGenerator = new PgpSignatureSubpacketGenerator();

            signatureGenerator.InitSign(PgpSignature.CanonicalTextDocument, privateKey);
            foreach (string userId in secretKey.PublicKey.GetUserIds())
            {
                var signatureSubpacketGenerator = new PgpSignatureSubpacketGenerator();
                signatureSubpacketGenerator.SetSignerUserId(isCritical: false, userId: userId);
                signatureGenerator.SetHashedSubpackets(signatureSubpacketGenerator.Generate());
                // Just the first one!
                break;
            }

            // Closing armouredOutputStream does not close the underlying stream
            var armouredOutputStream = new ArmoredOutputStream(outputStream);
            using (var bcpgOutputStream = new BcpgOutputStream(armouredOutputStream))
            {
                armouredOutputStream.BeginClearText(HashAlgorithmTag.Sha1);

                int chr;
                while ((chr = input.ReadByte()) > 0)
                {
                    signatureGenerator.Update((byte)chr);
                    bcpgOutputStream.Write((byte)chr);
                }

                // For some reason we need to add a trailing newline
                bcpgOutputStream.Write((byte)'\n'); 

                armouredOutputStream.EndClearText();

                signatureGenerator.Generate().Encode(bcpgOutputStream);
            }
        }
        private static void CreateSignature(
			string	fileName,
			Stream	keyIn,
			Stream	outputStream,
			char[]	pass,
			bool	armor)
        {
            if (armor)
            {
                outputStream = new ArmoredOutputStream(outputStream);
            }

            IPgpSecretKey pgpSec = PgpExampleUtilities.ReadSecretKey(keyIn);
            IPgpPrivateKey pgpPrivKey = pgpSec.ExtractPrivateKey(pass);
            PgpSignatureGenerator sGen = new PgpSignatureGenerator(
                pgpSec.PublicKey.Algorithm, HashAlgorithmTag.Sha1);

            sGen.InitSign(PgpSignature.BinaryDocument, pgpPrivKey);

            BcpgOutputStream bOut = new BcpgOutputStream(outputStream);

            Stream fIn = File.OpenRead(fileName);

            int ch;
            while ((ch = fIn.ReadByte()) >= 0)
            {
                sGen.Update((byte)ch);
            }

            fIn.Close();

            sGen.Generate().Encode(bOut);

            if (armor)
            {
                outputStream.Close();
            }
        }
Beispiel #7
0
        private static void WriteOutputAndSign(Stream compressedOut, Stream literalOut, FileStream inputFile, PgpSignatureGenerator signatureGenerator)
        {
            int length = 0;
            byte[] buf = new byte[BufferSize];
            while ((length = inputFile.Read(buf, 0, buf.Length)) > 0)
            {
                literalOut.Write(buf, 0, length);
                signatureGenerator.Update(buf, 0, length);
            }

            signatureGenerator.Generate().Encode(compressedOut);
        }
        /**
        * Generate an encapsulated signed file.
        *
        * @param fileName
        * @param keyIn
        * @param outputStream
        * @param pass
        * @param armor
        */
        private static void SignFile(
            string	fileName,
            Stream	keyIn,
            Stream	outputStream,
            char[]	pass,
            bool	armor,
			bool	compress)
        {
            if (armor)
            {
                outputStream = new ArmoredOutputStream(outputStream);
            }

            PgpSecretKey pgpSec = PgpExampleUtilities.ReadSecretKey(keyIn);
            PgpPrivateKey pgpPrivKey = pgpSec.ExtractPrivateKey(pass);
            PgpSignatureGenerator sGen = new PgpSignatureGenerator(pgpSec.PublicKey.Algorithm, HashAlgorithmTag.Sha1);

            sGen.InitSign(PgpSignature.BinaryDocument, pgpPrivKey);
            foreach (string userId in pgpSec.PublicKey.GetUserIds())
            {
                PgpSignatureSubpacketGenerator spGen = new PgpSignatureSubpacketGenerator();
                spGen.SetSignerUserId(false, userId);
                sGen.SetHashedSubpackets(spGen.Generate());
                // Just the first one!
                break;
            }

            Stream cOut = outputStream;
			PgpCompressedDataGenerator cGen = null;
			if (compress)
			{
				cGen = new PgpCompressedDataGenerator(CompressionAlgorithmTag.ZLib);

				cOut = cGen.Open(cOut);
			}

			BcpgOutputStream bOut = new BcpgOutputStream(cOut);

            sGen.GenerateOnePassVersion(false).Encode(bOut);

            FileInfo					file = new FileInfo(fileName);
            PgpLiteralDataGenerator     lGen = new PgpLiteralDataGenerator();
            Stream						lOut = lGen.Open(bOut, PgpLiteralData.Binary, file);
            FileStream					fIn = file.OpenRead();
            int                         ch = 0;

			while ((ch = fIn.ReadByte()) >= 0)
            {
                lOut.WriteByte((byte) ch);
                sGen.Update((byte)ch);
            }

			fIn.Close();
			lGen.Close();

			sGen.Generate().Encode(bOut);

			if (cGen != null)
			{
				cGen.Close();
			}

			if (armor)
			{
				outputStream.Close();
			}
        }
Beispiel #9
0
        private PgpSignatureGenerator InitSignatureGenerator(Stream compressedOut)
        {
            const bool IsCritical = false;

            const bool IsNested = false;

            PublicKeyAlgorithmTag tag = m_encryptionKeys.SecretKey.PublicKey.Algorithm;

            PgpSignatureGenerator pgpSignatureGenerator =

                new PgpSignatureGenerator(tag, HashAlgorithmTag.Sha1);

            pgpSignatureGenerator.InitSign(PgpSignature.BinaryDocument, m_encryptionKeys.PrivateKey);

            foreach (string userId in m_encryptionKeys.SecretKey.PublicKey.GetUserIds())
            {

                PgpSignatureSubpacketGenerator subPacketGenerator =
                   new PgpSignatureSubpacketGenerator();

                subPacketGenerator.SetSignerUserId(IsCritical, userId);

                pgpSignatureGenerator.SetHashedSubpackets(subPacketGenerator.Generate());

                // Just the first one!

                break;

            }

            pgpSignatureGenerator.GenerateOnePassVersion(IsNested).Encode(compressedOut);

            return pgpSignatureGenerator;
        }
        private static void updateSignatureGeneratorWithInputBytes(Stream unsigendContent,
				  PgpSignatureGenerator signatureGenerator, Stream lOut)
        {
            int ch;

            while ((ch = unsigendContent.ReadByte()) >= 0) {
                lOut.WriteByte((byte)ch);
                signatureGenerator.Update((byte)ch);
            }
        }
        private static PgpPublicKey CertifiedPublicKey(
            int certificationLevel,
            PgpKeyPair keyPair,
            string id,
            PgpSignatureSubpacketVector hashedPackets,
            PgpSignatureSubpacketVector unhashedPackets,
            HashAlgorithmTag hashAlgorithm)
        {
            PgpSignatureGenerator sGen;
            try
            {
                sGen = new PgpSignatureGenerator(keyPair.PublicKey.Algorithm, hashAlgorithm);
            }
            catch (Exception e)
            {
                throw new PgpException("Creating signature generator: " + e.Message, e);
            }

            //
            // Generate the certification
            //
            sGen.InitSign(certificationLevel, keyPair.PrivateKey);

            sGen.SetHashedSubpackets(hashedPackets);
            sGen.SetUnhashedSubpackets(unhashedPackets);

            try
            {
                PgpSignature certification = sGen.GenerateCertification(id, keyPair.PublicKey);
                return PgpPublicKey.AddCertification(keyPair.PublicKey, id, certification);
            }
            catch (Exception e)
            {
                throw new PgpException("Exception doing certification: " + e.Message, e);
            }
        }
        /// <summary>
        /// Sign data using key
        /// </summary>
        /// <param name="data">Data to sign</param>
        /// <param name="key">Email address of key</param>
        /// <returns>Returns ascii armored signature</returns>
        public string SignClear(string data, string key, Encoding encoding, Dictionary<string, string> headers)
        {
            Context = new CryptoContext(Context);

            var senderKey = GetSecretKeyForSigning(key);
            if (senderKey == null)
                throw new SecretKeyNotFoundException("Error, unable to locate signing key \"" + key + "\".");

            // Setup signature stuff //
            var signatureData = new PgpSignatureGenerator(senderKey.PublicKey.Algorithm, HashAlgorithmTag.Sha1);
            signatureData.InitSign(PgpSignature.CanonicalTextDocument, senderKey.ExtractPrivateKey(Context.Password));

            foreach (string userId in senderKey.PublicKey.GetUserIds())
            {
                var subPacketGenerator = new PgpSignatureSubpacketGenerator();

                subPacketGenerator.SetSignerUserId(false, userId);
                signatureData.SetHashedSubpackets(subPacketGenerator.Generate());

                // Just the first one!
                break;
            }

            // //

            using (var sout = new MemoryStream())
            {
                using (var armoredOut = new ArmoredOutputStream(sout))
                {
                    foreach (var header in headers)
                        armoredOut.SetHeader(header.Key, header.Value);

                    armoredOut.BeginClearText(HashAlgorithmTag.Sha1);

                    // Remove any extra trailing whitespace.
                    // this should not include \r or \n.
                    data = data.TrimEnd(null);

                    using (var stringReader = new StringReader(data))
                    {
                        do
                        {
                            var line = stringReader.ReadLine();
                            if (line == null)
                                break;

                            // Lines must have all white space removed
                            line = line.TrimEnd(null);
                            line = line.TrimEnd(new char[] {' ', '\t', '\r', '\n'});

                            line += "\r\n";

                            signatureData.Update(encoding.GetBytes(line));
                            armoredOut.Write(encoding.GetBytes(line));
                        }
                        while (true);
                    }

                    // Write extra line before signature block.
                    armoredOut.Write(encoding.GetBytes("\r\n"));
                    armoredOut.EndClearText();

                    using (var outputStream = new BcpgOutputStream(armoredOut))
                    {

                        signatureData.Generate().Encode(outputStream);
                    }
                }

                return encoding.GetString(sout.ToArray());
            }
        }
Beispiel #13
0
        /// <summary>
        /// Attempt to sign a PGP message using the specific private key.
        /// </summary>
        /// <param name="messageStream">Stream containing the message to sign.</param>
        /// <param name="signatureStream">Stream to write the signature into.</param>
        /// <param name="senderPublicKey">The BouncyCastle public key associated with the signature.</param>
        /// <param name="senderPrivateKey">The BouncyCastle private key to be used for signing.</param>
        /// <param name="hashAlgorithmTag">The hash algorithm tag to use for signing.</param>
        /// <param name="armor">Whether to wrap the message with ASCII armor.</param>
        /// <returns>Whether the signature completed successfully.</returns>
        public static bool Sign(Stream messageStream, Stream signatureStream, PgpPublicKey senderPublicKey, PgpPrivateKey senderPrivateKey, HashAlgorithmTag hashAlgorithmTag = HashAlgorithmTag.Sha256, bool armor = true)
        {
            // Create a signature generator.
            PgpSignatureGenerator signatureGenerator = new PgpSignatureGenerator(senderPublicKey.Algorithm, hashAlgorithmTag);
            signatureGenerator.InitSign(PgpSignature.BinaryDocument, senderPrivateKey);

            // Add the public key user ID.
            foreach (string userId in senderPublicKey.GetUserIds())
            {
                PgpSignatureSubpacketGenerator signatureSubGenerator = new PgpSignatureSubpacketGenerator();
                signatureSubGenerator.SetSignerUserId(false, userId);
                signatureGenerator.SetHashedSubpackets(signatureSubGenerator.Generate());
                break;
            }

            // Handle ASCII armor.
            if (armor)
            {
                using (ArmoredOutputStream armoredStream = new ArmoredOutputStream(signatureStream))
                {
                    armoredStream.BeginClearText(hashAlgorithmTag);

                    // Process each character in the message.
                    int messageChar;
                    while ((messageChar = messageStream.ReadByte()) >= 0)
                    {
                        armoredStream.WriteByte((byte)messageChar);
                        signatureGenerator.Update((byte)messageChar);
                    }

                    armoredStream.EndClearText();

                    using (BcpgOutputStream bcpgStream = new BcpgOutputStream(armoredStream))
                    {
                        signatureGenerator.Generate().Encode(bcpgStream);
                    }
                }
            }
            else
            {
                // Process each character in the message.
                int messageChar;
                while ((messageChar = messageStream.ReadByte()) >= 0)
                {
                    signatureGenerator.Update((byte)messageChar);
                }

                signatureGenerator.Generate().Encode(signatureStream);
            }

            return true;
        }
        /// <summary>
        /// Add a subkey with specific hashed and unhashed packets associated with it and
        /// default certification.
        /// </summary>
        /// <param name="keyPair">Public/private key pair.</param>
        /// <param name="hashedPackets">Hashed packet values to be included in certification.</param>
        /// <param name="unhashedPackets">Unhashed packets values to be included in certification.</param>
        /// <param name="hashAlgorithm">The hash algorithm.</param>
        /// <exception cref="Org.BouncyCastle.Bcpg.OpenPgp.PgpException">exception adding subkey: </exception>
        /// <exception cref="PgpException"></exception>
        public void AddSubKey(
            PgpKeyPair keyPair,
            PgpSignatureSubpacketVector hashedPackets,
            PgpSignatureSubpacketVector unhashedPackets,
            HashAlgorithmTag hashAlgorithm)
        {
            try
            {
                var sGen = new PgpSignatureGenerator(masterKey.PublicKey.Algorithm, hashAlgorithm);

                //
                // Generate the certification
                //
                sGen.InitSign(PgpSignature.SubkeyBinding, masterKey.PrivateKey);

                sGen.SetHashedSubpackets(hashedPackets);
                sGen.SetUnhashedSubpackets(unhashedPackets);

                IList subSigs = Platform.CreateArrayList();
                subSigs.Add(sGen.GenerateCertification(masterKey.PublicKey, keyPair.PublicKey));

                keys.Add(new PgpSecretKey(keyPair.PrivateKey, new PgpPublicKey(keyPair.PublicKey, null, subSigs), encAlgorithm, passPhrase, useSha1, rand));
            }
            catch (PgpException)
            {
                throw;
            }
            catch (Exception e)
            {
                throw new PgpException("exception adding subkey: ", e);
            }
        }
		/// <summary>
		/// Cryptographically signs and encrypts the specified content for the specified recipients.
		/// </summary>
		/// <remarks>
		/// Cryptographically signs and encrypts the specified content for the specified recipients.
		/// </remarks>
		/// <returns>A new <see cref="MimeKit.MimePart"/> instance
		/// containing the encrypted data.</returns>
		/// <param name="signer">The signer.</param>
		/// <param name="digestAlgo">The digest algorithm to use for signing.</param>
		/// <param name="cipherAlgo">The encryption algorithm.</param>
		/// <param name="recipients">The recipients.</param>
		/// <param name="content">The content.</param>
		/// <exception cref="System.ArgumentNullException">
		/// <para><paramref name="signer"/> is <c>null</c>.</para>
		/// <para>-or-</para>
		/// <para><paramref name="recipients"/> is <c>null</c>.</para>
		/// <para>-or-</para>
		/// <para><paramref name="content"/> is <c>null</c>.</para>
		/// </exception>
		/// <exception cref="System.ArgumentException">
		/// <para><paramref name="signer"/> cannot be used for signing.</para>
		/// <para>-or-</para>
		/// <para>One or more of the recipient keys cannot be used for encrypting.</para>
		/// <para>-or-</para>
		/// <para>No recipients were specified.</para>
		/// </exception>
		/// <exception cref="System.NotSupportedException">
		/// The specified encryption algorithm is not supported.
		/// </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>
		public MimePart SignAndEncrypt (PgpSecretKey signer, DigestAlgorithm digestAlgo, EncryptionAlgorithm cipherAlgo, IEnumerable<PgpPublicKey> recipients, Stream content)
		{
			// TODO: document the exceptions that can be thrown by BouncyCastle

			if (signer == null)
				throw new ArgumentNullException ("signer");

			if (!signer.IsSigningKey)
				throw new ArgumentException ("The specified secret key cannot be used for signing.", "signer");

			if (recipients == null)
				throw new ArgumentNullException ("recipients");

			if (content == null)
				throw new ArgumentNullException ("content");

			var encrypter = new PgpEncryptedDataGenerator (GetSymmetricKeyAlgorithm (cipherAlgo), true);
			var hashAlgorithm = GetHashAlgorithm (digestAlgo);
			int count = 0;

			foreach (var recipient in recipients) {
				if (!recipient.IsEncryptionKey)
					throw new ArgumentException ("One or more of the recipient keys cannot be used for encrypting.", "recipients");

				encrypter.AddMethod (recipient);
				count++;
			}

			if (count == 0)
				throw new ArgumentException ("No recipients specified.", "recipients");

			var compresser = new PgpCompressedDataGenerator (CompressionAlgorithmTag.ZLib);

			using (var compressed = new MemoryBlockStream ()) {
				using (var signed = compresser.Open (compressed)) {
					var signatureGenerator = new PgpSignatureGenerator (signer.PublicKey.Algorithm, hashAlgorithm);
					signatureGenerator.InitSign (PgpSignature.CanonicalTextDocument, GetPrivateKey (signer));
					var subpacket = new PgpSignatureSubpacketGenerator ();

					foreach (string userId in signer.PublicKey.GetUserIds ()) {
						subpacket.SetSignerUserId (false, userId);
						break;
					}

					signatureGenerator.SetHashedSubpackets (subpacket.Generate ());

					var onepass = signatureGenerator.GenerateOnePassVersion (false);
					onepass.Encode (signed);

					var literalGenerator = new PgpLiteralDataGenerator ();
					using (var literal = literalGenerator.Open (signed, 't', "mime.txt", content.Length, DateTime.Now)) {
						var buf = new byte[4096];
						int nread;

						while ((nread = content.Read (buf, 0, buf.Length)) > 0) {
							signatureGenerator.Update (buf, 0, nread);
							literal.Write (buf, 0, nread);
						}

						literal.Flush ();
					}

					var signature = signatureGenerator.Generate ();
					signature.Encode (signed);

					signed.Flush ();
				}

				compressed.Position = 0;

				var memory = new MemoryBlockStream ();
				using (var armored = new ArmoredOutputStream (memory)) {
					using (var encrypted = encrypter.Open (armored, compressed.Length)) {
						compressed.CopyTo (encrypted, 4096);
						encrypted.Flush ();
					}

					armored.Flush ();
				}

				memory.Position = 0;

				return new MimePart ("application", "octet-stream") {
					ContentDisposition = new ContentDisposition ("attachment"),
					ContentObject = new ContentObject (memory)
				};
			}
		}
        /*
        * create a clear text signed file.
        */
        private static void SignFile(
            string	fileName,
            Stream	keyIn,
            Stream	outputStream,
            char[]	pass,
			string	digestName)
        {
			HashAlgorithmTag digest;

			if (digestName.Equals("SHA256"))
			{
				digest = HashAlgorithmTag.Sha256;
			}
			else if (digestName.Equals("SHA384"))
			{
				digest = HashAlgorithmTag.Sha384;
			}
			else if (digestName.Equals("SHA512"))
			{
				digest = HashAlgorithmTag.Sha512;
			}
			else if (digestName.Equals("MD5"))
			{
				digest = HashAlgorithmTag.MD5;
			}
			else if (digestName.Equals("RIPEMD160"))
			{
				digest = HashAlgorithmTag.RipeMD160;
			}
			else
			{
				digest = HashAlgorithmTag.Sha1;
			}

			PgpSecretKey                    pgpSecKey = PgpExampleUtilities.ReadSecretKey(keyIn);
            PgpPrivateKey                   pgpPrivKey = pgpSecKey.ExtractPrivateKey(pass);
            PgpSignatureGenerator           sGen = new PgpSignatureGenerator(pgpSecKey.PublicKey.Algorithm, digest);
            PgpSignatureSubpacketGenerator  spGen = new PgpSignatureSubpacketGenerator();

			sGen.InitSign(PgpSignature.CanonicalTextDocument, pgpPrivKey);

			IEnumerator enumerator = pgpSecKey.PublicKey.GetUserIds().GetEnumerator();
            if (enumerator.MoveNext())
            {
                spGen.SetSignerUserId(false, (string) enumerator.Current);
                sGen.SetHashedSubpackets(spGen.Generate());
            }

            Stream fIn = File.OpenRead(fileName);
			ArmoredOutputStream aOut = new ArmoredOutputStream(outputStream);

			aOut.BeginClearText(digest);

			//
			// note the last \n/\r/\r\n in the file is ignored
			//
			MemoryStream lineOut = new MemoryStream();
			int lookAhead = ReadInputLine(lineOut, fIn);

			ProcessLine(aOut, sGen, lineOut.ToArray());

			if (lookAhead != -1)
			{
				do
				{
					lookAhead = ReadInputLine(lineOut, lookAhead, fIn);

					sGen.Update((byte) '\r');
					sGen.Update((byte) '\n');

					ProcessLine(aOut, sGen, lineOut.ToArray());
				}
				while (lookAhead != -1);
			}

			fIn.Close();

			aOut.EndClearText();

			BcpgOutputStream bOut = new BcpgOutputStream(aOut);

            sGen.Generate().Encode(bOut);

            aOut.Close();
        }
		/// <summary>
		/// Sign data using key
		/// </summary>
		/// <param name="data">Data to sign</param>
		/// <param name="key">Email address of key</param>
		/// <param name="headers">Headers to add to signed message</param>
		/// <param name="wrapLines">Automatically wrap lines that are too long</param>
		/// <param name="encoding">s</param>
		/// <returns>Returns ascii armored signature</returns>
		public string SignClear(string data, string key, Encoding encoding, Dictionary<string, string> headers, bool wrapLines = true)
		{
			Context = new CryptoContext(Context);

			PgpSecretKey senderMasterKey;
			var senderKey = GetSecretKeyForSigning(key, out senderMasterKey);
			if (senderKey == null)
				throw new SecretKeyNotFoundException("Error, unable to locate signing key \"" + key + "\".");

			// Setup signature stuff //
			var signatureData = new PgpSignatureGenerator(senderKey.PublicKey.Algorithm, GetHashAlgTagFromString(Context.Digest));
			signatureData.InitSign(PgpSignature.CanonicalTextDocument, senderKey.ExtractPrivateKey(Context.PasswordCallback(senderMasterKey, senderKey)));

			foreach (string userId in senderKey.PublicKey.GetUserIds())
			{
				var subPacketGenerator = new PgpSignatureSubpacketGenerator();

				subPacketGenerator.SetSignerUserId(false, userId);
				signatureData.SetHashedSubpackets(subPacketGenerator.Generate());

				// Just the first one!
				break;
			}

			// Split any long lines if we are asked to do so.

			var mailLines = data.Split('\n');

			if (wrapLines && mailLines.Any(line => line.Length > 70))
			{
				var lines = new List<string>(mailLines);
				for (var i = 0; i < lines.Count; i++)
				{
					var line = lines[i];
					if (line.Length <= 70) continue;
					
					var newLine = line.Substring(70);
					line = line.Substring(0, 70);

					lines[i] = line;
					lines.Insert(i + 1, newLine);
				}

				var sb = new StringBuilder(data.Length + 20);
				foreach (var line in lines)
					sb.AppendLine(line.TrimEnd('\r', '\n'));

				data = sb.ToString();
			}

			// Now lets do our signing stuff

			using (var sout = new MemoryStream())
			{
				using (var armoredOut = new ArmoredOutputStream(sout))
				{
					foreach (var header in headers)
						armoredOut.SetHeader(header.Key, header.Value);

					armoredOut.BeginClearText(GetHashAlgTagFromString(Context.Digest));

					// Remove any extra trailing whitespace.
					// this should not include \r or \n.
					data = data.TrimEnd(null);

					using (var stringReader = new StringReader(data))
					{
						do
						{
							var line = stringReader.ReadLine();
							if (line == null)
								break;

							// Lines must have all white space removed
							line = line.TrimEnd(null);
							line = line.TrimEnd(' ', '\t', '\r', '\n');

							line += "\r\n";

							signatureData.Update(encoding.GetBytes(line));
							armoredOut.Write(encoding.GetBytes(line));
						}
						while (true);
					}

					// Write extra line before signature block.
					armoredOut.Write(encoding.GetBytes("\r\n"));
					armoredOut.EndClearText();

					using (var outputStream = new BcpgOutputStream(armoredOut))
						signatureData.Generate().Encode(outputStream);
				}

				return encoding.GetString(sout.ToArray());
			}
		}
        public static void SignAndEncryptFile(string actualFileName, string embeddedFileName,
             Stream privateKeyStream, string passPhrase, Stream publicKeyStream,
             bool armor, bool withIntegrityCheck, Stream outputStream)
        {
            const int BUFFER_SIZE = 1 << 16; // should always be power of 2

            if (armor)
                outputStream = new ArmoredOutputStream(outputStream);

            PgpPublicKey pubKey = ReadPublicKey(publicKeyStream);

            // Init encrypted data generator
            PgpEncryptedDataGenerator encryptedDataGenerator =
                new PgpEncryptedDataGenerator(SymmetricKeyAlgorithmTag.Cast5, withIntegrityCheck, new SecureRandom());
            encryptedDataGenerator.AddMethod(pubKey);
            Stream encryptedOut = encryptedDataGenerator.Open(outputStream, new byte[BUFFER_SIZE]);

            // Init compression
            PgpCompressedDataGenerator compressedDataGenerator = new PgpCompressedDataGenerator(CompressionAlgorithmTag.Zip);
            Stream compressedOut = compressedDataGenerator.Open(encryptedOut);

            // Init signature
            PgpSecretKeyRingBundle pgpSecBundle = new PgpSecretKeyRingBundle(PgpUtilities.GetDecoderStream(privateKeyStream));

            var pgpSecKey = ReadSecretKey(pgpSecBundle);
            if (pgpSecKey == null)
                throw new ArgumentException(pubKey.KeyId.ToString("X") + " could not be found in specified key ring bundle.", "keyId");

            PgpPrivateKey pgpPrivKey = pgpSecKey.ExtractPrivateKey(passPhrase.ToCharArray());
            PgpSignatureGenerator signatureGenerator = new PgpSignatureGenerator(pgpSecKey.PublicKey.Algorithm, HashAlgorithmTag.Sha1);
            signatureGenerator.InitSign(PgpSignature.BinaryDocument, pgpPrivKey);
            var userIds = pgpSecKey.PublicKey.GetUserIds();

            string userId = null;
            foreach (string value in userIds)
            {
                // Just the first one!
                userId = value;
                break;
            }

            if (string.IsNullOrEmpty(userId))
            {
                throw new ArgumentException(string.Format("Can't find userId in signing key. KeyId '{0}'.", pubKey.KeyId.ToString("X")));
            }

            PgpSignatureSubpacketGenerator spGen = new PgpSignatureSubpacketGenerator();
            spGen.SetSignerUserId(false, userId);
            signatureGenerator.SetHashedSubpackets(spGen.Generate());

            signatureGenerator.GenerateOnePassVersion(false).Encode(compressedOut);

            // Create the Literal Data generator output stream
            PgpLiteralDataGenerator literalDataGenerator = new PgpLiteralDataGenerator();

            // NOTE: Commented this out because it uses FileInfo to get stats on files and won't work properly

            FileInfo embeddedFile = new FileInfo(embeddedFileName);
            FileInfo actualFile = new FileInfo(actualFileName);

            if (!actualFile.Exists)
            {
                throw new FileNotFoundException(actualFile.FullName);
            }

            // TODO: Use lastwritetime from source file
            Stream literalOut = literalDataGenerator.Open(compressedOut, PgpLiteralData.Binary, embeddedFile.Name, actualFile.LastWriteTime, new byte[BUFFER_SIZE]);

            // Open the input file
            FileStream inputStream = actualFile.OpenRead();

            byte[] buf = new byte[BUFFER_SIZE];
            int len;
            while ((len = inputStream.Read(buf, 0, buf.Length)) > 0)
            {
                literalOut.Write(buf, 0, len);
                signatureGenerator.Update(buf, 0, len);
            }

            literalOut.Close();
            literalDataGenerator.Close();
            signatureGenerator.Generate().Encode(compressedOut);
            compressedOut.Close();
            compressedDataGenerator.Close();
            encryptedOut.Close();
            encryptedDataGenerator.Close();
            inputStream.Close();

            if (armor)
                outputStream.Close();
        }
        ////--------------------------------------------------- BUILDER ------------------------------------------------------
        //private  class Builder {
        //   private static char[] EMPTY_PASSWORD = new char[0];
        //   private PgpPublicKey publicKeyForEncryption = null;
        //   private PgpSecretKey secretKeyForSigning = null;
        //   private char[] secretKeyPassword = EMPTY_PASSWORD;
        //   public Builder setPublicKeyForEncryption(InputStream pgpPublicKeyIn) {
        //      Validate.notNull(pgpPublicKeyIn, "If you do not want to set public key, then simply do not call this method!");
        //      try {
        //         PgpPublicKey publicKey = KeyUtils.findPublicKeyForEncryption(pgpPublicKeyIn);
        //         if (publicKey == null) {
        //            throw new IllegalArgumentException("Cannot load public key from given input stream");
        //         }
        //         this.publicKeyForEncryption = publicKey;
        //      } catch (Exception e) {
        //         throw new IllegalArgumentException("Cannot load public key from given input stream");
        //      }
        //      return this;
        //   }
        //   public Builder setPublicKeyForEncryption(PgpPublicKey pgpPublicKey) {
        //      Validate.notNull(pgpPublicKey, "If you do not want to set public key, then simply do not call this method!");
        //      this.publicKeyForEncryption = pgpPublicKey;
        //      return this;
        //   }
        //   public Builder setSecretKeyForSigning(InputStream pgpSecretKeyIn) {
        //      Validate.notNull(pgpSecretKeyIn, "If you do not want to set private key, then simply do not call this method!");
        //      try {
        //         PgpSecretKey secretKey = KeyUtils.findSecretKeyForSigning(pgpSecretKeyIn);
        //         if (secretKey == null) {
        //            throw new IllegalArgumentException("Cannot load secret key from given input stream");
        //         }
        //         this.secretKeyForSigning = secretKey;
        //      } catch (Exception e) {
        //         throw new IllegalArgumentException("Cannot load secret key from given input stream");
        //      }
        //      return this;
        //   }
        //   public Builder setSecretKeyForSigning(PgpSecretKey pgpSecretKey) {
        //      Validate.notNull(pgpSecretKey, "If you do not want to set private key, then simply do not call this method!");
        //      this.secretKeyForSigning = pgpSecretKey;
        //      return this;
        //   }
        //   public Builder setSecretKeyPassword(char[] secretKeyPassword) {
        //      if (secretKeyPassword == null) {
        //         this.secretKeyPassword = EMPTY_PASSWORD;
        //      } else {
        //         this.secretKeyPassword = Arrays.copyOf(secretKeyPassword, secretKeyPassword.length);
        //      }
        //      return this;
        //   }
        //   public PgpEncryptor createPgpEncryptor() {
        //      return new PgpEncryptor(publicKeyForEncryption, secretKeyForSigning, secretKeyPassword);
        //   }
        //}
        //--------------------------------------------------- HELPER METHODS -----------------------------------------------
        private PgpSignatureGenerator createSignatureGenerator(PgpPrivateKey pgpPrivateKey)
        {
            PgpSignatureGenerator signatureGenerator = new PgpSignatureGenerator(
                secretKeyForSigning.PublicKey.Algorithm, HashAlgorithmTag.Sha1);

            signatureGenerator.InitSign(PgpSignature.BinaryDocument, pgpPrivateKey);

            setSignatureSubpackets(secretKeyForSigning, signatureGenerator);
            return signatureGenerator;
        }
        /// <summary>
        /// Signs then encrypts data using key and list of recipients.
        /// </summary>
        /// <param name="data">Data to encrypt</param>
        /// <param name="key">Signing key</param>
        /// <param name="recipients">List of keys to encrypt to</param>
        /// <returns>Returns ascii armored signed/encrypted data</returns>
        protected string SignAndEncrypt(byte[] data, string key, IList<string> recipients, Dictionary<string, string> headers, bool isBinary)
        {
            Context = new CryptoContext(Context);

            var senderKey = GetSecretKeyForEncryption(key);
            if (senderKey == null)
                throw new SecretKeyNotFoundException("Error, Unable to locate sender encryption key \"" + key + "\".");

            var senderSignKey = GetSecretKeyForSigning(key);
            if (senderSignKey == null)
                throw new SecretKeyNotFoundException("Error, Unable to locate sender signing key \"" + key + "\".");

            var compressedData = new PgpCompressedDataGenerator(CompressionAlgorithmTag.ZLib);
            var literalData = new PgpLiteralDataGenerator();
            var cryptData = new PgpEncryptedDataGenerator(SymmetricKeyAlgorithmTag.Cast5, true, new SecureRandom());

            foreach (var recipient in recipients)
            {
                var recipientKey = GetPublicKeyForEncryption(recipient);
                if (recipientKey == null)
                    throw new PublicKeyNotFoundException("Error, unable to find recipient key \"" + recipient + "\".");

                cryptData.AddMethod(recipientKey);
            }

            // Setup signature stuff //
            var signatureData = new PgpSignatureGenerator(
                senderSignKey.PublicKey.Algorithm,
                HashAlgorithmTag.Sha256);
            signatureData.InitSign(
                isBinary ? PgpSignature.BinaryDocument : PgpSignature.CanonicalTextDocument,
                senderSignKey.ExtractPrivateKey(Context.Password));

            foreach (string userId in senderKey.PublicKey.GetUserIds())
            {
                var subPacketGenerator = new PgpSignatureSubpacketGenerator();

                subPacketGenerator.SetSignerUserId(false, userId);
                signatureData.SetHashedSubpackets(subPacketGenerator.Generate());

                // Just the first one!
                break;
            }
            // //

            using (var sout = new MemoryStream())
            {
                using (var armoredOut = new ArmoredOutputStream(sout))
                {
                    foreach (var header in headers)
                        armoredOut.SetHeader(header.Key, header.Value);

                    using (var clearOut = new MemoryStream())
                    {
                        using (var compressedOut = compressedData.Open(clearOut))
                        {
                            signatureData.GenerateOnePassVersion(false).Encode(compressedOut);

                            using (var literalOut = literalData.Open(
                                compressedOut,
                                isBinary ? PgpLiteralData.Binary : PgpLiteralData.Text,
                                "",
                                data.Length,
                                DateTime.UtcNow))
                            {
                                literalOut.Write(data, 0, data.Length);
                                signatureData.Update(data, 0, data.Length);
                            }

                            signatureData.Generate().Encode(compressedOut);
                        }

                        var clearData = clearOut.ToArray();

                        using (var encryptOut = cryptData.Open(armoredOut, clearData.Length))
                        {
                            encryptOut.Write(clearData, 0, clearData.Length);
                        }
                    }
                }

                return ASCIIEncoding.ASCII.GetString(sout.ToArray());
            }
        }
        private static void setSignatureSubpackets(PgpSecretKey pgpSec, PgpSignatureGenerator signatureGenerator)
        {
            IEnumerable it = pgpSec.PublicKey.GetUserIds();
            var e = it.GetEnumerator();

            if (e.MoveNext()) {
                PgpSignatureSubpacketGenerator spGen = new PgpSignatureSubpacketGenerator();

                spGen.SetSignerUserId(false, (String)e.Current);
                signatureGenerator.SetHashedSubpackets(spGen.Generate());
            }
        }
		/// <summary>
		/// Cryptographically signs the content.
		/// </summary>
		/// <remarks>
		/// Cryptographically signs the content using the specified signer and digest algorithm.
		/// </remarks>
		/// <returns>A new <see cref="MimeKit.MimePart"/> instance
		/// containing the detached signature data.</returns>
		/// <param name="signer">The signer.</param>
		/// <param name="digestAlgo">The digest algorithm to use for signing.</param>
		/// <param name="content">The content.</param>
		/// <exception cref="System.ArgumentNullException">
		/// <para><paramref name="signer"/> is <c>null</c>.</para>
		/// <para>-or-</para>
		/// <para><paramref name="content"/> is <c>null</c>.</para>
		/// </exception>
		/// <exception cref="System.ArgumentException">
		/// <paramref name="signer"/> cannot be used for signing.
		/// </exception>
		/// <exception cref="System.ArgumentOutOfRangeException">
		/// The <paramref name="digestAlgo"/> was out of range.
		/// </exception>
		/// <exception cref="System.NotSupportedException">
		/// The <paramref name="digestAlgo"/> is not supported.
		/// </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>
		public ApplicationPgpSignature Sign (PgpSecretKey signer, DigestAlgorithm digestAlgo, Stream content)
		{
			if (signer == null)
				throw new ArgumentNullException ("signer");

			if (!signer.IsSigningKey)
				throw new ArgumentException ("The specified secret key cannot be used for signing.", "signer");

			if (content == null)
				throw new ArgumentNullException ("content");

			var hashAlgorithm = GetHashAlgorithm (digestAlgo);
			var memory = new MemoryBlockStream ();

			using (var armored = new ArmoredOutputStream (memory)) {
				var compresser = new PgpCompressedDataGenerator (CompressionAlgorithmTag.ZLib);
				using (var compressed = compresser.Open (armored)) {
					var signatureGenerator = new PgpSignatureGenerator (signer.PublicKey.Algorithm, hashAlgorithm);
					var buf = new byte[4096];
					int nread;

					signatureGenerator.InitSign (PgpSignature.CanonicalTextDocument, GetPrivateKey (signer));

					while ((nread = content.Read (buf, 0, buf.Length)) > 0)
						signatureGenerator.Update (buf, 0, nread);

					var signature = signatureGenerator.Generate ();

					signature.Encode (compressed);
					compressed.Flush ();
				}

				armored.Flush ();
			}

			memory.Position = 0;

			return new ApplicationPgpSignature (memory);
		}
		private static void ProcessLine(
			Stream					aOut,
			PgpSignatureGenerator	sGen,
			byte[]					line)
		{
			int length = GetLengthWithoutWhiteSpace(line);
			if (length > 0)
			{
				sGen.Update(line, 0, length);
			}

			aOut.Write(line, 0, line.Length);
		}
        /// <summary>
        /// Sign data using key
        /// </summary>
        /// <param name="data">Data to sign</param>
        /// <param name="key">Email address of key</param>
        /// <returns>Returns ascii armored signature</returns>
        public string Sign(byte[] data, string key, Dictionary<string, string> headers)
        {
            Context = new CryptoContext(Context);

            var senderKey = GetSecretKeyForSigning(key);
            if (senderKey == null)
                throw new SecretKeyNotFoundException("Error, unable to locate signing key \"" + key + "\".");

            var compressedData = new PgpCompressedDataGenerator(CompressionAlgorithmTag.Zip);
            var literalData = new PgpLiteralDataGenerator();

            // Setup signature stuff //
            var tag = senderKey.PublicKey.Algorithm;
            var signatureData = new PgpSignatureGenerator(tag, HashAlgorithmTag.Sha256);
            signatureData.InitSign(PgpSignature.BinaryDocument, senderKey.ExtractPrivateKey(Context.Password));

            foreach (string userId in senderKey.PublicKey.GetUserIds())
            {
                var subPacketGenerator = new PgpSignatureSubpacketGenerator();

                subPacketGenerator.SetSignerUserId(false, userId);
                signatureData.SetHashedSubpackets(subPacketGenerator.Generate());

                // Just the first one!
                break;
            }
            // //

            using (var sout = new MemoryStream())
            {
                using (var armoredOut = new ArmoredOutputStream(sout))
                {
                    foreach (var header in headers)
                        armoredOut.SetHeader(header.Key, header.Value);

                    using (var compressedOut = compressedData.Open(armoredOut))
                    using (var outputStream = new BcpgOutputStream(compressedOut))
                    {
                        signatureData.GenerateOnePassVersion(false).Encode(outputStream);

                        using (var literalOut = literalData.Open(outputStream, 'b', "", data.Length, DateTime.Now))
                        {
                            literalOut.Write(data, 0, data.Length);
                            signatureData.Update(data);
                        }

                        signatureData.Generate().Encode(outputStream);
                    }
                }

                return ASCIIEncoding.ASCII.GetString(sout.ToArray());
            }
        }