2009-08-20 151 views
57

我正在構建一個自定義購物車,其中CC號和Exp date將存儲在數據庫中直到處理(然後刪除)。我需要加密這些數據(顯然)。如何通過RSA生成唯一的公鑰和私鑰

我想使用RSACryptoServiceProvider類。

這是我的代碼來創建我的密鑰。

public static void AssignNewKey(){ 
    const int PROVIDER_RSA_FULL = 1; 
    const string CONTAINER_NAME = "KeyContainer"; 
    CspParameters cspParams; 
    cspParams = new CspParameters(PROVIDER_RSA_FULL); 
    cspParams.KeyContainerName = CONTAINER_NAME; 
    cspParams.Flags = CspProviderFlags.UseMachineKeyStore; 
    cspParams.ProviderName = "Microsoft Strong Cryptographic Provider"; 
    rsa = new RSACryptoServiceProvider(cspParams); 

    string publicPrivateKeyXML = rsa.ToXmlString(true); 
    string publicOnlyKeyXML = rsa.ToXmlString(false); 
    // do stuff with keys... 
} 

現在計劃是將私鑰xml存儲在連接到管理器鑰匙鏈的USB驅動器上。

每當經理離開公司時,我希望能夠生成新的公鑰和私鑰(並用新的公鑰重新加密所有當前存儲的CC號碼)。

我的問題是這個代碼生成的密鑰總是一樣的。我將如何每次生成一組獨特的密鑰?

UPDATE。我的測試代碼如下:
注意:這裏的「privatekey」參數是原始私鑰。爲了更改密鑰,我需要驗證私鑰是否有效。

在Default.aspx.cs

public void DownloadNewPrivateKey_Click(object sender, EventArgs e) 
{ 
    StreamReader reader = new StreamReader(fileUpload.FileContent); 
    string privateKey = reader.ReadToEnd(); 
    Response.Clear(); 
    Response.ContentType = "text/xml"; 
    Response.End(); 
    Response.Write(ChangeKeysAndReturnNewPrivateKey(privateKey)); 
} 

在Crytpography.cs:

public static privateKey; 
public static publicKey; 
public static RSACryptoServiceProvider rsa; 

public static string ChangeKeysAndReturnNewPrivateKey(string _privatekey) 
{ 

    string testData = "TestData"; 
    string testSalt = "salt"; 
    // encrypt the test data using the exisiting public key... 
    string encryptedTestData = EncryptData(testData, testSalt); 
    try 
    { 
     // try to decrypt the test data using the _privatekey provided by user... 
     string decryptTestData = DecryptData(encryptedTestData, _privatekey, testSalt); 
     // if the data is successfully decrypted assign new keys... 
     if (decryptTestData == testData) 
     { 
      AssignNewKey(); 
      // "AssignNewKey()" should set "privateKey" to the newly created private key... 
      return privateKey; 
     } 
     else 
     { 
      return string.Empty; 
     } 
    } 
    catch (Exception ex) 
    { 
     return string.Empty; 
    } 
} 
public static void AssignParameter(){ 
    const int PROVIDER_RSA_FULL = 1; 
    const string CONTAINER_NAME = "KeyContainer"; 
    CspParameters cspParams; 
    cspParams = new CspParameters(PROVIDER_RSA_FULL); 
    cspParams.KeyContainerName = CONTAINER_NAME; 
    cspParams.Flags = CspProviderFlags.UseMachineKeyStore; 
    cspParams.ProviderName = "Microsoft Strong Cryptographic Provider"; 
    rsa = new RSACryptoServiceProvider(cspParams); 
} 
public static void AssignNewKey() 
{ 
    AssignParameter(); 

    using (SqlConnection myConn = new SqlConnection(Utilities.ConnectionString)) 
    { 
     SqlCommand myCmd = myConn.CreateCommand(); 

     string publicPrivateKeyXML = rsa.ToXmlString(true); 
     privateKey = publicPrivateKeyXML; // sets the public variable privateKey to the new private key. 

     string publicOnlyKeyXML = rsa.ToXmlString(false); 
     publicKey = publicOnlyKeyXML; // sets the public variable publicKey to the new public key. 

     myCmd.CommandText = "UPDATE Settings SET PublicKey = @PublicKey"; 
     myCmd.Parameters.AddWithValue("@PublicKey", publicOnlyKeyXML); 
     myConn.Open(); 

     myComm.ExecuteScalar(); 
    } 
} 
public static string EncryptData(string data2Encrypt, string salt) 
{ 
    AssignParameter(); 

    using (SqlConnection myConn = new SqlConnection(Utilities.ConnectionString)) 
    { 
     SqlCommand myCmd = myConn.CreateCommand(); 

     myCmd.CommandText = "SELECT TOP 1 PublicKey FROM Settings"; 

     myConn.Open(); 

     using (SqlDataReader sdr = myCmd.ExecuteReader()) 
     { 
      if (sdr.HasRows) 
      { 
       DataTable dt = new DataTable(); 
       dt.Load(sdr); 
       rsa.FromXmlString(dt.Rows[0]["PublicKey"].ToString()); 
      } 
     } 
    } 

    //read plaintext, encrypt it to ciphertext 
    byte[] plainbytes = System.Text.Encoding.UTF8.GetBytes(data2Encrypt + salt); 
    byte[] cipherbytes = rsa.Encrypt(plainbytes, false); 
    return Convert.ToBase64String(cipherbytes); 
} 
public static string DecryptData(string data2Decrypt, string privatekey, string salt) 
{ 
    AssignParameter(); 

    byte[] getpassword = Convert.FromBase64String(data2Decrypt); 

    string publicPrivateKeyXML = privatekey; 
    rsa.FromXmlString(publicPrivateKeyXML); 

    //read ciphertext, decrypt it to plaintext 
    byte[] plain = rsa.Decrypt(getpassword, false); 
    string dataAndSalt = System.Text.Encoding.UTF8.GetString(plain); 
    return dataAndSalt.Substring(0, dataAndSalt.Length - salt.Length); 
} 
+0

你是如何測試的? – 2009-08-20 16:55:08

+0

我基本上是從.net頁面調用AssignNewKey()函數,然後檢查新的「publicPrivateKeyXML」對我的以前的版本。我會更新上面的問題以包含我的測試代碼。 – 2009-08-20 18:14:27

+2

這有點切題,但您是否意識到爲了存儲信用卡號碼,您需要您的系統符合PCI標準?看到http://stackoverflow.com/questions/4300863/storing-credit-card-number-pci – Art 2012-05-03 05:23:37

回答

7

我最終什麼事做的是創建一個新的KeyContainer名稱基於當前的DateTime(DateTime.Now.Ticks.ToString()),每當我需要創建一個新的密鑰和將容器名稱和公鑰保存到數據庫中。此外,每當我創建新密鑰時,我都會執行以下操作:

public static string ConvertToNewKey(string oldPrivateKey) 
{ 

    // get the current container name from the database... 

    rsa.PersistKeyInCsp = false; 
    rsa.Clear(); 
    rsa = null; 

    string privateKey = AssignNewKey(true); // create the new public key and container name and write them to the database... 

     // re-encrypt existing data to use the new keys and write to database... 

    return privateKey; 
} 
public static string AssignNewKey(bool ReturnPrivateKey){ 
    string containerName = DateTime.Now.Ticks.ToString(); 
    // create the new key... 
    // saves container name and public key to database... 
    // and returns Private Key XML. 
} 

創建新密鑰之前。

+1

如果你發佈了完整的解決方案,那將是很好的,因爲我無法弄清楚我在正確地閱讀你的代碼的意見中做了什麼 – ShaneKm 2013-06-12 09:54:45

+0

?你有一個名爲'privateKey'的變量,但是你的意見建議你創建一個新的公鑰? – barrypicker 2016-05-05 18:48:28

+0

我寫這篇文章已經差不多7年了......但我認爲'AssignNewKey'方法的目的是創建一個新的'public''和''private'鍵,將'public'鍵存儲在數據庫中,同時返回'private'鍵作爲XML字符串。 – 2016-05-05 20:17:15

21

RSACryptoServiceProvider(CspParameters)構造函數創建其被存儲在本地機器上的密鑰庫一個密鑰對。如果您已經有了指定名稱的密鑰對,它將使用現有的密鑰對。

這聽起來好像你是而不是有興趣在機器上存儲密鑰。

所以使用RSACryptoServiceProvider(Int32)構造:

public static void AssignNewKey(){ 
    RSA rsa = new RSACryptoServiceProvider(2048); // Generate a new 2048 bit RSA key 

    string publicPrivateKeyXML = rsa.ToXmlString(true); 
    string publicOnlyKeyXML = rsa.ToXmlString(false); 
    // do stuff with keys... 
} 

編輯:

或者嘗試PersistKeyInCsp設置爲false:

public static void AssignNewKey(){ 
    const int PROVIDER_RSA_FULL = 1; 
    const string CONTAINER_NAME = "KeyContainer"; 
    CspParameters cspParams; 
    cspParams = new CspParameters(PROVIDER_RSA_FULL); 
    cspParams.KeyContainerName = CONTAINER_NAME; 
    cspParams.Flags = CspProviderFlags.UseMachineKeyStore; 
    cspParams.ProviderName = "Microsoft Strong Cryptographic Provider"; 
    rsa = new RSACryptoServiceProvider(cspParams); 

    rsa.PersistKeyInCsp = false; 

    string publicPrivateKeyXML = rsa.ToXmlString(true); 
    string publicOnlyKeyXML = rsa.ToXmlString(false); 
    // do stuff with keys... 
} 
+0

正確;我想我對將密鑰存儲在機器上不感興趣。 如果我使用RSACryptoServiceProvider(Int32)構造函數,下面的代碼給了我一個「系統找不到指定的文件」。錯誤。 RSA rsa = new RSACryptoServiceProvider(2048); rsa.ToXmlString(true); – 2009-08-20 19:08:26

+0

因爲我在asp.net中運行這可能是問題嗎? – 2009-08-20 19:23:29

+0

是的,問題可能是因爲「網絡服務」無法在用戶存儲中生成密鑰。 – 2009-08-20 20:18:31

115

你知道,每次你使用這樣的代碼時間:

using (var rsa = new RSACryptoServiceProvider(1024)) 
{ 
    // Do something with the key... 
    // Encrypt, export, etc. 
} 

.NET(其實Windows)中存儲的關鍵在持久性密鑰容器 - 永遠? 並且該容器是由.NET隨機生成的...

結果是:

  1. 你曾經爲了保護數據的目的生成,創建自定義的X.509證書的任何隨機的RSA/DSA密鑰等已在Windows文件系統泄露 。對於有權訪問您的帳戶的所有人聲明。並且您認爲您的數據是安全的...

  2. 您的磁盤緩慢地充滿了數據。通常不是一個大問題,但它取決於您的應用程序(例如,它可能會每分鐘生成數百個密鑰)。

那麼你會怎麼做,以避免這種情況而意外行爲?

using (var rsa = new RSACryptoServiceProvider(1024)) 
{ 
    try 
    { 
     // Do something with the key... 
     // Encrypt, export, etc. 
    } 
    finally 
    { 
     rsa.PersistKeyInCsp = false; 
    } 
} 

ALWAYS

更新:

想看到自己?使用此工具http://www.jensign.com/KeyPal/index.html。我的機器上有幾個

+2

我和OP有完全一樣的需求;公共密鑰進入數據庫,私鑰進入安全存儲在拇指驅動器上。因此,如果我使用了示例代碼,但第一行是'rsa.FromXMLString(pubKey)',那麼生成的密鑰和加載的密鑰都不會持久存儲到商店中? – KeithS 2012-12-19 15:49:20

+0

用CspParameters(){Flags = CspProviderFlags.CreateEphemeralKey}初始化RSACryptoServiceProvider)是否完成同樣的事情? – 2013-02-22 12:17:17

+0

有沒有辦法在創建rsa對象之前設置PersistKeyInCsp? – Nayef 2015-02-03 05:07:40