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

Posted

技术标签:

【中文标题】如何通过 RSA 生成唯一的公钥和私钥【英文标题】:How to Generate Unique Public and Private Key via RSA 【发布时间】:2010-11-21 09:19:08 【问题描述】:

我正在构建一个自定义购物车,其中 CC 编号和 Exp 日期将存储在数据库中,直到处理(然后删除)。我需要加密这些数据(显然)。

我想使用 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 号码)。

我的问题是这段代码生成的密钥总是相同的。我如何每次都生成一组唯一的密钥?

更新。我的测试代码如下:注意:这里的“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);

【问题讨论】:

我基本上是从 .net 页面调用 AssignNewKey() 函数,然后对照我以前的版本检查新的“publicPrivateKeyXML”。我将更新上面的问题以包含我的测试代码。 这有点切题,但是您是否意识到为了存储信用卡号码,您的系统需要符合 PCI 标准?见***.com/questions/4300863/… 是的,这实际上是引发这个问题的原因。尽管我们最终使用了外部支付提供商。 【参考方案1】:

当你使用这样的代码时:

using (var rsa = new RSACryptoServiceProvider(1024))

   // Do something with the key...
   // Encrypt, export, etc.

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

这意味着:

    您曾经为保护数据、创建自定义 X.509 证书等而生成的任何随机 RSA/DSA 密钥都可能在您不知情的情况下暴露在 Windows 文件系统中。有权访问您帐户的任何人都可以访问。

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

要解决这些问题:

using (var rsa = new RSACryptoServiceProvider(1024))

   try
   
      // Do something with the key...
      // Encrypt, export, etc.
   
   finally
   
      rsa.PersistKeyInCsp = false;
   

总是

【讨论】:

我的需求和OP一模一样;公钥进入数据库,私钥进入拇指驱动器上的安全存储。所以,如果我使用了你的示例代码,但第一行是rsa.FromXMLString(pubKey),那么生成的密钥和加载的密钥都不会持久化到存储区? 使用 CspParameters() Flags = CspProviderFlags.CreateEphemeralKey ) 初始化 RSACryptoServiceProvider 是否完成同样的事情? 有没有办法在创建 rsa 对象之前设置 PersistKeyInCsp? 对我来说,这是错误的。 .Net 4.6.x using (RSACryptoServiceProvider myRSA = new RSACryptoServiceProvider(2048)) var x = myRSA.PersistKeyInCsp; ... x = false; 根据 msdn:“在 KeyContainerName 字段中指定密钥容器名称时,PersistKeyInCsp 属性会自动设置为 true...”msdn.microsoft.com/en-us/library/…【参考方案2】:

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

【讨论】:

正确;我想我对将密钥存储在机器上不感兴趣。如果我使用 RSACryptoServiceProvider(Int32) 构造函数,下面的代码会给我一个“系统找不到指定的文件”。错误。 RSA rsa = 新的 RSACryptoServiceProvider(2048); rsa.ToXmlString(true); 既然我在 asp.net 中运行它,这可能是问题吗? 是的,问题可能是因为“网络服务”无法在用户存储中生成密钥。 我想要的只是一个公钥(存储在服务器上)、一个私钥(与管理器一起存储),以及使用这些密钥安全地加密和解密某些数据的方法。默认情况下,我不希望在机器上存储任何内容。为什么这么难?你有什么替代方案可以推荐吗? 您看到我所做的编辑了吗?尝试将 PersistKeyInCsp 设置为 false。【参考方案3】:

当我需要创建新密钥并将容器名称和公钥保存到数据库时,我最终会根据当前的 DateTime (DateTime.Now.Ticks.ToString()) 创建一个新的 KeyContainer 名称.此外,每当我创建一个新密钥时,我都会执行以下操作:

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.

在创建新密钥之前。

【讨论】:

如果您发布完整的解决方案会很好,因为我无法弄清楚 cmets 中正在做什么 我是否正确阅读了您的代码?您有一个名为“privateKey”的变量,但您的 cmets 建议您创建一个新的公钥? 我写这篇文章已经快 7 年了……但我认为AssignNewKey 方法旨在创建一个新的public private 键,将 public 键存储在数据库中,同时将 private 键作为 XML 字符串返回。

以上是关于如何通过 RSA 生成唯一的公钥和私钥的主要内容,如果未能解决你的问题,请参考以下文章

如何使用openssl生成RSA公钥和私钥对 / 蓝讯

RSA公钥和私钥的生成以及PKCS#1与PKCE#8格式的转换

OpenSSL生成公私钥

通过 OpenSSL (c++) 以 XML (w3c) 格式保存 RSA 公钥和私钥

java中RSA用私钥加密公钥解密问题

公钥和私钥怎么接独立站