Exemplo n.º 1
0
        /// <summary>
        /// Create PGP-decrypting stream around a PGP-encrypted input source stream
        /// </summary>
        /// <param name="privatekeysource">Input stream containing private keyring bundle</param>
        /// <param name="privatekeypassphrase">Passphrase to access private key (if required)</param>
        /// <param name="encryptedinput">Input stream containing source data to be decrypted</param>
        /// <returns>
        /// A StreamStack object, from which decrypted data can be read
        /// </returns>
        /// <remarks>
        /// - Caller is responsible for disposing of returned StreamStack (BEFORE disposing of original encryptedinput Stream)
        /// - Caller is also still responsible for disposing of encryptedinput stream (AFTER disposing of returned StreamStack)
        /// - Source data stream will be read synchronously (due to limitation in PGP library; all source data must be available to perform decryption,
        /// but source library does not provide async option). If this is a concern, caller should consider asynchronously pulling source data into local
        /// stream first and passing local stream to this function (if increased use of memory outweighs waiting for blocked thread to perform I/O)
        /// </remarks>
        public static async Task <StreamStack> GetDecryptionStream(Stream privatekeysource, string privatekeypassphrase, Stream encryptedinput)
        {
            var decryptionstream = new StreamStack();

            try
            {
                // Open decoder stream and push on to return value stack:
                decryptionstream.PushStream(PgpUtilities.GetDecoderStream(encryptedinput));

                // Create object factory (using stream at top of stack) to extract encrypted data:
                var factory       = new PgpObjectFactory(decryptionstream.GetStream());
                var encrypteddata = factory.GetEncryptedData() ?? throw new ArgumentException("No PGP-encrypted data found");

                // Extract private key from key source (using key ID required by encrypted data object), use to open
                // clear stream and push on to return value stack:
                decryptionstream.PushStream(encrypteddata.GetDataStream(await ExtractPrivateKey(privatekeysource, privatekeypassphrase, encrypteddata.KeyId)));

                // Create new factory from clear stream and extract first PGP object:
                factory = new PgpObjectFactory(decryptionstream.GetStream());
                var message = factory.NextPgpObject();
                while (message != null)
                {
                    // If object is literal data, push de-literalization stream on to return value stack and return:
                    if (message is PgpLiteralData cleardata)
                    {
                        decryptionstream.PushStream(cleardata.GetInputStream());
                        return(decryptionstream);
                    }
                    // Otherwise if this is compressed data, we need to push decompression stream on to return value
                    // stack, then create a new object factory to extract data from decompressed source:
                    else if (message is PgpCompressedData compresseddata)
                    {
                        decryptionstream.PushStream(compresseddata.GetDataStream());
                        factory = new PgpObjectFactory(decryptionstream.GetStream());
                    }

                    // Extract next message object from factory and continue:
                    message = factory.NextPgpObject();
                }

                // If this point is reached, no literal packet was ever found
                throw new ArgumentException("Invalid PGP data (no payload found)");
            }
            catch
            {
                decryptionstream.Dispose();
                throw;
            }
        }