2013年11月18日 星期一

字串的加解密CrytoStream使用DES加密法

介紹本章之前
先來簡單介紹一下 什麼是DES ??
原文要如何透過演算法才能轉換讓人短時間
無法解密的訊息呢?

字串的加解密演算法有很多有種
可以在維基百科中找到 這裡不多作介紹了

本章範例使用DES(Data Encryption Standard)
這種古老的演算法來自1975年左右
在強度或複雜度就不便多說了
就是透過 常數與規則演算去增加複雜度
拉長破解時間

DES對稱演算法
簡單來說 原文 與 KEY 經由DES演算後
將會的到一串 加密字串

解密則是相反,加密字串 與KEY 經由DES
反推出 原文 這兩種方式 是對對稱的
所以才叫對稱演算法
加密和解密 都是同一把KEY

首先 我們先來看 CryptoStream類別 中的建構函式
MSDN中的介紹如下:
使用目標資料流、要使用的轉換和資料流模式
來初始化 CryptoStream 類別的新執行個體。

OK! 目標資料流,轉換資料,和資料流模式 3個
再來 來看他的語法!!

public CryptoStream( Stream stream,
ICryptoTransform transform,
CryptoStreamMode mode
)

再來看 MSDN中 參數的解釋如下

參數

stream
型別:System.IO.Stream
要在其上執行密碼編譯轉換的資料流。
transform
型別:System.Security.Cryptography.ICryptoTransform
要在資料流上執行的密碼編譯轉換。
mode
型別:System.Security.Cryptography.CryptoStreamMode
其中一個 CryptoStreamMode 值。
 
 
第一個參數Stream意思是指
要使用哪一種資料流來編譯轉換


