上一篇文章解說過如何用 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接解密實作範例。