顯示具有 C# 標籤的文章。 顯示所有文章
顯示具有 C# 標籤的文章。 顯示所有文章

2013年5月9日 星期四

簡轉繁 C# Code 僅供參考

第一段為資料庫簡轉繁

第二段為文字檔繁轉簡

可參考 但是裡面的CODE 還需要調整

SQL 簡轉繁

-----------------------

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.Threading.Tasks;

using System;

using System.Data;

using MySql.Data;

using MySql.Data.MySqlClient;

using System.Collections.Generic;

using System.Text;

using Microsoft.VisualBasic;

namespace EncodeTool

{

/*

    a、注意資料庫編碼要能相容gb2312和big5,比如MySql中使用utf8

    b、該代碼採用遍歷的方式,並用MySqlCommandBuilder進行批量更新,所以能轉換的表必須包含主鍵,不包括主鍵的表則不能轉換

    c、引用了Microsoft.VisualBasic.dll進行簡繁轉換

    */

namespace Gb2312ToBig5

    {

class Program

        {

static void Main(string[] args)

            {

//入口

Console.WriteLine("請輸入資料庫所在IP:");

string ip = Console.ReadLine().Trim(); Console.WriteLine("請輸入資料庫名稱:");

string db = Console.ReadLine().Trim(); Console.WriteLine("請輸入登錄資料庫使用者名:");

string user = Console.ReadLine().Trim(); Console.WriteLine("請輸入登錄資料庫密碼:");

string psw = Console.ReadLine(); string connectionString = "Data Source=" + ip + ";User ID=" + user + ";Password=" + psw + ";DataBase=" + db + ";Allow Zero Datetime=true;Charset=utf8;"; Console.WriteLine("生成的資料庫連接字串為:{0},繼續嗎?(Y/N)", connectionString);

if (Console.ReadLine().ToString().ToUpper() == "Y")

                {

//包含所有表名稱的DataTable

DataTable dtAll = tableList(connectionString);

if (dtAll != null)

                    {

if (dtAll.Rows.Count > 0)

                        {

Console.Write("轉換中,請稍候:");

for (int i = 0; i < dtAll.Rows.Count; i++)

                            {

                                dtConvert(dtAll.Rows[i][0].ToString(), connectionString);

                            }

                        }

                    }

                }

            } //將DataTable中每行每列轉為繁體

private static void dtConvert(string dtName, string connectionString)

            {

string sql = "";

                MySqlCommand cmd = null;

                MySqlDataAdapter da = null;

DataTable dt = null;

                MySqlCommandBuilder builder = null; using (MySqlConnection conn = new MySqlConnection(connectionString))

                {

try

                    {

                        sql = "select * from " + dtName;

                        cmd = new MySqlCommand(sql, conn);

                        conn.Open();

                        da = new MySqlDataAdapter(cmd);

//添加主鍵映射

                        da.MissingSchemaAction = MissingSchemaAction.AddWithKey;

                        dt = new DataTable();

                        da.Fill(dt); //遍歷dt做替換

if (dt.Rows.Count > 0)

                        {

//如果表包含主鍵

if (dt.PrimaryKey.Length > 0)

                            {

                                #region 遍歷

for (int i = 0; i < dt.Rows.Count; i++)

                                {

for (int j = 0; j < dt.Columns.Count; j++)

                                    {

if (dt.Columns[j].DataType.ToString() == "System.String")

                                        {

if (dt.Rows[i][j] != null)

                                            {

if (dt.Rows[i][j].ToString() != string.Empty)

                                                {

                                                    dt.Rows[i][j] = getBig5(dt.Rows[i][j].ToString());

Console.Write(".");

                                                }

                                            }

                                        }

                                    }

                                }

                                #endregion builder = new MySqlCommandBuilder(da);

                                da.Update(dt);

                            }

                        }

//釋放資源

                        builder.Dispose();

                        cmd.Dispose();

                        da.Dispose();

                        dt.Clear();

                        dt.Dispose();

                    }

catch (Exception error)

                    {

Console.WriteLine(error.ToString());

                    }

finally

                    {

                        conn.Close();

                    }

                }

            } //遍歷每個表

private static DataTable tableList(string connectionString)

