如何通过 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 生成唯一的公钥和私钥的主要内容,如果未能解决你的问题,请参考以下文章
RSA公钥和私钥的生成以及PKCS#1与PKCE#8格式的转换