使用X.509憑證加解密

by Vivid 6. 二月 2013 01:07

 

.NET Magazine國際中文電子雜誌
作 者:許薰尹
審 稿:張智凱
文章編號:N130213301
出刊日期:2013/2/6

 

X.509憑證中含公開和私密金鑰組(Key Pair),經常被使用在非對稱式資料加、解密,或是數位簽章。本文將介紹使用.NET Framework內附的憑證建立工具—makecert.exe來建一個測試用憑證,並透過憑證中的公開金鑰來進行加密動作,保護重要的資料不被惡意人士偷窺。

通常X.509憑證是儲存在電腦的憑證存放區位置,在Windows Server 2012作業系統上,憑證存放區位置分為三種,分別是使用者帳戶、服務帳戶與電腦帳戶,我們可以使用作業系統的「Microsoft Management Console」工具程式來檢視憑證。

 

使用makecert.exe建立測試用憑證

要在.NET Framework使用憑證進行加、解密,首先第一個工作是建立憑證。憑證通常要跟有公信力的憑證發行單位(CA)來伸請,本文主要展示透過憑證來加、解密,使用.NET Framework的makecert.exe建立測試用憑證,這個工具建立的憑證僅供測試使用,不建議在真正的系統上使用它。

我們可以使用「Developer Command Prompt for VS2012」工具來執行makecert.exe以建立憑證,在「Developer Command Prompt for VS2012」工具執行以下命令:

 

makecert -n "CN=MyCert" -a sha1 -pe -r -sr LocalMachine -ss my -sky exchange

執行結果,請參考下圖所示:

clip_image002

圖 1:使用makecert.exe建立測試用憑證。

執行makecert.exe可以設定許多參數,以上述範例而言:

  • -n:用來指定憑證的Common Name,以本例來說名稱為「MyCert」。
  • -a:指定建立憑證時使用的演算法,以本例來說演算法為「sha1」。
  • -pe:允許匯出憑證的私密金鑰(private key)。
  • -r:建立一個自我簽章的憑證。
  • -sr:指定憑證存放區位置,本例設定為「電腦帳戶」。
  • -ss:憑證存放區名稱,本例設定為「my」,表示將放在「電腦帳戶」的「個人」憑證存放區。
  • -sky:指定金鑰的類型,可設定為signature或exchange,以應用在數位簽章或加、解密。

檢視憑證

在作業系統「命令提示字元」或在「Developer Command Prompt for VS2012」工具執行mmc.exe,就可以開啟「Microsoft Management Console」,從「檔案」-「新增/移除嵌入式管理單元」,請參考下圖所示:

clip_image004

圖 2:新增/移除嵌入式管理單元。

新增「憑證」到右方的清單,然後,按「確定」按鈕,請參考下圖所示:

clip_image006

圖 3:新增「憑證」管理介面。

你可以選取想要開啟使用者帳戶、服務帳戶或電腦帳戶的憑證存放地,因為本文範例建立憑證時將憑證存放在LocalMachine,在此步驟要選取「電腦帳戶」,然後,按「下一步」按鈕,請參考下圖所示:

clip_image008

圖 4:選取「電腦帳戶」。

下一個畫面是選取電腦的名字,選取「本機電腦」,然後,按「完成」按鈕,請參考下圖所示:

clip_image010

圖 5:選取電腦的名字。

從「Microsoft Management Console」-「憑證(本機電腦)」-「個人」「憑證」下就可以看到剛剛建立起來的MyCert憑證,請參考下圖所示:

clip_image012

圖 6:檢視MyCert憑證。

你可以使用滑鼠雙擊MyCert,檢視憑證的詳細內容,請參考下圖所示:

clip_image013

圖 7:檢視憑證的詳細內容。

讀取憑證資訊

憑證建立完成之後,便可以使用.NET Framework提供的類別來讀取憑證資訊。您可以使用System.Security.Cryptography.X509Certificates命名空間下的X509Certificate2類別來讀取之。舉例來說,參考以下程式碼範例:

namespace EncDec {
  class Program {
    static void Main( string [] args ) {
      X509Store store = new X509Store( StoreName.My , StoreLocation.LocalMachine );
      X509Certificate2 certificate = null;

      var certificateName = "CN=MyCert";
      store.Open( OpenFlags.ReadOnly );
      foreach ( var s in store.Certificates ) {
        if ( s.SubjectName.Name == certificateName ) {
          certificate = s;
          Console.WriteLine( "Subject Name : " + certificate.SubjectName.Name );
          Console.WriteLine( "\n含Private Key :  " + certificate.HasPrivateKey );
          Console.WriteLine( "\n有效期限 : {0} - {1} " , certificate.NotBefore , certificate.NotAfter );
          Console.WriteLine( "\n序號 : " + certificate.SerialNumber );
          Console.WriteLine( "\n版本 : " + certificate.Version );
          Console.WriteLine( "\nAlgorithm : " + certificate.SignatureAlgorithm.FriendlyName );
          Console.WriteLine( "\nPrivate Key : " + certificate.PrivateKey.ToXmlString( false ) );
          Console.WriteLine( "\nPublic Key : " + certificate.PublicKey.Key.ToXmlString( false ) );
          break;
        }
      }
      store.Close( );
    }
  }
}

 

我們先利用X509Store類別的Open方法來開啟本機電腦的個人憑證存放地,利用for迴圈讀取個人憑證存放地中所有的憑證來比對,找尋出SubjectName為MyCert的憑證後,讀取憑證的相關資訊印出,像是憑證有效期限與公開金鑰、私密金鑰等等,此範例程式的執行結果參考下圖:

clip_image015

圖 8:印出憑證的相關資訊。

 

使用憑證加密

有了憑證之後,您就可以利用其中的公開金鑰資訊對資料做加密。.NET Framework的System.Security.Cryptography命名空間下的RSACryptoServiceProvider類別來設計非對稱式加密。RSACryptoServiceProvider類別支援資料加、解密與數位簽章的功能,除此之外,也提供一些方法可以將加解、密使用的金鑰資訊匯出,以便在其它台電腦匯入金鑰進行解密。

以下範例程式碼讀取本機憑證存放地中的MyCert憑證,並建立RSACryptoServiceProvider物件,使用憑證中的公開金鑰來加密資料。RSACryptoServiceProvider物件的Encrypt方法可以將資料加密,回傳的結果便是密文,我們將密文印出在主控台以及寫到一個文字檔案中:

namespace EncDec {
  class Program {
    static void Main( string [] args ) {
      X509Store store = new X509Store( StoreName.My , StoreLocation.LocalMachine );
      X509Certificate2 certificate = null;
      var certificateName = "CN=MyCert";
      store.Open( OpenFlags.ReadOnly );
      foreach ( var s in store.Certificates ) {
        if ( s.SubjectName.Name == certificateName ) {
          certificate = s;
          break;
        }
      }
      store.Close( );

      RSACryptoServiceProvider RSA = ( RSACryptoServiceProvider ) certificate.PublicKey.Key;
      byte [] dataToEnc = Encoding.UTF8.GetBytes( "Hello World" );
      byte [] encData = RSA.Encrypt( dataToEnc , false );
      Console.WriteLine( BitConverter.ToString( encData ) );
      File.WriteAllBytes( "encData.txt" , encData );
    }
  }
}

 

此範例程式的執行結果參考下圖:

clip_image017

圖 9:使用憑證加密過的內容。

 

使用憑證解密

使用憑證解密的做法和上一節加密雷同,參考以下程式碼,先從本機憑證存放地取得要解密用的MyCert憑證,從PrivateKey屬性便可以取得私密金鑰來解密,最後再叫用RSACryptoServiceProvider類別的Decrypt方法,將從檔案讀取出加密過的密文做解密處理。

namespace EncDec {
  class Program {
    static void Main( string [] args ) {
      X509Store store = new X509Store( StoreName.My , StoreLocation.LocalMachine );
      X509Certificate2 certificate = null;
      var certificateName = "CN=MyCert";
      store.Open( OpenFlags.ReadOnly );
      foreach ( var s in store.Certificates ) {
        if ( s.SubjectName.Name == certificateName ) {
          certificate = s;
          break;
        }
      }
      store.Close( );

      byte [] encData = File.ReadAllBytes( "encData.txt" );
      RSACryptoServiceProvider RSA = ( RSACryptoServiceProvider ) certificate.PrivateKey;
      Byte [] data = RSA.Decrypt( encData , false );
      Console.WriteLine( Encoding.UTF8.GetString( data ) );
    }
  }
}

 

此範例程式的執行結果參考下圖,印出解密過的明文:

clip_image018

圖 10:使用憑證解密過的內容。

 

匯出憑證(*.pfx)

憑證的資訊有時以 Public-Key Cryptography Standards (PKCS)格式存在,檔案的附檔名為pfx。例如在「Microsoft Management Console」,從「憑證(本機電腦)」-「個人」-「憑證」,找到建立的「MyCert」憑證後,按滑鼠右鍵,選取快捷選單中的「所有工作」-「匯出」,請參考下圖所示:

clip_image020

圖 11:匯出個人憑證。