            {

DataTable dt = new DataTable(); using (MySqlConnection conn = new MySqlConnection(connectionString))

                {

//SHOW TABLES為MySQL列出所有表,如SQLServer請使用相關命令

                    MySqlCommand cmd = new MySqlCommand("SHOW TABLES", conn);

                    MySqlDataAdapter da = new MySqlDataAdapter(cmd);

DataSet ds = new DataSet(); try

                    {

                        conn.Open();

                        da.Fill(ds, "temp_tables");

                        dt = ds.Tables["temp_tables"];

                    }

catch (Exception error)

                    {

Console.WriteLine(error.ToString());

                    }

finally

                    {

                        conn.Close();

                    }

                } return dt;

            } //簡體轉繁體

private static string getBig5(string gb2312)

            {

string big5 = "";

if ((gb2312 != null) && (gb2312 != String.Empty))

                {

                    gb2312 = gb2312.Trim();

                    big5 = Strings.StrConv(gb2312, VbStrConv.TraditionalChinese, 0);

                }

return big5;

            }

        }

    }

}

文字檔 繁轉簡

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.Threading.Tasks;

using System.IO;

using System.Text;

using System.Runtime.InteropServices;

namespace EncodeTool

{

public class EncodeTool

    {

        [DllImport("kernel32.dll", EntryPoint = "LCMapStringA")]

public static extern int LCMapString(int Locale, int dwMapFlags, byte[] lpSrcStr, int cchSrc, byte[] lpDestStr, int cchDest);

const int LCMAP_SIMPLIFIED_CHINESE = 0x02000000;

const int LCMAP_TRADITIONAL_CHINESE = 0x04000000;

public static void Main(String[] args)

        {

if (args.Length < 1)

            {

Console.WriteLine("请指定路径!");

            }

String[] files = Directory.GetFiles(args[0]);

if (args.Length == 2)

                files = Directory.GetFiles(args[0], args[1]);

String dir = args[0] + "\\conv";

if (!Directory.Exists(dir))

            {

Directory.CreateDirectory(dir);

            }

for (int i = 0; i < files.Length; i++)

            {

StreamReader sr = new StreamReader(files[i], Encoding.GetEncoding("big5"));

String lines = sr.ReadToEnd();

                sr.Close();

                lines = ConvertString(lines);

//Console.WriteLine(lines);

StreamWriter sw = new StreamWriter(@"D:\EncodeTool\conv\test.txt", false, Encoding.GetEncoding("gb2312"));

//StreamWriter sw = new StreamWriter(dir + "\\" + files[i], false, Encoding.GetEncoding("gb2312"));

                sw.WriteLine(lines);

                sw.Close();

Console.WriteLine("转换 {0} ok!", files[i]);

            }

        }

public static String ConvertString(String lines)

        {

Encoding gb2312 = Encoding.GetEncoding(936);

byte[] src = gb2312.GetBytes(lines);

byte[] dest = new byte[src.Length];

            LCMapString(0x0804, LCMAP_SIMPLIFIED_CHINESE, src, -1, dest, src.Length);

return gb2312.GetString(dest);

        }

    }

}

2011年5月24日 星期二

Visual Studio 最常用的13個快速鍵

Visual Studio 最常用的13個快速鍵

字體大小:小 大

2009-04-24 – 18:20

原文地址:http://blog.tech-cats.com/2008/03/top-13-visual-studio-keyboard-shortcuts.html

如果你經常使用Visual Studio 2008開發程式,下面13個最常用快速鍵你一定很熟悉。

http://www.watch-life.net/life-thinking/top-13-visual-studio-keyboard.html

1. F5:啟動調試
clip_image001

2. F7 /Shift-F7:顯示代碼視窗或顯示設計器視窗
clip_image002

3.Alt-Enter:顯示選中物件的屬性(這是個常用的windows快速鍵,可以用顯示目錄和檔的屬性)

clip_image003

4.F6 / Shift-F6 / Ctrl-Shift-B:生成解決方案/生成專案/生成解決方案
clip_image004

5.Shift-Alt-C: 在項目裡添加類
clip_image005

6.Ctrl-K + Ctrl-C: 注釋一段選擇代碼
clip_image006

7.Ctrl-K + Ctrl-U: 取消一段選擇代碼的注釋
clip_image007

