[C#] Giới thiệu và hướng dẫn viết ứng dụng sử dụng thuật toán mã hóa và giải mã Pretty Good Privacy (PGP)
Xin chào các bạn bài viết hôm nay mình sẽ giới thiệu đến các bạn tiếp tục một thuật toán mã hóa và giải mã PGP trong lập trình C# . Vậy thuật toán mã hóa PGP là gì? Pretty Good Privacy (PGP) là một giao thức bảo mật được các doanh nghiệp và tổ chức ...
Xin chào các bạn bài viết hôm nay mình sẽ giới thiệu đến các bạn tiếp tục một thuật toán mã hóa và giải mã PGP trong lập trình C#.
Vậy thuật toán mã hóa PGP là gì?
Pretty Good Privacy (PGP) là một giao thức bảo mật được các doanh nghiệp và tổ chức sử dụng để mã hóa dữ liệu qua mạng.
PGP kết hợp cả quyền riêng tư và xác thực mật mã để mã hóa, cũng như giải mã tất cả các loại file qua mạng, cho dù đó là email, file văn bản, đa phương tiện, thư mục hoặc phân vùng.
Chính sự linh hoạt này, cũng như sức mạnh của PGP trong việc ngăn chặn dữ liệu không thể truy cập trái phép, đã khiến nó trở thành tiêu chuẩn mã hóa được sử dụng rộng rãi nhất trên toàn thế giới.
Dưới đây là giao diện demo ứng dụng mã hóa và giải mã sử dụng thuật toán PGP trong lập trình C#:
Trong bài viết này mình sử dụng thư viện BouncyCastle từ nuget.
Từ giao diện Nuget Console, các bạn cài đặt thư viện BouncyCastle bằng lệnh sau
PM> Install-Package BouncyCastle -Version 1.8.5
Video demo ứng dụng:
Tiếp đến, các bạn tạo cho mình 4 file thư viện sau, dùng để tạo public key, private key, encrypt và decrypt, như hình dưới đây:
- Source code file KeysForPGPEncryptionDecryption.cs
using Org.BouncyCastle.Bcpg; using Org.BouncyCastle.Bcpg.OpenPgp; using Org.BouncyCastle.Crypto; using Org.BouncyCastle.Crypto.Generators; using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.Math; using Org.BouncyCastle.Security; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; namespace PGP_Algorithm.PGP { class KeysForPGPEncryptionDecryption { public static void GenerateKey(string username, string password, string keyStoreUrl) { IAsymmetricCipherKeyPairGenerator kpg = new RsaKeyPairGenerator(); kpg.Init(new RsaKeyGenerationParameters(BigInteger.ValueOf(0x13), new SecureRandom(), 1024, 8)); AsymmetricCipherKeyPair kp = kpg.GenerateKeyPair(); FileStream out1 = new FileInfo(string.Format("{0}PGPPrivateKey.asc", keyStoreUrl)).OpenWrite(); FileStream out2 = new FileInfo(string.Format("{0}PGPPublicKey.asc", keyStoreUrl)).OpenWrite(); ExportKeyPair(out1, out2, kp.Public, kp.Private, username, password.ToCharArray(), true); out1.Close(); out2.Close(); } private static void ExportKeyPair(Stream secretOut, Stream publicOut, AsymmetricKeyParameter publicKey, AsymmetricKeyParameter privateKey, string identity, char[] passPhrase, bool armor) { if (armor) { secretOut = new ArmoredOutputStream(secretOut); } PgpSecretKey secretKey = new PgpSecretKey(PgpSignature.DefaultCertification, PublicKeyAlgorithmTag.RsaGeneral, publicKey, privateKey, DateTime.Now, identity, SymmetricKeyAlgorithmTag.Cast5, passPhrase, null, null, new SecureRandom() // ,"BC" ); secretKey.Encode(secretOut); secretOut.Close(); if (armor) { publicOut = new ArmoredOutputStream(publicOut); } PgpPublicKey key = secretKey.PublicKey; key.Encode(publicOut); publicOut.Close(); } } }
2. Source code file PGPDecrypt.cs
using Org.BouncyCastle.Bcpg; using Org.BouncyCastle.Bcpg.OpenPgp; using Org.BouncyCastle.Security; using Org.BouncyCastle.Utilities.IO; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; namespace PGP_Algorithm.PGP { class PGPDecrypt { private const int BufferSize = 0x10000; // should always be power of 2 #region Encrypt /* * Encrypt the file. */ public static void EncryptFile(string inputFile, string outputFile, string publicKeyFile, bool armor, bool withIntegrityCheck) { try { using (Stream publicKeyStream = File.OpenRead(publicKeyFile)) { PgpPublicKey encKey = ReadPublicKey(publicKeyStream); using (MemoryStream bOut = new MemoryStream()) { PgpCompressedDataGenerator comData = new PgpCompressedDataGenerator(CompressionAlgorithmTag.Zip); PgpUtilities.WriteFileToLiteralData(comData.Open(bOut), PgpLiteralData.Binary, new FileInfo(inputFile)); comData.Close(); PgpEncryptedDataGenerator cPk = new PgpEncryptedDataGenerator(SymmetricKeyAlgorithmTag.Cast5, withIntegrityCheck, new SecureRandom()); cPk.AddMethod(encKey); byte[] bytes = bOut.ToArray(); using (Stream outputStream = File.Create(outputFile)) { if (armor) { using (ArmoredOutputStream armoredStream = new ArmoredOutputStream(outputStream)) { using (Stream cOut = cPk.Open(armoredStream, bytes.Length)) { cOut.Write(bytes, 0, bytes.Length); } } } else { using (Stream cOut = cPk.Open(outputStream, bytes.Length)) { cOut.Write(bytes, 0, bytes.Length); } } } } } } catch (PgpException e) { throw; } } #endregion Encrypt #region Encrypt and Sign /* * Encrypt and sign the file pointed to by unencryptedFileInfo and */ public static void EncryptAndSign(string inputFile, string outputFile, string publicKeyFile, string privateKeyFile, string passPhrase, bool armor) { PgpEncryptionKeys encryptionKeys = new PgpEncryptionKeys(publicKeyFile, privateKeyFile, passPhrase); if (!File.Exists(inputFile)) throw new FileNotFoundException(String.Format("Input file [{0}] does not exist.", inputFile)); if (!File.Exists(publicKeyFile)) throw new FileNotFoundException(String.Format("Public Key file [{0}] does not exist.", publicKeyFile)); if (!File.Exists(privateKeyFile)) throw new FileNotFoundException(String.Format("Private Key file [{0}] does not exist.", privateKeyFile)); if (String.IsNullOrEmpty(passPhrase)) throw new ArgumentNullException("Invalid Pass Phrase."); if (encryptionKeys == null) throw new ArgumentNullException("Encryption Key not found."); using (Stream outputStream = File.Create(outputFile)) { if (armor) using (ArmoredOutputStream armoredOutputStream = new ArmoredOutputStream(outputStream)) { OutputEncrypted(inputFile, armoredOutputStream, encryptionKeys); } else OutputEncrypted(inputFile, outputStream, encryptionKeys); } } private static void OutputEncrypted(string inputFile, Stream outputStream, PgpEncryptionKeys encryptionKeys) { using (Stream encryptedOut = ChainEncryptedOut(outputStream, encryptionKeys)) { FileInfo unencryptedFileInfo = new FileInfo(inputFile); using (Stream compressedOut = ChainCompressedOut(encryptedOut)) { PgpSignatureGenerator signatureGenerator = InitSignatureGenerator(compressedOut, encryptionKeys); using (Stream literalOut = ChainLiteralOut(compressedOut, unencryptedFileInfo)) { using (FileStream inputFileStream = unencryptedFileInfo.OpenRead()) { WriteOutputAndSign(compressedOut, literalOut, inputFileStream, signatureGenerator); inputFileStream.Close(); } } } } } 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); } private static Stream ChainEncryptedOut(Stream outputStream, PgpEncryptionKeys m_encryptionKeys) { PgpEncryptedDataGenerator encryptedDataGenerator; encryptedDataGenerator = new PgpEncryptedDataGenerator(SymmetricKeyAlgorithmTag.TripleDes, new SecureRandom()); encryptedDataGenerator.AddMethod(m_encryptionKeys.PublicKey); return encryptedDataGenerator.Open(outputStream, new byte[BufferSize]); } private static Stream ChainCompressedOut(Stream encryptedOut) { PgpCompressedDataGenerator compressedDataGenerator = new PgpCompressedDataGenerator(CompressionAlgorithmTag.Zip); return compressedDataGenerator.Open(encryptedOut); } private static Stream ChainLiteralOut(Stream compressedOut, FileInfo file) { PgpLiteralDataGenerator pgpLiteralDataGenerator = new PgpLiteralDataGenerator(); return pgpLiteralDataGenerator.Open(compressedOut, PgpLiteralData.Binary, file); } private static PgpSignatureGenerator InitSignatureGenerator(Stream compressedOut, PgpEncryptionKeys m_encryptionKeys) { 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; } #endregion Encrypt and Sign #region Decrypt /* * decrypt a given stream. */ public static void Decrypt(string inputfile, string privateKeyFile, string passPhrase, string outputFile) { if (!File.Exists(inputfile)) throw new FileNotFoundException(String.Format("Encrypted File [{0}] not found.", inputfile)); if (!File.Exists(privateKeyFile)) throw new FileNotFoundException(String.Format("Private Key File [{0}] not found.", privateKeyFile)); if (String.IsNullOrEmpty(outputFile)) throw new ArgumentNullException("Invalid Output file path."); using (Stream inputStream = File.OpenRead(inputfile)) { using (Stream keyIn = File.OpenRead(privateKeyFile)) { Decrypt(inputStream, keyIn, passPhrase, outputFile); } } } /* * decrypt a given stream. */ public static void Decrypt(Stream inputStream, Stream privateKeyStream, string passPhrase, string outputFile) { try { PgpObjectFactory pgpF = null; PgpEncryptedDataList enc = null; PgpObject o = null; PgpPrivateKey sKey = null; PgpPublicKeyEncryptedData pbe = null; PgpSecretKeyRingBundle pgpSec = null; pgpF = new PgpObjectFactory(PgpUtilities.GetDecoderStream(inputStream)); // find secret key pgpSec = new PgpSecretKeyRingBundle(PgpUtilities.GetDecoderStream(privateKeyStream)); if (pgpF != null) o = pgpF.NextPgpObject(); // the first object might be a PGP marker packet. if (o is PgpEncryptedDataList) enc = (PgpEncryptedDataList)o; else enc = (PgpEncryptedDataList)pgpF.NextPgpObject(); // decrypt foreach (PgpPublicKeyEncryptedData pked in enc.GetEncryptedDataObjects()) { sKey = FindSecretKey(pgpSec, pked.KeyId, passPhrase.ToCharArray()); if (sKey != null) { pbe = pked; break; } } if (sKey == null) throw new ArgumentException("Secret key for message not found."); PgpObjectFactory plainFact = null; using (Stream clear = pbe.GetDataStream(sKey)) { plainFact = new PgpObjectFactory(clear); } PgpObject message = plainFact.NextPgpObject(); if (message is PgpCompressedData) { PgpCompressedData cData = (PgpCompressedData)message; PgpObjectFactory of = null; using (Stream compDataIn = cData.GetDataStream()) { of = new PgpObjectFactory(compDataIn); } message = of.NextPgpObject(); if (message is PgpOnePassSignatureList) { message = of.NextPgpObject(); PgpLiteralData Ld = null; Ld = (PgpLiteralData)message; using (Stream output = File.Create(outputFile)) { Stream unc = Ld.GetInputStream(); Streams.PipeAll(unc, output); } } else { PgpLiteralData Ld = null; Ld = (PgpLiteralData)message; using (Stream output = File.Create(outputFile)) { Stream unc = Ld.GetInputStream(); Streams.PipeAll(unc, output); } } } else if (message is PgpLiteralData) { PgpLiteralData ld = (PgpLiteralData)message; string outFileName = ld.FileName; using (Stream fOut = File.Create(outputFile)) { Stream unc = ld.GetInputStream(); Streams.PipeAll(unc, fOut); } } else if (message is PgpOnePassSignatureList) throw new PgpException("Encrypted message contains a signed message - not literal data."); else throw new PgpException("Message is not a simple encrypted file - type unknown."); #region commented code //if (pbe.IsIntegrityProtected()) //{ // if (!pbe.Verify()) // msg = "message failed integrity check."; // //Console.Error.WriteLine("message failed integrity check"); // else // msg = "message integrity check passed."; // //Console.Error.WriteLine("message integrity check passed"); //} //else //{ // msg = "no message integrity check."; // //Console.Error.WriteLine("no message integrity check"); //} #endregion commented code } catch (PgpException ex) { throw; } } #endregion Decrypt #region Private helpers /* * A simple routine that opens a key ring file and loads the first available key suitable for encryption. */ private static PgpPublicKey ReadPublicKey(Stream inputStream) { inputStream = PgpUtilities.GetDecoderStream(inputStream); PgpPublicKeyRingBundle pgpPub = new PgpPublicKeyRingBundle(inputStream); // we just loop through the collection till we find a key suitable for encryption, in the real // world you would probably want to be a bit smarter about this. // iterate through the key rings. foreach (PgpPublicKeyRing kRing in pgpPub.GetKeyRings()) { foreach (PgpPublicKey k in kRing.GetPublicKeys()) { if (k.IsEncryptionKey) return k; } } throw new ArgumentException("Can't find encryption key in key ring."); } /* * Search a secret key ring collection for a secret key corresponding to keyId if it exists. */ private static PgpPrivateKey FindSecretKey(PgpSecretKeyRingBundle pgpSec, long keyId, char[] pass) { PgpSecretKey pgpSecKey = pgpSec.GetSecretKey(keyId); if (pgpSecKey == null) return null; return pgpSecKey.ExtractPrivateKey(pass); } #endregion Private helpers } }
3. source code file PgpEncrypt.cs
using Org.BouncyCastle.Bcpg; using Org.BouncyCastle.Bcpg.OpenPgp; using Org.BouncyCastle.Security; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; namespace PGP_Algorithm.PGP { class PgpEncrypt { private PgpEncryptionKeys m_encryptionKeys; private const int BufferSize = 0x10000; // should always be power of 2 /// <summary> /// Instantiate a new PgpEncrypt class with initialized PgpEncryptionKeys. /// </summary> /// <param name="encryptionKeys"></param> /// <exception cref="ArgumentNullException">encryptionKeys is null</exception> public PgpEncrypt(PgpEncryptionKeys encryptionKeys) { if (encryptionKeys == null) throw new ArgumentNullException("encryptionKeys", "encryptionKeys is null."); m_encryptionKeys = encryptionKeys; } /// <summary> /// Encrypt and sign the file pointed to by unencryptedFileInfo and /// write the encrypted content to outputStream. /// </summary> /// <param name="outputStream">The stream that will contain the /// encrypted data when this method returns.</param> /// <param name="fileName">FileInfo of the file to encrypt</param> public void EncryptAndSign(Stream outputStream, FileInfo unencryptedFileInfo) { if (outputStream == null) throw new ArgumentNullException("outputStream", "outputStream is null."); if (unencryptedFileInfo == null) throw new ArgumentNullException("unencryptedFileInfo", "unencryptedFileInfo is null."); if (!File.Exists(unencryptedFileInfo.FullName)) throw new ArgumentException("File to encrypt not found."); using (Stream encryptedOut = ChainEncryptedOut(outputStream)) using (Stream compressedOut = ChainCompressedOut(encryptedOut)) { PgpSignatureGenerator signatureGenerator = InitSignatureGenerator(compressedOut); using (Stream literalOut = ChainLiteralOut(compressedOut, unencryptedFileInfo)) using (FileStream inputFile = unencryptedFileInfo.OpenRead()) { WriteOutputAndSign(compressedOut, literalOut, inputFile, signatureGenerator); } } } 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); } private Stream ChainEncryptedOut(Stream outputStream) { PgpEncryptedDataGenerator encryptedDataGenerator; encryptedDataGenerator = new PgpEncryptedDataGenerator(SymmetricKeyAlgorithmTag.TripleDes, new SecureRandom()); encryptedDataGenerator.AddMethod(m_encryptionKeys.PublicKey); return encryptedDataGenerator.Open(outputStream, new byte[BufferSize]); } private static Stream ChainCompressedOut(Stream encryptedOut) { PgpCompressedDataGenerator compressedDataGenerator = new PgpCompressedDataGenerator(CompressionAlgorithmTag.Zip); return compressedDataGenerator.Open(encryptedOut); } private static Stream ChainLiteralOut(Stream compressedOut, FileInfo file) { PgpLiteralDataGenerator pgpLiteralDataGenerator = new PgpLiteralDataGenerator(); return pgpLiteralDataGenerator.Open(compressedOut, PgpLiteralData.Binary, file); } 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; } } }
và cuối cùng là file PgpEncryptionKeys.cs
using Org.BouncyCastle.Bcpg.OpenPgp; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; namespace PGP_Algorithm.PGP { class PgpEncryptionKeys { public PgpPublicKey PublicKey { get; private set; } public PgpPrivateKey PrivateKey { get; private set; } public PgpSecretKey SecretKey { get; private set; } public PgpEncryptionKeys(string publicKeyPath, string privateKeyPath, string passPhrase) { if (!File.Exists(publicKeyPath)) throw new ArgumentException("Public key file not found", "publicKeyPath"); if (!File.Exists(privateKeyPath)) throw new ArgumentException("Private key file not found", "privateKeyPath"); if (String.IsNullOrEmpty(passPhrase)) throw new ArgumentException("passPhrase is null or empty.", "passPhrase"); PublicKey = ReadPublicKey(publicKeyPath); SecretKey = ReadSecretKey(privateKeyPath); PrivateKey = ReadPrivateKey(passPhrase); } #region Secret Key private PgpSecretKey ReadSecretKey(string privateKeyPath) { using (Stream keyIn = File.OpenRead(privateKeyPath)) using (Stream inputStream = PgpUtilities.GetDecoderStream(keyIn)) { PgpSecretKeyRingBundle secretKeyRingBundle = new PgpSecretKeyRingBundle(inputStream); PgpSecretKey foundKey = GetFirstSecretKey(secretKeyRingBundle); if (foundKey != null) return foundKey; } throw new ArgumentException("Can't find signing key in key ring."); } private PgpSecretKey GetFirstSecretKey(PgpSecretKeyRingBundle secretKeyRingBundle) { foreach (PgpSecretKeyRing kRing in secretKeyRingBundle.GetKeyRings()) { PgpSecretKey key = kRing.GetSecretKeys() .Cast<PgpSecretKey>() .Where(k => k.IsSigningKey) .FirstOrDefault(); if (key != null) return key; } return null; } #endregion #region Public Key private PgpPublicKey ReadPublicKey(string publicKeyPath) { using (Stream keyIn = File.OpenRead(publicKeyPath)) using (Stream inputStream = PgpUtilities.GetDecoderStream(keyIn)) { PgpPublicKeyRingBundle publicKeyRingBundle = new PgpPublicKeyRingBundle(inputStream); PgpPublicKey foundKey = GetFirstPublicKey(publicKeyRingBundle); if (foundKey != null) return foundKey; } throw new ArgumentException("No encryption key found in public key ring."); } private PgpPublicKey GetFirstPublicKey(PgpPublicKeyRingBundle publicKeyRingBundle) { foreach (PgpPublicKeyRing kRing in publicKeyRingBundle.GetKeyRings()) { PgpPublicKey key = kRing.GetPublicKeys() .Cast<PgpPublicKey>() .Where(k => k.IsEncryptionKey) .FirstOrDefault(); if (key != null) return key; } return null; } #endregion #region Private Key private PgpPrivateKey ReadPrivateKey(string passPhrase) { PgpPrivateKey privateKey = SecretKey.ExtractPrivateKey(passPhrase.ToCharArray()); if (privateKey != null) return privateKey; throw new ArgumentException("No private key found in secret key."); } #endregion } }
Sau khi tạo 4 file thư viện này xong, mình sẽ bắt đầu code cho sự kiện form main.cs, trong form main sẽ có các nút tạo key, mã hóa và giải mã, chi tiết các bạn có thể download source code về tham khảo nhé.
Full source code Main.cs:
using PGP_Algorithm.PGP; using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; namespace PGP_Algorithm { public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void btn_createKey_Click(object sender, EventArgs e) { PGP.KeysForPGPEncryptionDecryption.GenerateKey(txt_email.Text, txt_password.Text, Application.StartupPath + "\"); txt_privateKey.Text = File.ReadAllText("PGPPrivateKey.asc"); txt_Publickey.Text = File.ReadAllText("PGPPublicKey.asc"); MessageBox.Show("Create public key and private key successful!"); } private void btn_encrypt_Click(object sender, EventArgs e) { PgpEncryptionKeys encryptionKeys = new PgpEncryptionKeys("PGPPublicKey.asc", "PGPPrivateKey.asc", txt_password.Text); PgpEncrypt encrypter = new PgpEncrypt(encryptionKeys); using (Stream outputStream = File.Create("EncryptData.txt")) { encrypter.EncryptAndSign(outputStream, new FileInfo(txt_file.Text)); } txt_encrypt.Text = File.ReadAllText("EncryptData.txt"); } private void btn_browser_Click(object sender, EventArgs e) { var dlg = new OpenFileDialog(); if(dlg.ShowDialog() == DialogResult.OK) { txt_file.Text = dlg.FileName; txt_input.Text = File.ReadAllText(dlg.FileName); } } private void btn_decrypt_Click(object sender, EventArgs e) { PGPDecrypt.Decrypt("EncryptData.txt", @"PGPPrivateKey.asc", txt_password.Text, "OriginalText.txt"); txt_decrypt.Text = File.ReadAllText("OriginalText.txt"); } } }
Thanks for watching!
DOWNLOAD SOURCE