2012年4月4日 星期三

在C#中使用RSA加解密

RSA是非對稱式加密,金鑰分為公鑰和私鑰兩種,詳情可參考RSA加密演算法。一般來說,公鑰會存成.crt,並且撒出去讓大家知道,而私鑰則是存成.pfx,由自己保管,同時加上密碼來保護

在使用這兩把鑰匙的時候,需先用X509Certificate2物件將其讀出

using System.Security.Cryptography.X509Certificates;

RSACryptoServiceProvider LoadPublicKeyFromFile(
string publicKeyFile)
{
///load public key from .crt
X509Certificate2 pubKey =
new X509Certificate2(publicKeyFile);

return (RSACryptoServiceProvider)pubKey.PublicKey.Key;
}

RSACryptoServiceProvider LoadPrivateKeyFromFile(
string privateKeyFile, string password)
{
///load private key from .pfx with password
X509Certificate2 priKey =
new X509Certificate2(privateKeyFile, password);

return (RSACryptoServiceProvider)priKey.PrivateKey;
}

有時會需要將鑰匙存入資料庫,可以採用XML的格式,RSACryptoServiceProvider有提供很方便的存讀XML字串的函式。

string ToXmlString(RSACryptoServiceProvider key)
{
/// true for private key, false for public key
return key.ToXmlString(true);
}

RSACryptoServiceProvider FromXmlString(string xmlString)
{
RSACryptoServiceProvider key =
new RSACryptoServiceProvider();
key.FromXmlString(xmlString);

return key;
}

注意,不論是公鑰或私鑰皆可以取得一個RSACryptoServiceProvider的物件。實際上在使用RSA加解密演算法時,有時會需要用到私鑰加密,公鑰解密(數位簽章,由自己發給別人,證實該資訊是由我發出),有時則是公鑰加密,私鑰解密(確認只有自己才可以看的到的文件),所以這兩把鑰匙的用法是相同的。

byte[] Encrypt(byte[] rawData, RSACryptoServiceProvider key)
{
/// encrypt without OAEP, but PKCS#1 v1.5 padding
return key.Encrypt(rawData, false);
}

byte[] Decrypt(byte[] encData, RSACryptoServiceProvider key)
{
/// decrypt without OAEP, but PKCS#1 v1.5 padding
return key.Decrypt(rawData, false);
}

在這邊使用哪種padding演算法都可以,但是記得加解密的時候需要用同樣的演算法即可。另外,因為加解密時都是採用byte陣列做為原始資料,所以如果原本是string物件的話,可以透過Convert.FromBase64String及Encoding.UTF8.GetString互為轉換。

--
參考資料
RSACryptoServiceProvider Class
Optimal asymmetric encryption padding

2012年2月28日 星期二

報告服務(Sql Server Reporting Service, SSRS)的高可用性(High Availability, HA)

微軟的資料庫伺服器(Sql Server)提供了報告服務(Reporting Service),讓客戶可以很容易的產生有關資料庫狀態的報告,像是登入的列表或是某個事件發生的頻率等等。只要有相對應的記錄,都可以用查詢的方式,定期或是依照需求產生報告。有關報告服務的架構可以參考剖析SQL Server 2005中的報告服務架構

在提供這樣的服務的同時,系統的高可用性也必須涵蓋這部分。報告服務的高可用性可以分成三個方面來考慮,服務,報告服務資料庫,以及查詢資料庫。

服務的部分比較單純,為了提供高可用性,就多建立幾台報告服務伺服器即可。而查詢資料庫的部分則如同一般資料庫伺服器的高可用性,可以採用叢集(Cluster)或是鏡像(Mirroring)等方式。值得注意的是,由於報告服務的查詢字串有提供故障轉移(Failover Partner)的選項,所以如果採用鏡像的方式時,只需要將備份的伺服器網址填入該欄位,在故障發生時,報告服務就會自動查詢備份的資料庫伺服器,而不會造成服務的中斷。

