DES稱之為Data Encryption Standard, 屬於基礎的單方向性服務. 可以拿來應用於資料串流編碼. 根據微軟提供的類別物件, 如下所示:
[ComVisibleAttribute(true)]
public sealed class DESCryptoServiceProvider : DES
可以看到這個是一個密封(sealed)的實體類別, 意味它不能被繼承. 不過他實作了DES類別. 來看看DES類別的架構:
[ComVisibleAttribute(true)]
public abstract class DES : SymmetricAlgorithm
這個DES是一個抽象, 不能被實例化. 由此推斷, 他的上層依然也是屬於抽象.
[ComVisibleAttribute(true)]
public abstract class SymmetricAlgorithm : IDisposable
這個上層的SymmetricAlgorithm對於DESCryptoProvider而言是最頂級的抽象, 這個頂級的抽象實作了IDisposable介面, 表示由下直接或間接繼承的具象類別所產生的實例物件可以被直接摧毀(Dispose). 在微軟的DES加密服務提供64位元長度.
根據DESCryptoServiceProvider的完整類別繼承結構如下所示:
System.Object
System.Security.Cryptography.SymmetricAlgorithm
System.Security.Cryptography.DES
System.Security.Cryptography.DESCryptoServiceProvider
這個服務類別來自mscorlib (在 mscorlib.dll 中), 因此在創建專案, 直接就可以使用了, 預設他會匯入mscorlib.dll的參考.
以下根據MSDN的基本範例來看看怎麼使用DES加密服務:
private static void EncryptData(String inName, String outName, byte[] desKey, byte[] desIV)
{
//Create the file streams to handle the input and output files.
FileStream fin = new FileStream(inName, FileMode.Open, FileAccess.Read);
FileStream fout = new FileStream(outName, FileMode.OpenOrCreate, FileAccess.Write);
fout.SetLength(0);
//Create variables to help with read and write.
byte[] bin = new byte[100]; //This is intermediate storage for the encryption.
long rdlen = 0; //This is the total number of bytes written.
long totlen = fin.Length; //This is the total length of the input file.
int len; //This is the number of bytes to be written at a time.
DES des = new DESCryptoServiceProvider();
CryptoStream encStream = new CryptoStream(fout, des.CreateEncryptor(desKey, desIV), CryptoStreamMode.Write);
Console.WriteLine("Encrypting...");
//Read from the input file, then encrypt and write to the output file.
while(rdlen < totlen)
{
len = fin.Read(bin, 0, 100);
encStream.Write(bin, 0, len);
rdlen = rdlen + len;
Console.WriteLine("{0} bytes processed", rdlen);
}
encStream.Close();
fout.Close();
fin.Close();
}
上述是一個DES加密服務的實體操作(EncryptData), 用來對資料串流進行加密, 這個操作傳入四個引數, 分別來說明這四個引數的作用:
1. inName: 這個參數是要傳入資料串流的實體路徑
2. outName: 將取得的加密資料串流寫回指定路徑
3. desKey: 這個是DES所使用的加密金鑰
4. desIV: 這是CBC的初始化向量, DES必須要用到, 他不是ECB
下面這兩句陳述式是重要的關鍵, 主要是依賴他們來提供DES私鑰加密服務. 就以OOD的角度來看, 也牽涉到非常實用的設計模式, 不管在JAVA或著.NET, I/O的包裝很常使用:
DES des = new DESCryptoServiceProvider();
CryptoStream encStream = new CryptoStream(fout, des.CreateEncryptor(desKey, desIV), CryptoStreamMode.Write);
第一句的des物件變數, 這是一個針對抽象實踐的方式, 通用的一點說法就是一般化. DESCryptoServiceProvider具象類別則是主要用來提供對DES相關簽名的實作. 在CryptoStream這段, 來看看他的建構函式, new CryptoStream(...), 他有一個非常重要的特殊性, 首先看他傳入的參數, 其中包含了fout的FileStream物件, CryptoStream具象類別和這個類別就某種程度來說有很大的相關性, 這種相關性先看他是否有實作抽象.
[ComVisibleAttribute(true)]
public class FileStream : Stream
[ComVisibleAttribute(true)]
public class CryptoStream : Stream, IDisposable
這上面的類別架構可以看到了這兩個具象類別結實做了Stream的抽象, 這是相當重要的存在性. 對Stream的抽象來說, FileStream或著CryptoStream都是他的實現, 換句話說, FileStream或著CryptoStream可以是Stream的一種:
Stream sUsingFs=new FileStream(...);
Stream sUsingCs=new CryptoStream(...);
不管FileStream或著CryptoStream都可以是Stream, 在物件導向的觀點來看, 就是對Stream的多型展現, 這種一般化的設計在OOA的設計上可以達到幾個存在性法則:
1. OCP(Open-Close Principle)
2. DIP(Dependency Inversion Principle)
3. LSP(Lisksov Principle)
這些法則在實務設計上廣泛且常應用, 透過這些法則可以達到好幾種模式的展現. 在這個Stream的多型展現上, 往回他的具象類別來看, CryptoStream的建構函數在前面有提到, 一個fout的FileStream物件被傳入, 如果仔細再看他的完整建構函式表示:
public CryptoStream (
Stream stream,
ICryptoTransform transform,
CryptoStreamMode mode
)
可以注意到, 他真正傳入的型別是Stream抽象, 就高層次的角度來看, CryptoStream只知道有一個Stream物件被傳入, 但他並不知道是FileStream的真正引入, 也就是說CryptoStream並不清楚真被傳入的Stream物件真正細節為何, 代表這是某種程度的封裝, 允許隱藏實際的細節. 非常有意義, 但是在這段建構函式真正的重點在於CryptoStream是Stream的一種; 而FileStream也是, 這兩者對Stream來說是IS-A的關係, 一個Stream的CryptoStream包入Stream的Filetream, 兩者細節可用.NET Reflector拆解, 這邊就不詳細了. Stream包Stream在有些教科書學術的說法稱為包裏效果, 但是在OOD的角度來看, 他是非常經典的Decorator Pattern, 這個模式是相當棒的, 而且實用價值極高:
首先, fin的FileStream物件會取得實體檔案的串流, 根據傳入的實體路徑參數而定. fout則是用來將取得的加密資料串流寫回指定路徑.
1. fin: 傳入三個引數, 分別是=> 實體路徑, 允許檔案模式為開啟和允許檔案存取為讀取
2. fout: 同樣也是三個引數=> 實體路徑, 允許檔案模式為打開或建立和允許檔案存取為寫入
fout.SetLength(0)預先設定他的串流大小為0, 也就是在這個狀況沒有任何資料. 另外說明幾個變數:
1. bin: 長度為100的bin位元組參數用還放置讀取的二進位資料串流, 一次讀取100長度
2. rdlen: 作為目前讀取的檔案長度, 表示其位址
3. totlen: 放置fin讀取目前資料串流的實際大小
4. len: 放置目前的讀取大小