8.Ctrl-M + Ctrl-O / Ctrl-M + Ctrl-P: 折疊定義/展開所有代碼(停止大綱顯示 )
clip_image008

9.Ctrl-M + Ctrl+M:展開或折疊程式碼片段(切換大綱顯示)
clip_image009

10.Ctrl-B + Ctrl-T:切換代碼書簽
clip_image010

11.Ctrl-Alt-P:附加調試器的進程
clip_image011

12.Ctrl-Alt-L:顯示解決方案流覽器
clip_image012

13.Ctrl-Shift-A / Alt-Shift-A:在項目裡添加項/在項目裡移除項
clip_image013

以上是原作者認為提出13個最長常用的,我覺得13個還少了,至少還補充一下這些:
1. 強迫智慧感知:Ctrl+J;
2.強迫智慧感知顯示參數資訊:Ctrl-Shift-空格;
3.格式化整個塊:Ctrl+K+F
4. 檢查括弧匹配(在左右括弧間切換): Ctrl +]
5. 選中從游標起到行首(尾)間的代碼: Shift + Home(End)
6. 在方法定義和調用之點切換:Ctrl+Shift+7(8)
7. 設置中斷點:F9
8. 使用Tab增加縮進,Shift+Tab減少縮進
9. 格式化整篇代碼: Ctrl+K, D
10. 設置書簽:Ctrl+B+T,跳轉書簽:Ctrl+B+N

2009年7月21日 星期二

.NET 非對稱式加密與數位簽章程式設計入門

以下文章參考出處:.NET 非對稱式加密與數位簽章程式設計入門
簡介

這裡不打算講太多加解密的原理,我想,對這篇文章有興趣的人,對於雜湊演算法(hashing algorithm,如:SHA-1)、對稱式加密(如:DES、AES)、非對稱式加密(如:RSA 演算法)、公開金鑰(public key)、私密金鑰(private key)等應該多少都有一點概念吧。當然,在說明程式如何實作之前,還是會帶到一些關鍵的概念與原理,否則光看程式碼而不知其所以然,等到實際應用時恐怕還是不知從何著手。

基本上,數位簽章是基於非對稱式加密的技術,而目前較常用的非對稱式加密方法,則是 RSA 演算法。就實務面的應用而言,你可以不用了解非對稱式加密技術是怎麼來的,但至少要知道以下規則:

1. 私密金鑰必須妥善保存,絕不可外洩;公開金鑰可對外公開,任何人都能取得。
2. 使用公開金鑰加密的資料只有對應的私鑰可解,使用私密金鑰加密的資料只有對應的公開金鑰能解。

基於上述特性,非對稱式加密方法非常適合用於許多需要加密和驗證資料完整性的場合。比如說,Alice 想要寫一封情書給隔壁班的 Bob,並拜託中間人 Eve 轉交給他,可是 Alice 擔心 Eve 會偷看情書的內容,此時她就可以將情書內容用 Bob 的公開金鑰(以下簡稱公鑰)加密;基於前述第 2 項特性,因此只有 Bob 能夠用他的私密金鑰(以下簡稱私鑰)解密。由於 Eve 沒有 Alice 的私密金鑰,因此她無法窺得原文,而且,就算 Eve 花再多時間,也很難在有生之年解開這段密文。

接著就來看看如何使用 .NET Framework 提供的類別實作非對稱式加密和解密。

註:在舉例說明加解密的應用情境時,經常會看到 Alice 和 Bob 這兩個人名,這已經成了慣例。
使用公鑰加密,私鑰解密

.NET Framework 提供的加解密類別全都放在 System.Security.Cryptography 命名空間裡,因此以下的範例程式都必須引用此命名空間。以下示範以 RSA 演算法來實作非對稱式加解密:



1 // 建立 RSA 加解密物件。

2 RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();

3

4 // 欲加密的原文。

5 string orgText = "Hello, 密碼學!";

6

7 // 加密。

8 byte[] orgData = Encoding.Default.GetBytes(orgText);

9 byte[] encryptedData = rsa.Encrypt(orgData, false);

10

11 // 解密。

12 byte[] decryptedData = rsa.Decrypt(encryptedData, false);
13 string decryptedText = Encoding.Default.GetString(decryptedData);