最困難的部分在於報告服務資料庫的高可用性。由於在報告服務伺服器端僅能設定一台報告服務伺服器(至少在2008之前尚未提供,可以參考ReportServer (TempDB) Mirror capability),所以如果採用鏡像的方式,在主伺服器發生故障時,無法自動將設定更新到備份伺服器。

在這樣的狀況下,第一種選擇是直接將報告服務的資料庫切開,每組報告服務都有自己的資料庫,所以在某一台報告服務+資料庫的伺服器故障時,其他台仍然可以正常運作。但問題是這些資料庫之間沒有同步,所以必須對每個資料庫匯入相同的範本及資料源。同時,報告服務的歷史資料也會散落在各地。

第二種選擇是純粹的鏡像,將所有的報告服務設定相同的資料庫,如何設定可以參考How to: Configure a Report Server Scale-Out Deployment (Reporting Services Configuration)。此方法在主要資料庫伺服器故障時,需要手動將報告服務的伺服器設定到備份的伺服器。如果不希望手動,也可以透過代理服務(Agent Service),在故障發生時,用rsconfig設定報告服務的資料庫。此方法的細節可以參考Reporting Services Disaster Recovery

第三種是微軟官方的建議,就是使用叢集的方式,設定可以參考安裝 SQL Server 2008 容錯轉移叢集心得筆記。不過在叢集的資料庫伺服器上,無法同時安裝報告服務,也就是說,需要額外的數台伺服器來提供高可用性的報告服務。細節可以參考Planning a Deployment Topology

--
參考資料
SQL Server Reporting Services Disaster Recovery Case Study
How to: Configure SharePoint Integration on Multiple Servers

2012年1月31日 星期二

在虛擬機器(Vitual Machine)中擴充硬碟

使用虛擬機器最重要的好處之一,就是可以彈性的擴充硬體
硬體的擴充動作相較於實體機器來說,就少掉了更新硬體這一步驟
(當然要在開虛擬機器的實體機器的硬體限制下)
而Windows在更新CPU或記憶體都是自動完成的
所以在虛擬機器上擴充這兩者都僅僅需要關機,設定,開機三個步驟

如果是一般的硬碟擴充,只要先在虛擬機器的設定中延伸(Expand)硬碟

再到作業系統裡面使用內建的diskpart將同一個硬碟擴充即可

C:\Documents and Settings\Administrator>diskpart

Microsoft DiskPart version 5.2.3790.3959
Copyright (C) 1999-2001 Microsoft Corporation.
On computer: WIN2K3X86

DISKPART> list volume

Volume ### Ltr Label Fs Type Size Status Info
---------- --- ----------- ----- ---------- ------- --------- --------
Volume 0 C NTFS Partition 15 GB Healthy System
Volume 1 D CD-ROM 0 B Healthy
Volume 2 E New Volume NTFS Partition 20 GB Healthy

DISKPART> select volume 2

Volume 2 is the selected volume.

DISKPART> extend

DiskPart successfully extended the volume.

DISKPART> list volume

Volume ### Ltr Label Fs Type Size Status Info
---------- --- ----------- ----- ---------- ------- --------- --------
Volume 0 C NTFS Partition 15 GB Healthy System
Volume 1 D CD-ROM 0 B Healthy
* Volume 2 E New Volume NTFS Partition 21 GB Healthy

DISKPART>


另外,在Windows 2003中
因為保護作業系統的檔案,所以無法變更系統磁碟的大小
這時候就需要一點小技巧,在把系統碟延伸後
先將系統碟掛到另外一台機器當作一般磁碟並擴充
此時用原本的虛擬機器開機,就會發現系統磁碟大小已經更改成功了

--
參考資料
How to Extend Windows Boot Volumes in VMware
如何擴充 VMware 虛擬磁碟及作業系統容量

2011年1月7日 星期五

常見開源協議(Open Source License)的比較

開放原始碼這樣的想法非常崇高,藉由自由分享各自的程式碼
讓軟體的發展可以更加的快速而且安全
(透明度可以讓全世界一起除錯,而且沒辦法藏後門)