第二個參數transform
型別傳回數值為ICryptoTransform介面
待會我們將使用DESCryptoServiceProvider()類別中的
CreateEncryptor(Byte[], Byte[]))方法
他的語法如下
public override ICryptoTransform CreateEncryptor(
byte[] rgbKey,
byte[] rgbIV
剛好回傳 ICryptoTransform 正是CryptoStream()方法
第二參數中所需要回傳的 ICryptoTransform 介面
但是奇怪的是,為何要使用ICryptoTransform 介面
因為CryptoStream類別中並沒有實作或複寫ICryptoTransform介面
此問題先保留 以後發現再回來改

第三個參數mode
將決定此建構函式是要加密 或解密?
如果加密 就CryptoStreamMode.Write
解密則CryptoStreamMode.Read

OK!現在讓我們來看個範例!




來講解一下上圖範例!
首先btn_Encrypt_Click按鍵觸發後
先進入一個If來判斷key的長度是否等於4
如果不是等於4則else
再來進入try與catch
首先try區塊中 最後傳回數據為txt_EncryptStr.Text
而我們實體化 Encrypt類別並且調用類別中的ToEncrypt方法
並且傳入兩個參數txt_password.Text=密碼
txt_str.Text=要加密的字串

再來我們來看一下 Encrypt類別中
是如何處理 密碼與加密字串的

來看一下Encrypt.cs檔案中的類別吧!!


















看上圖我們將使用到Cryptography的命名空間
而Encrypt類中包含兩個Internal的字串方法
關於Internal上一章有詳細介紹!
我們先來看 第一個方法
剛剛說到
ToEncrypt方法傳入兩個字串參數
來看下圖





































看完上圖的註解應該不難理解!
首先 傳入ToEncrypt方法中的兩個字串
一個是Key金鑰,一個是Str原文
而Key中的數字1234每個數字以unicode取值
而unicode是UTF16所以會有16bit 等於2byte
當方法GetBytes的時候會取2個byte陣列
而假如密碼第一個數字是1
ascii碼是49 但是49不足2bytes
所以在P_Byte_Key中 會塞入
一個49另外一個則是0的陣列

而Str也是一樣 轉乘Byte陣列

再來建立記憶體資料流P_Stream_MS

再來建立加密流 CryptoStream
而語法在本文章開端有大概地介紹過了
這裡要注意的是,所需要的參數
記憶體資料流 P_Stream_MS
加密模式
DESCryptoServiceProvider()類別中的
CreateEncryptor(Key,Key)加密方法 並且帶入Key值!
最後CryptoStreamMode.Write為寫入模式

再來寫入P_CryptStream_Stream.Write(P_byte_data, 0, P_byte_data.Length)
參數中的意思應該不用多解釋0是起始length是一個字元種字數
意思將原文全部寫入

再來
 P_CryptStream_Stream.FlushFinalBlock()
msdn解釋如下
以緩衝區的目前狀態更新基礎資料來源或存放庫,並接著清除緩衝區。

因為還沒呼叫Close所以我們先讓 P_CryptStream_Stream裡面的buffer
刷新好讓P_byte_date資料流進P_Stream_MS
 
呼叫 Close 方法將會呼叫 FlushFinalBlock如果不呼叫Close,請呼叫FlushFinalBlock以完成刷新緩衝區。僅在所有資料流活動都完成時呼叫 FlushFinalBlock


最吼取P_Stream_MS中的陣列到P_bt_temp 中
byte[] P_bt_temp = P_Stream_MS.ToArray();

最後將 P_bt_temp 轉成 Base64 回傳
return Convert.ToBase64String(P_bt_temp);

什麼是 Base64 ?

依遞增順序從零起始的 Base-64 位數,包含大寫字元 "A" 到 "Z"、小寫字母 "a" 到 "z"、數字 "0" 到 "9",以及 "+" 和 "/" 符號。
ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/
不表示數值的 "=" 字元是做為尾端填補。
更多解釋請參考 MSDN維基百科



上面我們已經知道 如何將 原文字串
轉成短時間難以讀取的加密字串了

再來我們將示範如何將字串解密
還原,當初加密前的訊息
請看下圖 ToDecrypt方法一樣送入兩個字串
加密訊息與KEY值
 
注意解密區塊中與加密有稍微的不同
一開始我們一樣 將接收到加密字串與key
的兩個參數,接下來我們要把加密時
轉成Base64的字串用FromBase64String
轉回位元組陣列,這方法回傳byte陣列
 
再來實體化一個MemoryStream並將P_byte_data
寫入記憶體中的buffer
 
 
我們一樣會使用到CryptoStream的加密流
不同的是參數,使用剛剛建立已寫入資料的記憶流
解密一樣使用DESCryptoServiceProvider類別
不同的是使用CreateDecryptor(key, key)對稱解密的方法
而最後一個參數CryptoStreamMode.Read為讀取模式

接下來準備資料流要存取的成員吧
byte[] P_bt_temp = new byte[200] 存取解密字節序列結果

在實體化一個記憶體資料流來寫入P_bt_temp 序列結果!
MemoryStream P_MemoryStream_temp =new MemoryStream();

首先進入一個While判斷式!
CryptoStream.Readhttp有讀取到序列資料則會傳會int
而我們之前也宣告了一個int i = 0;//建立記數器
剛好可以用來進行 While判斷是否大於0代表有獨到資料
而資料回存到剛剛新建的P_bt_temp陣列中

而while 為true時進入程式區塊
並且將解密後的資料放入記憶體流
P_MemoryStream_temp.Write(P_bt_temp, 0, i);

最後 將記憶體資料流以陣列傳給GetString
將指定位元組陣列中的所有位元組解碼成字串
return Encoding.Unicode.GetString(P_MemoryStream_temp.ToArray());
最後將字串輸出

OK!完





2013年11月15日 星期五

存取範圍層級internal與DLL介紹

本文參考文章連結
在理解什麼是Internal的時候在MSDN找到解釋是

第一
internal 修飾詞讓類別、介面或成員只能在目前封裝內看得見。
目前封裝外部的程式碼不能存取 internal 成員。

上文意思是指
限於在DLL或EXE中使用被修飾的類別 介面成員
意思是如果開新Windiws Form專案將DLL加入參考
DLL中被internal修飾的成員將無法調用

第二
類別和介面可以使用 internal 修飾詞來標記。
在全域範圍中,internal 修飾詞與 public 修飾詞相同。
類別或介面的任何成員都可以使用 internal 修飾詞來標記。

意思是指 類別或介面 在使用Internal修飾 如同Public
待會我們將會示範這個疑點

第三
您不能將 internal 修飾詞與其他任何可視性的
修飾詞 (publicprivateprotected) 結合使用。
可視性修飾詞與它們所定義的範圍有關。
例如,internal 類別的 public 方法不能公開存取
但是任何可以存取此類別的程式碼都可以存取此方法。

意思是指 public 與 Internal 不能共存拉 不對外公開只對內
就用internal就好,公開就public了唷~

來看範例比較快理解
首先我們先建立一個WindowsForm
而在此專案在新建一個Class檔
如下圖








建立的Class叫C1.cs
C1.cs檔案中的內容如下

 
 
 
首先來看上圖 第一個類別 公開的Class1
裡面的成員如上,將被Class2繼承
注意到不是只有protected可以被繼承喔
注意到internal同等public差別在於
internal只對內公開不對外
來看下圖Class2如何繼承Class1吧
 


 
上圖清楚看到
繼承了Class1成員完全沒問題啊
注意Public string GetProtectedInternal()
中的方法調用的是一個Internal
可以證明Internal是一個對內的Public
 
下圖將介紹Class3 Class4
 


 
上圖可以看到
Internal Class3
被Class4繼承
這兩個類別我們等等會在
新專案中把此類別加入參考
並且證明 Class3 與 Class4 是不會被調用的
下圖Class5則是證明
實體化物件中的internal成員如同
public也可以被調用 如下圖






















上圖 注意實體化的cla1與cla3
可以調用 Internal的成員喔!!



再來我們將 新建一個專案
再將上面OP專案中的C1.CS加入參考
將證明 internal Class3與Class4不會被調用

首先 開新專案


 
 
 
加入參考

 
 
選擇剛剛的專案位置op.exe


 
 
再來我們在專案中加入OP的命名空間

並且實體化類別
看可不可以調用類別中的成員
以及證明internal是不能被其他專案調用的
請看下圖即可明瞭




上圖Class3 與 Class4 因為是internal所以不會顯示
而C1.str則是公開成員 Class1 中的internal成員將不會顯示
C2也是

本章節介紹到此

2013年10月26日 星期六

Enum 列舉型別 介紹

Enum 關鍵字用來宣告列舉型別 (Enumeration),是由一組稱為列舉值清單的具名常數所構成的獨特型別。
通常最好是在命名空間內直接定義 enum,讓該命名空間中的所有類別都能同樣便利地存取它。不過,enum 也可以透過巢狀方式置於個類別或結構 (Struct) 中。
根據預設,第一個列舉值的值是 0,而每一個接下來的列舉值會遞增 1

來看一下範例


這邊第一個列舉值意思是Black 這個預設值是0開始 依此類推1,2,3,4~11的None

當然這個順序,不一定要照系統預設的 也可以自己改,看下面程式碼!!








這邊可以看到 Sat=6 以此類推 Sum 就一定會是7,除非你在Sum=後面加上常數不然會依
前面列舉值+1
再來看 Mon=1,以此類推 Tur=2 一子到 Fri 當然會等於5了唷!


每個列舉型別都有基礎型別,此基礎型別可以是除了 char 型別以外的任何整數型別。列舉項目的預設基礎型別是 int。若要宣告另一個整數類資料型別 (Integral Type) 的 enum,例如 byte,請在後面接著型別的識別項之後使用冒號:
來看下面程式碼


 

enum Days : 後面加上型別就行了
enum 的核准型別為 bytesbyteshortushortintuintlongulong
可將基礎型別範圍內的任何值指派給 Days 型別的變數;這些值並不受限於具名常數。


再來我們來看看 如何使用吧!!
分別是以值取字
或是以字取值

看下圖!!



 

大概介紹到這!! 日後補充

2013年10月24日 星期四

替換字串的方法 String.Replace()

假如今天打了一篇日記但手殘打錯了故事中主角的名子了
要修改!卻要把整篇文章重頭到尾打錯的名子
一個一個的改回來 很麻煩!!

這時候有個方便又好用的方法

假如故事如下

豬爸爸生豬兒子,豬兒子愛把妹

把豬改成帥哥!

可以使用String.Replace() 語法如下
public string Replace(string oldValue, string newValue)

oldValue
型別:System.String
要被取代的字串。
newValue
型別:System.String
用來取代所有出現之 oldValue 的字串。
 
 
來看實例程式碼如下


 

 

分割字串Split

這裡分割的意思是
比如說有一個字串是
我是@豬與帥哥$的重合體^哈哈哈
我要把@$^給分割掉 這時候用Split這方法超好用
來看一下MSDN的語法,有如下幾種
String.Split 方法




來看第一個程式範例
語法如下public string[] Split(params char[] separator)















不多說,圖片簡單又明瞭!
注意的是
separator 的每個項目各定義一個分隔字元
就是一個字串陣列一個Empty, 那麼如果兩個分隔符號為相鄰
如下圖也就是兩個^就會多產生一個Empty
或是在此執行個體的開頭或結尾 找到一個分隔符號
則對應的陣列元素會包含 Empty
因為每個項目前跟後各有一個分割字元
其餘多的將會被推入空陣列     

請看下圖:因為兩個^造成兩個分割字元
一個是要給 "我是帥哥"的 於是 第二個空字串則推入陣列
 

        











要解決這問題也很簡單
給他一個foreach 跟一個if 判斷不等於""空字串時印出!



















而這裡還搭配一個Trim()方法用於清除空白字串
看下圖






我們也可以使用String.Split 方法 (Char[], StringSplitOptions)
來達到不回傳空字串的目的
語法如下
[ComVisibleAttribute(false)]
public string[] Split(char[] separator,StringSplitOptions options)
來看程式碼如下










啊在假如 今天判斷式改成string
可以用這個語法 如下

String.Split 方法 (String[], StringSplitOptions)

來看圖片吧!











不一樣的是 Scut 是一個字串唷!!


再來看最後一個例子
語法如下
String.Split 方法 (Char[], Int32)
或者
String.Split 方法 (String[], Int32, StringSplitOptions)

可以看到 第二個參數是一個 Int32 整數型態
用來限制回傳字串陣列的最大數目
如果是2 代表只有兩個陣列 0跟1
如果是3 代表只有三個陣列0,1,2
來看下圖 就能明白!!









看紅色箭頭!! 可以注意的是2
分別得到兩個陣列

再來看下圖 將參數改成3 出現三個陣列
因此可以清楚明白他的規則

2013年10月23日 星期三

利用(基礎)REGEX正規化查詢字串中有幾個中文與數字

利用簡易的正規化來看字串中有幾個中文字
首先本篇將會介紹 簡易的正規化如何使用
語法如下
Regex 物件名 = new Regex("pattern")
在pattern放入符號表達式
下面圖表來源為菲力貓的程式設計


符號
表示式
說明或範例
不成立字串
.
a.
表示任何字元。
含字母 "a" 以及其後任一個字元的字串。
Ex: "ab", "bac"
"a", "ba"
重覆字串
*
ab*
表示沒有或更多字元。
"ab*":表示一個字串有一個a後面跟著零個或若干個b
Ex: "a", "ab", "abbb"
+
ab+
一次或更多字元。
"ab+":表示一個字串有一個a後面跟著至少一個b或者更多
Ex: “ab”,”abbbb”
?
ab?
沒有或一次字元。
ab?":表示一個字串有一個a後面跟著零個或者一個b
Ex: “a”,”ab”
[]
[13579]
方括號表示某些字元允許在一個字串中的某一特定位置出現。
包含 "1" "3" "5" "7" "9" 的字串。
Ex: "a3b", "1xy"
"y2k"
[0-9]
含數字之字串
不含數字之字串
[a-z0-9]
含數字或小寫字母之字串
不含數字及小寫字母之字串
[a-zA-Z0-9]
含數字或字母之字串
不含數字及字母之字串
b[aeiou]t
"bat", "bet", "bit", "bot", "but"
"bxt", "bzt"
^[a-zA-Z]
表示一個以字母開頭的字串
[0-9]%
表示一個百分號前有一位元的數位
[a-zA-Z0-9]$
表示一個字串以一個逗號後面跟著一個字母或數位結束
{}
ab{2}
用以表示重復次數的範圍。
表示一個字串有一個a跟著2b
Ex: "abb"
ab{2,}
表示一個字串有一個a跟著至少2b
ab{3,5}
表示一個字串有一個a跟著35b
字串位置
^
^xy
"xy" 開始的字串
Ex: "xyz", "xyab"
"axy", "bxy"
$
xy$
"xy" 結尾的字串
Ex: 例如:"axy", "abxy"
"xya", "xyb"
[^]
[^0-9]
不希望出現的字元,'^'應在方括號裏的第一位。
不含數字之字串
含數字之字串
%[^a-zA-Z]%
兩個百分號中不應該出現字母
[^aeiouAEIOU]
不含母音之字串
含母音之字串
[^\^]
不含 "^" 之字串,例如:"xyz", "abc"
"xy^", "a^bc"
特殊字元
\d
數字0~9
\D
非數字
\w
數字、字母、底線
\W
\w
\s
空白字元 + \r\t\n\f
\S
\s
|
(b¦cd)ef
表示”或”
"bef""cdef"
\
^\^
跳脫字元,將特殊符號的義意去除,^.$()¦*+?{\"這些字元前加上跳脫字元'\'
字首必須是^

 








 


程式碼如下!!

private void btn_GetCount_Click(object sender, EventArgs e)
        {
           
            int P_scalar = 0;//定義值類型變數並賦值為0
            Regex P_regex = //建立正則表達式對象,用於判斷字符是否為中文字
                new Regex("^[\u4E00-\u9FA5]{0,}$");
            for (int i = 0; i < txt_str.Text.Length; i++)//深度搜尋字串中每一個字符
            {
                P_scalar = //如果檢查的字符是中文字則計數器加1沒有則不動作
                    P_regex.IsMatch(txt_str.Text[i].
                    ToString()) ? ++P_scalar : P_scalar;
            }
            txt_count.Text = P_scalar.ToString();//顯示中文字數量
        }


那如果要查詢英文字母呢??  一樣
跟上面差不多 唯一修改的地方是

private void button2_Click(object sender, EventArgs e)
        {
            int COUNT = 0;
            Regex tr = new Regex("^[A-Za-z]");
            for (int w = 0; w < txt_str.Text.Length; w++)
            {
                COUNT = tr.IsMatch(txt_str.Text[w].ToString()) ? ++COUNT : COUNT;
            }
            label3.Text = COUNT.ToString();
        }




顯示數字可以用正規化
不過下面不用正規劃
改用一下 char.IsDigit如果是數字會回傳一個BOOL


private void button1_Click(object sender, EventArgs e)
        {
            int r = 0;//計數器
            char[] MyisDigit = txt_str.Text.ToCharArray();//字轉轉字元陣列
            foreach (char E in MyisDigit)//一個一個搜尋
            {
                while (char.IsDigit(E))//如果是數字則回傳true
                {
                    r = r + 1;
                    break;//跳出while迴圈 不然會永無止境的循環
                }
               
            }
            //將累加結果SHOW出
            MessageBox.Show("字串中有" + r.ToString() + "個數字");
        }