2013年8月25日 星期日

[C#] Bouncy Castle C# API(二) PGP 加解密(Encrypt AND Decrypt) 呼叫使用

上一篇文章解說過如何用 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 。  
其實作程式如下:

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接解密實作範例。

沒有留言:

張貼留言