但是對於"自由",大家的想法都不太一樣
比如說,如果有人拿你寫的程式去賣錢呢?
這種顧慮會讓很多開發者不願意分享,所以開源協議就出現了
目的就是,讓作者可以在被保護的狀況下
依照期待(選擇的開源協議)的讓全世界的人一起分享

目前常見的開源協議大都經過Open Source Initiative批准
協議書的全文都可以在Open Source Licenses找到
(從協議的數量就可以看出,這方面也是相當的"自由")
最常見的有GPL, GNU General Public License, LGPL, The GNU Lesser General Public License, BSD License
這些協議的一個共同點,就是再發佈的時候需要伴隨原有的協議

其中最嚴厲(崇尚自由,並要求所有人一起崇尚自由)的是GPL
強硬的要求(傳染)所有"引用/修改/衍生"GPL授權的程式碼的軟體
也必須採用GPL授權,並提供未來開發者同樣的自由,包括
1. 以任何目的執行此程式的自由
2. 再發行複製件的自由
3. 改進此程式,並公開發布改進的自由
基於這樣的規定,所以商業軟體或是不打算公開程式碼的軟體
都不得修改甚至引用GPL授權的程式碼
著名的GPL自由軟體包括Linux核心和GCC

LGPL則是將GPL條件稍微放寬,
如果僅是"引用(Link)"LGPL授權的函式庫
則不需要公開該軟體的程式碼
所以對於商業軟體來說,是可以引用LGPL的函式庫的
但是,對於"修改/衍生"LGPL授權軟體的部分,則同樣必須採用LGPL的授權
也就是一樣需要公開,且提供未來開發者同等的自由
著名的LGPL自由軟體有Mozilla跟OpenOffice.Org

BSD相對來說就寬鬆的多了
主要差異是在"修改/衍生"BSD授權的軟體時
除了版權聲明中仍需包含原來代碼中的BSD協議以外
開發者有權選擇是否要開放原始碼,或是做為商業用途

由此可以看出,如果是公司企業打算要使用開放原始碼的授權軟體的時候
BSD授權軟體可以直接使用甚至修改衍生
LGPL授權軟體則是僅能引用(Link)
GPL授權軟體則是儘量不使用,或是規避GPL的授權範圍
例如避免GPL風險的另類方法所提的方法

--
參考資料
五種開源授權規範的比較 (BSD, Apache, GPL, LGPL, MIT)
GPL 常見問題
自由軟體專案授權方式的轉換(上):不得撤銷原授權條款
自由軟體專案授權方式的轉換(下):新版本號另以更改後的授權方式釋出
在原始碼之外:編譯與安裝資訊的提供
散佈 GPL 衍生程式所須提供的原始碼範圍

2010年12月28日 星期二

在C#中讀取Excel的試算表

取出試算表的名稱後,就可以針對單一試算表進行讀取

public List LoadExcelSheet(
string excelFile, string sheetName)
{
List dataList = new List();

/// load excel file as an ole database
OleDbConnection excelDatabase =
new OleDbConnection(string.Format(
"Provider = Microsoft.Jet.OLEDB.4.0;" +
"Data Source = {0};" +
"Extended Properties='Excel 8.0;HDR=No'",
excelFile));
excelDatabase.Open();

/// query sheet from excel file
OleDbDataAdapter sheetAdapter =
new OleDbDataAdapter(string.Format(
"SELECT * FROM [{0}]", sheetName),
excelDatabase);
DataTable fileData = new DataTable();
sheetAdapter.Fill(fileData);

/// scan all cells in the sheet
foreach (DataRow row in fileData.Rows)
{
foreach (object item in row.ItemArray)
{
if (!(item is DBNull))
{
dataList.Add(item.ToString());
}
}
}

/// close and release database connection
excelDatabase.Close();

return dataList;
}

這裡值得注意的是,如果試算表中有些欄位是空的
讀出來的資料會是DBNull的型別,其餘則會是string的型別

另外,如果是手動輸入試算表名稱的話
要記得在試算表名稱後面加一個$,例如預設的"Sheet1$"

--
參考資料
轉錄 C# 讀取 Excel
C# 讀取 Excel
DataTable 類別

在C#中讀取Excel的試算表名稱

在將Excel的檔案當做資料庫的存取的時候
可以先取得檔案中每個試算表的名稱,再做處理

public List LoadExcelSheetNameList(
string excelFile)
{
List sheetNameList = new List();

/// load excel file as an ole database
OleDbConnection excelDatabase =
new OleDbConnection(string.Format(
"Provider = Microsoft.Jet.OLEDB.4.0;" +
"Data Source = {0};" +
"Extended Properties='Excel 8.0;HDR=No'",
excelFile));
excelDatabase.Open();

/// load excel sheet as DataRow
DataRow[] sheetList =
excelDatabase.GetSchema("Tables").Select();
foreach (DataRow sheet in sheetList)
{
/// query each sheet name from excel file
sheetNameList.Add(
sheet["TABLE_NAME"] as string);
}

/// close and release database connection
excelDatabase.Close();

return sheetNameList;
}

在得到名稱後,就可以針對單一的試算表進行讀取

--
參考資料
[工作] 取得Excel頁籤名稱
如何取得 Excel 中的 Sheet Name

2010年12月27日 星期一

在C#中使用SQLite資料庫

自己開發程式的時候,想用資料庫又不希望花一大筆錢
常常會使用SQLite,一個輕量級的嵌入式資料庫系統(僅單一檔案)
由於.net原生並不支援SQLite
可以去System.Data.SQLite找到在公開的函式庫
注意如果是x64的程式,則需要使用x64版本的函式庫,無法混用

在每次使用資料庫的時候,都需要建立與相關資料庫的連結

private SQLiteConnection ConnectDatabase()
{
string path = "Test.db";
string password = "Password";
SQLiteConnection connection;

if (!File.Exists(_databasePath))
{
/// for non-exist database, create it
SQLiteConnection.CreateFile(path);
connection = new SQLiteConnection(
"Data Source = " + path);
connection.Open();
connection.ChangePassword(password);
}
else
{
/// for exist database, connect it
connection = new SQLiteConnection(
"Data Source = " + path);
connection.SetPassword(password);
connection.Open();
}

return connection;
}

這邊要注意的是,由於SQLite本身並沒有支援資料庫加密
有關Password的功能(紅字部分)是System.Data.SQLite實做的
所以製作出來的加密資料庫並不能被其他的SQLite應用讀取
(例如Firefox的附加元件SQLite Manager)
所以如果不打算加密,建議將紅字部分拿掉

在連結之後,就可以透過該連結,執行SQL的命令

private void RunCommand(
SQLiteConnection connection, string commandText)
{
SQLiteCommand command =
new SQLiteCommand(connection);
command.CommandText = commandText;
command.ExecuteNonQuery();
}

除了不需要回傳值的命令(例如CREATE TABLE)以外
SQL最重要的當然是Query的指令

private List RunQueryCommand(
SQLiteConnection connection, string commandText)
{
List<string> result = new List<string>();

SQLiteCommand command =
new SQLiteCommand(connection);
command.CommandText = commandText;

using (SQLiteDataReader queryResult =
command.ExecuteReader())
{
/// read one row one time
while (queryResult.Read())
{
/// change index to get different column
result.Add(queryResult.GetValue(0)
.ToString());
}
}

return result;
}

當然,在使用完畢後一定要記得將連結中止,否則資料庫檔案會被鎖住

private void DisconnectDatabase(
SQLiteConnection connection)
{
connection.Close();
}

另外,讀取資料庫的時候,如果不能肯定資料表是否存在
則需要先做一個檢查,否則會跳出例外狀況
(當然也是可以直接用try catch將Command包住,端看習慣)

private bool IsTableExist(
SQLiteConnection connection, string name)
{
return connection.GetSchema("Tables")
.Select("Table_Name = '" + name + "'")
.Length > 0;
}

--
參考資料
SQLite 簡介
System.Data.SQLite
C sharp or .Net 使用sqlite 設定
SQLite Manager
SQL語法教學
Check if table exists