這段範例是把加密和解密的動作一次做完,當然實際上並不會這樣寫,通常是先將資料加密之後,傳送至目標,目標在收到密文之後才做解密的動作。這裡只是為了讓程式儘可能簡單,並且讓你一次看到全貌。

注意範例程式中的第 2 行,它會建立一個 RSACryptoServiceProvider 的物件,而且在建立物件時,就已經產生一對公鑰跟私鑰,在後續的加解密動作時,就是直接使用這對金鑰,因此你不會覺得需要對公鑰私鑰做什麼特別的處理。不過,當你將加密和解密的動作拆開時,就會碰到傳遞金鑰的問題。你可以想像一下,Alice 若要將密文傳給 Bob,而且希望只有 Bob 能看解開,她就必須使用 Bob 的公開金鑰來加密。因此在實作時,程式必須先產生 Bob 的金鑰,並將公鑰儲存在可任意存取的地方,以便加密時使用。

接著就來修改前面的範例,把加密和解密動作拆開。首先,在 Visual Studio 2005 中建立一個 Windows Forms 專案,然後在 form 上面放兩個 TextBox 和兩個 Button,其個別命名與用途如下:

* btnEncrypt:執行加密動作的按鈕。
* btnDecrypt:執行解密動作的按鈕。
* txtEncrypted:用來顯示加密的結果。
* txtDecrypted:用來顯示解密的結果。

我們需要先建立接收者的金鑰,這個動作就放在 form1 的 Load 事件中完成,而 btnEncrypt 和 btnDecrypt 按鈕的事件處理程序則分別處理資料的加密和解密。以下是修改後的完整原始碼:

1 using System;

2 using System.Collections.Generic;

3 using System.ComponentModel;

4 using System.Data;

5 using System.Drawing;

6 using System.Text;

7 using System.Windows.Forms;

8 using System.Security.Cryptography;

9

10 namespace AsymmetricEncryptionDemo

11 {

12 public partial class Form1 : Form

13 {

14 public string bobPrivateKey;

15 public string bobPublicKey;

16

17

18 private void Form1_Load(object sender, EventArgs e)

19 {

20 // 產生 Bob 的金鑰對。

21 RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();

22 bobPrivateKey = rsa.ToXmlString(true);

23 bobPublicKey = rsa.ToXmlString(false);

24 }

25

26 private void btnEncrypt_Click(object sender, EventArgs e)

27 {

28 // 建立 RSA 加解密物件。

29 RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();

30

31 // 匯入公開金鑰(因為要使用接收者的公鑰加密)。

32 rsa.FromXmlString(bobPublicKey);

33

34 // 欲加密的原文。

35 string orgText = "Hello, 密碼學!";

36

37 // 加密。

38 byte[] orgData = Encoding.Default.GetBytes(orgText);

39 byte[] encryptedData = rsa.Encrypt(orgData, false);

40

41 // 將加密過的文字顯示於 UI。

42 txtEncrypted.Text = Convert.ToBase64String(encryptedData);

43 }

44

45 private void btnDecrypt_Click(object sender, EventArgs e)

46 {

47 // 建立 RSA 加解密物件。

48 RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();

49

50 // 將私密金鑰匯入 RSA 物件。

51 rsa.FromXmlString(bobPrivateKey);

52

53 byte[] encryptedData = Convert.FromBase64String(txtEncrypted.Text);

54

55 // 解密。

56 byte[] decryptedData = rsa.Decrypt(encryptedData, false);

57 txtDecrypted.Text = Encoding.Default.GetString(decryptedData);

58 }

59 }

60 }

程式的 Form1_Load 事件處理程序(第 18~24 行)會先產生接收者的金鑰對,並將私鑰和公鑰分別保存在變數 bobPrivateKey 和 bobPublicKey 中。這裡我用 RSACryptoServiceProvider 的 ToXmlString 方法將金鑰匯出成 XML 格式的字串,此方法需要一個布林參數,當此參數為 true 時,表示要包含公鑰及私鑰,若為 false 則只傳回公鑰。因此這裡的 bobPrivateKey 其實包含了公鑰與私鑰。

加密和解密的處理過程都加了詳細的註解,應該不用再額外說明了。比較需要特別注意的是第 32 行和第 51 行,從這兩行可以看出加密跟解密時使用的是公鑰還是私鑰。此外,由於加解密的輸入與輸出資料都是 byte 陣列,因此在顯示加密的結果時,會先將資料轉成 Base64 編碼的字串(第 42 行),而在進行解密時,會先將 Base64 編碼的密文轉回 byte 陣列(第 53 行)。
使用私鑰加密,公鑰解密?

測試過公鑰加密、私鑰解密之後,也許你會想嘗試顛倒過來,用訊息發送者的私鑰加密,然後用公鑰解密。

不行,你不能這麼做。當你嘗試修改前面的範例程式碼,將加密和解密時使用的金鑰互相調換,像這樣(只列出有修改的部分):

....(略)
32 rsa.FromXmlString(bobPrivateKey);
....(略)
51 rsa.FromXmlString(bobPubliceKey);

執行時將會出現錯誤訊息:「機碼無效」(英文是 "Invalid Key")。

可是,前面不是才說過,非對稱式加密的基本規則是:「使用公鑰加密的資料只有對應的私鑰可解,而使用私鑰加密的資料只有對應的公鑰能解」?那又為什麼在撰寫程式時,使用私鑰加密會發生錯誤?

仔細想一下,使用公開金鑰加密,並利用私密金鑰解密,這種方式有其實際的應用場合,就像我們前面舉的例子;這種方式主要是用來防止資料被他人窺視。但如果反過來,假設 Alice 是用她自己的私鑰將訊息加密,那麼這段密文只有她的公鑰可解,而公鑰是可以任意散佈的,所以 Bob 也一定能用 Alice 的公鑰將密文解開。問題就在於,Alice 的公鑰任誰都可以取得,因此,不只 Bob,任何第三者只要能截取到該密文,也一定能夠將它解密。這麼一來,人人都可以輕易解密,那還要這種加密方式做什麼?

如果你花點時間思考一下上面的問題,或許會反駁:「不對!這種方式還是有用處。當我希望我發布的訊息不會遭到第三者篡改時,就可以先用我的私鑰將訊息加密,如此一來,別人如果要看訊息原文,就必須用我的公開金鑰解密,如果解密失敗,那就表示這段密文被篡改過了。」

是的,這種方式的確可以防止資料被篡改。不過,RSA 加解密演算法需要複雜的運算,並不適合用來加密大量資料;況且,如果只是要防止資料被篡改,並不需要將整份明文加密,我們需要的是能夠檢查文件是否被篡改的方法,而這個方法,就是接下來要談的數位簽章(digital signature)。
數位簽章

精確地說,數位簽章並不能防止訊息被篡改,而是讓我們能夠判斷訊息是否遭到篡改。那麼,它是如何做到的?首先,訊息傳送者使用雜湊演算法 (hashing algorithm,例如:SHA-1)將明文加以運算,得到一組雜湊值,這組雜湊值有個正式的名稱,叫做「訊息摘要」(message digest)。顧名思義,它就是將原始資料運算之後得到的一小塊摘要資料,這塊摘要資料雖然小,但是卻足以用來驗證原始資料是否遭到篡改。你也可以把訊息摘要想成是原始資料的縮影,但請注意它既非壓縮,也不是原始資料加密的結果;也就是說,你無法將訊息摘要還原成原始資料(因為雜湊函數是單向、不可逆的)。你或許會質疑,兩份不同的文件是否可能產生相同的雜湊值?儘管機率很低,但還是有可能的(因此便可能偽造簽章),如果對此議題有興趣,可以上網找一下關鍵字「加密 雜湊碰撞」,或者直接一點:「SHA-1 被破解」。

舉例來說,假設 Alice 要傳遞一段訊息給 Bob,而且她希望 Bob 收到時,能夠確認該訊息是否遭到篡改,整個處理過程描述如下:

1. Alice 使用雜湊演算法針對該訊息加以運算並產生雜湊值,然後用私鑰將雜湊值加密,以產生簽章,再將這個加密過的簽章連同訊息內容一併傳遞給 Bob。
2. 當 Bob 收到訊息和加密過的簽章,他可以將訊息透過相同的雜湊演算法重新計算雜湊值,然後用 Alice 的公鑰將簽章解密,以取得原有的雜湊值,最後再比對兩個雜湊值,若不相等, 即表示訊息被篡改了。

注意 Alice 必須用私鑰將雜湊值加密,如此一來,就算有第三者從中攔截訊息,並篡改其內容,然後利用雜湊演算法產生一組新的雜湊值,並替換掉原本的雜湊值,也無法達成其偽造訊息的目的。因為 Bob 會先用 Alice 的公鑰將簽章解密,如果簽章被替換過,就無法解密成功,當然 Bob 也就能發現訊息已經遭到篡改了。

我們可以根據前面描述的情境寫一個範例程式,來模擬及測試數位簽章。首先看一下範例程式的執行畫面:

畫面分成上下兩個區塊,上方區塊代表訊息傳送方,也就是 Alice,下方則是 Bob;紅字所標示的,是箭頭所指的控制項名稱。

當 Alice 要傳送訊息給 Bob 時,首先要產生數位簽章,而視窗中的「產生簽章」按鈕就是在做這件事情。簽章產生完畢之後,Alice 將明文、簽章、公開金鑰一併傳送給 Bob,此時再按「驗證簽章」按鈕,以執行驗證的動作。這裡我將 Alice 和 Bob 的行為分別用兩個類別封裝起來,而根據前面的描述可以知道,Alice 需要對文件簽章,並對外公布其公開金鑰,因此 Alice 類別會像這樣:

1 public class Alice

2 {

3 RSACryptoServiceProvider m_Rsa;

4

5 public Alice()

6 {

7 m_Rsa = new RSACryptoServiceProvider(); // 建立 RSA 加密器,並自動建立一組金鑰對。

8 }

9

10 public byte[] HashAnsSign(byte[] dataToSign)

11 {

12 // 使用 SHA1 演算法產生雜湊值,然後產生簽章。

13 return m_Rsa.SignData(dataToSign, new SHA1CryptoServiceProvider());

14 }

15

16 public string PublicKey

17 {

18 get

19 {

20 return m_Rsa.ToXmlString(false); // 傳入 false 代表只傳回公鑰的 XML 字串。

21 }

22 }

23 }

其中 HashAndSign 方法就是用來產生文件簽章,而 PublicKey 屬性則是 Alice 對外界公佈的公開金鑰,其內容格式為 XML 字串。計算雜湊值的演算法是 SHA-1,產生的簽章會是一塊大小為 128 bytes 的二進位資料區塊。

接著,Bob 只要作驗證簽章的動作,其類別定義如下:

1 public class Bob

2 {

3 public bool VerifySignedData(byte[] data, byte[] signature, string publicKey)

4 {

5 RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();

6 rsa.FromXmlString(publicKey); // 需要用到傳送者(Alice)的公鑰將簽章解密。

7 return rsa.VerifyData(data, new SHA1CryptoServiceProvider(), signature);

8 }

9 }

VerifySignedData 方法接受三個參數,data 是訊息的原始資料,signature 是簽章,publicKey 則是訊息傳送者的公開金鑰。

Alice 和 Bob 物件是在範例程式的視窗載入時就已經建立好。當「產生簽章」按鈕按下時,要執行的處理如下:

1 private void btnSign_Click(object sender, EventArgs e)

2 {

3 byte[] orgData = Encoding.Default.GetBytes(txtOrgData.Text); // 將訊息內容轉成 byte 陣列。

4 byte[] signature = alice.HashAnsSign(orgData); // Alice 產生數位簽章。

5

6 txtSignature.Text = Convert.ToBase64String(signature); // 將數位簽章的內容轉成 Base64 字串,以便顯示。

7 lblSignedLength.Text = "簽章大小: " + signature.Length.ToString() + " bytes";

8 }

最後是「驗證簽章」按鈕的事件處理程序:

1 private void btnVerifySignedData_Click(object sender, EventArgs e)

2 {

3 byte[] orgData = Encoding.Default.GetBytes(txtOrgData.Text); // 訊息原文

4 byte[] signedData = Convert.FromBase64String(txtSignature.Text); // 簽章資料

5

6 bool ok = bob.VerifySignedData(orgData, signedData, alice.PublicKey);

7 if (ok)

8 {

9 MessageBox.Show("驗證成功!");

10 }

11 else

12 {

13 MessageBox.Show("簽章無效!");

14 }

15 }

