简体   繁体   English

如何通过RSA生成唯一的公钥和私钥

[英]How to Generate Unique Public and Private Key via RSA

I am building a custom shopping cart where CC numbers and Exp date will be stored in a database until processing (then deleted). 我正在构建一个自定义购物车,其中CC号和Exp日期将存储在数据库中,直到处理(然后删除)。 I need to encrypt this data (obviously). 我需要加密这些数据(显然)。

I want to use the RSACryptoServiceProvider class. 我想使用RSACryptoServiceProvider类。

Here is my code to create my keys. 这是我创建密钥的代码。

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...
}

Now the plan is to store the private key xml on a USB drive attached to the managers key chain. 现在计划将私钥xml存储在连接到管理器密钥链的USB驱动器上。

Whenever a manager leaves the company I want to be able to generate new public and private keys (and re-encrypt all currently stored CC numbers with the new public key). 每当经理离开公司时,我希望能够生成新的公钥和私钥(并使用新的公钥重新加密所有当前存储的CC号)。

My problem is that the keys generated by this code are always the same. 我的问题是这段代码生成的密钥总是一样的。 How would I generate a unique set of keys every time? 我每次如何生成一组独特的密钥?

UPDATE. UPDATE。 My test code is below.: 我的测试代码如下:
note: the "privatekey" parameter here is the original private key. 注意:这里的“privatekey”参数是原始私钥。 In order for the keys to be changed I need to verify that the private key is valid. 为了更改密钥,我需要验证私钥是否有效。

In Default.aspx.cs 在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));
}

In Crytpography.cs: 在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);
}

When you use a code like this: 当您使用这样的代码时:

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

.NET (actually Windows) stores your key in a persistent key container forever. .NET(实际上是Windows)将您的密钥永久存储在持久密钥容器中。 The container is randomly generated by .NET 容器由.NET随机生成

This means: 这意味着:

  1. Any random RSA/DSA key you have EVER generated for the purpose of protecting data, creating custom X.509 certificate, etc. may have been exposed without your awareness in the Windows file system. 为了保护数据,创建自定义X.509证书等而生成的任何随机RSA / DSA密钥可能在您不知情的情况下暴露在Windows文件系统中。 Accessible by anyone who has access to your account. 任何有权访问您帐户的人都可以访问。

  2. Your disk is being slowly filled with data. 您的磁盘正在慢慢填充数据。 Normally not a big concern but it depends on your application (eg it might generates hundreds of keys every minute). 通常不是一个大问题,但它取决于您的应用程序(例如,它可能每分钟生成数百个密钥)。

To resolve these issues: 要解决这些问题:

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

ALWAYS 总是

The RSACryptoServiceProvider(CspParameters) constructor creates a keypair which is stored in the keystore on the local machine. RSACryptoServiceProvider(CspParameters)构造函数创建一个密钥对,该密钥对存储在本地计算机上的密钥库中。 If you already have a keypair with the specified name, it uses the existing keypair. 如果您已拥有具有指定名称的密钥对,则它将使用现有密钥对。

It sounds as if you are not interested in having the key stored on the machine. 听起来好像你对将密钥存储在机器上感兴趣。

So use the RSACryptoServiceProvider(Int32) constructor: 所以使用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...
}

EDIT: 编辑:

Alternatively try setting the PersistKeyInCsp to false: 或者尝试将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...
}

What I ended up doing is create a new KeyContainer name based off of the current DateTime (DateTime.Now.Ticks.ToString()) whenever I need to create a new key and save the container name and public key to the database. 我最后要做的是每当我需要创建一个新密钥并将容器名称和公钥保存到数据库时,根据当前的DateTime(DateTime.Now.Ticks.ToString())创建一个新的KeyContainer名称。 Also, whenever I create a new key I would do the following: 此外,每当我创建一个新密钥时,我会执行以下操作:

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.
}

before creating the new key. 在创建新密钥之前。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM