关于c#:如何通过RSA生成唯一的公钥和私钥

How to Generate Unique Public and Private Key via RSA

我正在构建一个定制的购物车,在这个购物车中,cc编号和exp日期将存储在数据库中,直到进行处理(然后删除)。我需要加密这些数据(显然)。

我想使用rsacryptoServiceProvider类。

这是我创建密钥的代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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号码)。

我的问题是,这个代码生成的键总是相同的。我如何每次生成一组唯一的键?

更新。我的测试代码如下。:
注意:这里的"private key"参数是原始的私钥。为了更改密钥,我需要验证私钥是否有效。

在default.aspx.cs中

1
2
3
4
5
6
7
8
9
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));
}

在crytopography.cs中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
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);
}


当您使用这样的代码时:

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

.NET(实际上是Windows)将密钥永远存储在持久密钥容器中。容器由.NET随机生成

这意味着:

  • 您为保护数据、创建自定义X.509证书等而生成的任何随机RSA/DSA密钥可能已在Windows文件系统中公开,而您对此一无所知。任何有权访问您帐户的人都可以访问。

  • 您的磁盘正缓慢地充满数据。通常不是一个大问题,但它取决于您的应用程序(例如,它可能每分钟生成数百个密钥)。

  • 要解决这些问题:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    using (var rsa = new RSACryptoServiceProvider(1024))
    {
       try
       {
          // Do something with the key...
          // Encrypt, export, etc.
       }
       finally
       {
          rsa.PersistKeyInCsp = false;
       }
    }

    总是


    RSACryptoServiceProvider(CspParameters)构造函数创建一个密钥对,该密钥对存储在本地计算机上的密钥库中。如果您已经有一个具有指定名称的密钥对,它将使用现有的密钥对。

    听起来你好像对把钥匙放在机器上不感兴趣。

    因此,使用RSACryptoServiceProvider(Int32)构造函数:

    1
    2
    3
    4
    5
    6
    7
    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...
    }

    编辑:

    或者尝试将PersisteKeyNCP设置为false:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    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...
    }


    当我需要创建一个新的密钥并将容器名和公钥保存到数据库时,我最终要做的是基于当前日期时间(datetime.now.ticks.toString())创建一个新的key container名称。此外,每当我创建新的密钥时,我都会执行以下操作:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    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.
    }

    在创建新密钥之前。