以下為執行結果:

如果您也有興趣練習看看,可以下載本文的附件,裡面包含數位簽章範例的完整原始碼。MSDN 網站上也有範例可以參考:http://msdn2.microsoft.com/zh-tw/library/9tsc5d0z(VS.80).aspx,而且你會發現 MSDN 上面的範例跟這裡的有些不同,特別是匯入跟匯出金鑰的部份,我用的是 RSACryptoServiceProvider 的 ToXmlString 和 FromXmlString 方法,它是用 ExportParameters 和 ImportParameters 方法。

另外要說明的是,這裡的數位簽章範例程式只是簡單地示範數位簽章的實作,因此該範例並沒有將訊息加密。實際應用時,如果你希望傳遞的訊息不僅可以防止篡改,而且要避免第三者窺視,也可以再加上前面介紹的公鑰加密、私鑰解密的方法,將訊息用接收者的公鑰加密。如此一來,傳送者和接收者的公鑰及私鑰便都會派上用場,其運作流程如下(Alice 為訊息傳送者,Bob 為接收者):

1. Alice 使用雜湊演算法產生訊息摘要。
2. Alice 用私鑰將訊息摘要加密,以產生數位簽章。
3. Alice 用 Bob 的公鑰將訊息加密。
4. Alice 將加密過的訊息連同簽章一併傳送給 Bob。
5. Bob 用 Alice 的公鑰將簽章解密還原成訊息摘要。
6. Bob 用自己的私鑰將密文解密。
7. Bob 將解密後的資料以雜湊演算法重新計算訊息摘要。
8. Bob 比對步驟 5 跟步驟 7 產生的訊息摘要是否相同,以驗證資料是否遭到篡改。

配著圖看會比較清楚:

小結

這篇文章只是個起點,當你開始動手練習,並參考網路上多種範例之後,就會發現密碼學這塊領域在實作上的細節還挺多的。比如說,你可能會發現這裡的範例程式,每次執行時都會重新產生一對金鑰,可是如果你希望金鑰對只產生一次,存放在你的硬碟裡,以後每次執行時就直接取用先前建立的金鑰對,這個問題該如何處理?(你得先了解什麼是「金鑰容器」)或者,你可能會想了解如何實作對稱式加解密,那麼你可以試試 AES(RijndaelManaged 類別)。總之,這塊領域還有很多東西等著我們探索與學習。

2008年5月29日 星期四

Tip:如何正確取得DataTable排序後的Row

一般而言,若我們想取出DataTable內某個欄位的值

程式寫法如下:DataTable.Rows[RowIndex]["ColName"]

但當此Table與Grid做資料繫結後

啟動Grid的自動排序功能,滑鼠點Grid欄位之Head,進行排序後

此時上述寫法將無法取得排序後正確之Row(Rows[RowIndex]仍然傳回排序前之Row)

該怎麼辦呢?

改用以下寫法及可解決此窘境

DataTable.DefaultValue[RowIndex]["ColName"]

2008年5月28日 星期三

如何將資料寫入/讀取登錄檔

有時候我們會將一些資料寫入登入檔

但是又怕資安問題,所以一搬在寫入前都會做一些編碼的動作

以下為登入檔寫入/讀取之方式......

RegistryKey rk = Registry.LocalMachine;
//機碼路徑位置:HKEY_LOCAL_MACHINE\SOFTWARE\Db_Config
rk = rk.CreateSubKey(@"Software\Db_Config");
//以二進位方式存資料(先轉Unicode)[也可以先行加密後再寫入]
rk.SetValue("ConStr", Encoding.Unicode.GetBytes("ConnectionString"), RegistryValueKind.Binary);

//讀取登錄檔值
string bStr = "";
object obj = rk.GetValue("ConStr");
byte[] b = (byte[])obj;
foreach (int s in b)
{
    bStr += s;
}
MessageBox.Show(bStr);

//將二進位轉成字串格式讀取
string str = "";
str = Encoding.Unicode.GetString((byte[])rk.GetValue("ConStr"));
MessageBox.Show(str);