就會啟動憑證匯出精靈,請參考下圖所示:

clip_image022

圖 12:憑證匯出精靈。

選取「是,匯出私密金鑰」,這樣才能夠透過私密金鑰來進行解密的動作,然後,按「下一步」,請參考下圖所示:

clip_image024

圖 13:匯出私密金鑰。

下一步是選取檔案格式,選取「個人資訊交換 - PKCS #12」,然後,按「下一步」,請參考下圖所示:

clip_image026

圖 14:選取檔案格式。

為了安全性考量,應該為憑證設定一個密碼,然後,按「下一步」,請參考下圖所示:

clip_image028

圖 15:為憑證設定一個密碼。

再來設定一個檔案名稱,請參考下圖所示:

clip_image030

圖 16:設定一個檔案名稱。

按下「完成」按鈕,就完工了,請參考下圖所示:

clip_image032

圖 17:匯出完成。

 

使用pfx檔案加解密

若要使用pfx檔案對資料加、解密,可以建立X509Certificate物件,從建構函式傳入pfx檔案名稱,以及密碼。接著建立X509Certificate2類別,從建構函式傳入X509Certificate物件,就可以利用X509Certificate物件來做加密動作,參考下列程式碼:

 

namespace EncDec {
  class Program {
    static void Main( string [] args ) {
      byte [] cipher;
      X509Certificate cert;
      X509Certificate2 cert2;
      cert = new X509Certificate( "mykey.pfx" , "111" );
      cert2 = new X509Certificate2( cert );
      RSACryptoServiceProvider rsa = ( RSACryptoServiceProvider ) cert2.PublicKey.Key;
      byte [] data = ASCIIEncoding.ASCII.GetBytes( "1234" );
      cipher = rsa.Encrypt( data , false );
      Console.WriteLine( BitConverter.ToString( cipher ) );
      File.WriteAllBytes( "encDatapfx.txt" , cipher );
    }
  }
}

 

 

執行範例後,「1234」字串便被加密,印在主控台,同時也儲存到encDatapfx.txt檔案中,請參考下圖所示:

clip_image034

圖 18:印出加密過的內容。

下列程式碼便是使用pfx檔案進行解密的範例:

namespace EncDec {
  class Program {
    static void Main( string [] args ) {

      X509Certificate cert;
      X509Certificate2 cert2;
      cert = new X509Certificate( "mykey.pfx" , "111" );
      cert2 = new X509Certificate2( cert );
      RSACryptoServiceProvider rsa = ( RSACryptoServiceProvider ) cert2.PrivateKey;
      byte [] cipher = File.ReadAllBytes( "encDatapfx.txt" );
      byte [] data = rsa.Decrypt( cipher , false );
      System.Text.ASCIIEncoding enc = new System.Text.ASCIIEncoding( );
      string s = enc.GetString( data );
      Console.WriteLine( s );
    }
  }
}

 

執行範例後,encDatapfx.txt檔案中加密的字串便被解密,並將「1234」字串印在主控台,請參考下圖所示:

clip_image035

圖 19:印出解密過的內容。

Tags:

.NET Framework | .NET Magazine國際中文電子雜誌 | 許薰尹Vivid Hsu

評論 (3) -

zgchen
zgchen Taiwan
2013/2/20 上午 09:12:11 #

讚!!!

另------------------------
使用憑證加密
有了憑證之後,您就可以利用其中的公開金鑰資訊對資料做加、解密。<---public key 僅能加密,是否打太快筆誤了!!

回覆

Vivid
Vivid Taiwan
2013/2/27 上午 11:59:24 #

謝謝提醒,已修正  ^_^

回覆

小精靈
小精靈 Taiwan
2014/7/11 下午 12:42:40 #

請教一個問題,就是我照著您的上做一開始都是正常的,但是當我電腦關機在開啟時在讀取private key時都會出現[System.Security.Cryptography.CryptographicException: 機碼組不存在]的錯誤訊息,請問大大這匯是什麼原因??謝謝

回覆

新增評論




  Country flag
biuquote
  • 評論
  • 線上預覽
Loading






NET Magazine國際中文電子雜誌

NET Magazine國際中文電子版雜誌,由恆逸資訊創立於2000,自發刊日起迄今已發行超過500篇.NET相關技術文章,擁有超過40000名註冊讀者群。NET Magazine國際中文電子版雜誌希望藉於電子雜誌與NET Developer達到共同學習與技術新知分享,歡迎每一位對.NET 技術有興趣的朋友們多多支持本雜誌,讓作者群們可以有持續性的動力繼續爬文。<請加入免費訂閱>