上一篇文章解說過如何用 Bouncy Castle 產生 Public 及 Private key, 接著要繼續解說市面上最常見的加解密模式。雙方傳送 資訊 or 檔案,為了確保資料的機密性。我們都會用接收方的 publica Key 對檔案進行加密,接收方收到檔案後再利用自己的 priavte Key做解密的動作。這是最常見的方法。
因此我透過修改 Bouncy Castle 官方範例,實作出以上的情境的程式。
1. 先官方範例中(bccrypto-net-1.7-src\csharp\crypto\test\src\openpgp\examples) 複製 PgpExampleUtilities.cs 檔案,加入專案中。此Class 提供取出 private 以及 public key 資訊的 method 。
2. Bouncy Castle 檔案 Public key 加密 EncryptFile method 實作如下
//外部呼叫的 method public static void EncryptFile( string outputFileName,//加密後輸出檔案名稱位置 string inputFileName, //欲加密檔案名稱位置 string encKeyFileName,//提供加密的 public key 檔名及位置 bool armor, //不明???,範例預設為true bool withIntegrityCheck//不明???,範例預設為false ) { PgpPublicKey encKey = PgpExampleUtilities.ReadPublicKey(encKeyFileName); using (Stream output = File.Create(outputFileName)) { EncryptFile(output, inputFileName, encKey, armor, withIntegrityCheck); } } //內部的實作參照官方範例 private static void EncryptFile( Stream outputStream, string fileName, PgpPublicKey encKey, bool armor, bool withIntegrityCheck) { if (armor) { outputStream = new ArmoredOutputStream(outputStream); } try { byte[] bytes = PgpExampleUtilities.CompressFile(fileName, CompressionAlgorithmTag.Zip); PgpEncryptedDataGenerator encGen = new PgpEncryptedDataGenerator( SymmetricKeyAlgorithmTag.Cast5, withIntegrityCheck, new SecureRandom()); encGen.AddMethod(encKey); Stream cOut = encGen.Open(outputStream, bytes.Length); cOut.Write(bytes, 0, bytes.Length); cOut.Close(); if (armor) { outputStream.Close(); } } catch (PgpException e) { Console.Error.WriteLine(e); Exception underlyingException = e.InnerException; if (underlyingException != null) { Console.Error.WriteLine(underlyingException.Message); Console.Error.WriteLine(underlyingException.StackTrace); } } }
3. Bouncy Castle 檔案 Private key 解密 DecryptFile method 實作如下:
//外部呼叫解密 mehtod public static void DecryptFile( string inputFileName, //欲解密之檔案名稱及位置 string keyFileName, //解密 Private key 位置 char[] passwd, //Private key password string defaultFileName //解密後檔案名稱及位置 ) { using (Stream input = File.OpenRead(inputFileName), keyIn = File.OpenRead(keyFileName)) { DecryptFile(input, keyIn, passwd, defaultFileName); } } //內部解密實作參照官方範例 private static void DecryptFile( Stream inputStream, Stream keyIn, char[] passwd, string defaultFileName) { inputStream = PgpUtilities.GetDecoderStream(inputStream); try { PgpObjectFactory pgpF = new PgpObjectFactory(inputStream); PgpEncryptedDataList enc; PgpObject o = pgpF.NextPgpObject(); // // the first object might be a PGP marker packet. // if (o is PgpEncryptedDataList) { enc = (PgpEncryptedDataList)o; } else { enc = (PgpEncryptedDataList)pgpF.NextPgpObject(); } // // find the secret key // PgpPrivateKey sKey = null; PgpPublicKeyEncryptedData pbe = null; PgpSecretKeyRingBundle pgpSec = new PgpSecretKeyRingBundle( PgpUtilities.GetDecoderStream(keyIn)); foreach (PgpPublicKeyEncryptedData pked in enc.GetEncryptedDataObjects()) { sKey = PgpExampleUtilities.FindSecretKey(pgpSec, pked.KeyId, passwd); if (sKey != null) { pbe = pked; break; } } if (sKey == null) { throw new ArgumentException("secret key for message not found."); } Stream clear = pbe.GetDataStream(sKey); PgpObjectFactory plainFact = new PgpObjectFactory(clear); PgpObject message = plainFact.NextPgpObject(); if (message is PgpCompressedData) { PgpCompressedData cData = (PgpCompressedData)message; PgpObjectFactory pgpFact = new PgpObjectFactory(cData.GetDataStream()); message = pgpFact.NextPgpObject(); } if (message is PgpLiteralData) { PgpLiteralData ld = (PgpLiteralData)message; string outFileName = ld.FileName; //if (outFileName.Length == 0) //{ outFileName = defaultFileName; //} Stream fOut = File.Create(outFileName); Stream unc = ld.GetInputStream(); Streams.PipeAll(unc, fOut); fOut.Close(); } 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."); } if (pbe.IsIntegrityProtected()) { if (!pbe.Verify()) { Console.Error.WriteLine("message failed integrity check"); } else { Console.Error.WriteLine("message integrity check passed"); } } else { Console.Error.WriteLine("no message integrity check"); } } catch (PgpException e) { Console.Error.WriteLine(e); Exception underlyingException = e.InnerException; if (underlyingException != null) { Console.Error.WriteLine(underlyingException.Message); Console.Error.WriteLine(underlyingException.StackTrace); } } }
這樣我們就實作完加密解密的 Method ,接著我們實驗一個範例。範例如下:
我們有個檔案 a.txt 利用 Public key 對檔案加密,產生出 b.txt 。接著我們再用 b.txt 利用 Private Key 作解密,產生 c.txt 。
我們有個檔案 a.txt 利用 Public key 對檔案加密,產生出 b.txt 。接著我們再用 b.txt 利用 Private Key 作解密,產生 c.txt 。
其實作程式如下:
static void Main(string[] args) { //Public 1 檔案加密 string encryptFileName = @"D:/BC/b.txt"; string inputFileName = @"D:/BC/a.txt"; string encKeyFileName = @"D:/BC/pub1.asc"; bool armor = true; bool withIntegrityCheck = false; try { EncryptFile(encryptFileName, inputFileName, encKeyFileName, armor, withIntegrityCheck); Console.WriteLine("加密成功"); } catch (Exception e) { Console.WriteLine("加密失敗" + e.Message); } //private1 檔案解密 string decryptEncryptFileName = @"D:/BC/b.txt"; string keyFileName = @"D:/BC/priv1.asc"; char[] passwd = "123456".ToCharArray(); string defaultFileName = @"D:/BC/c.txt"; try { DecryptFile(decryptEncryptFileName, keyFileName, passwd, defaultFileName); Console.WriteLine("解密成功"); } catch (Exception e) { Console.WriteLine("解密失敗" + e.Message); } Console.Read(); }
執行結果我們分別來看 a b c .xml 檔案內容。
a.txt:
19834209 02/02/2002
b.txt:
-----BEGIN PGP MESSAGE----- Version: BCPG C# v1.7.4937.24077 hIwDQtEQm+xjLocBA/0Vpz+RVTS8uMTr/RTJdvEcNZlcAHIPsPj6RrJouZkySjcO 6DkHVjS1gVW6RxcjDy/pPnfF/R7R3LUMF9ibh0dQjEu3stuPI3UXz7nEwJ2yhQ04 +vGEJYLmw185WkOcvUGiiVc/ZkU0AkYHolmz4zFfLAHbNJcPt1SSjhNHDSwchskz bu7bsgAg9aM/+E7Z5mWJgeePnX9guh9Ucl6psv6/ZYq45WtYGOMeeNB2trkcwa8k hh2R =oAx4 -----END PGP MESSAGE-----
c.txt:
19834209 02/02/2002
a.xml 內容與 c.xml 內容是一致的,證明解密成功。以上是簡單的PGP接解密實作範例。
沒有留言:
張貼留言