使用 RSACryptoServiceProvider (C#) 可以 encrypted/decrypted 的各种字符串的 "limit" 是什么?
What is the "limit" to the variety of strings that can be encrypted/decrypted using RSACryptoServiceProvider (C#)?
以下是我自己实现的Rsa加解密方法,我自己参考了微软的文档
public string Encrypt(string plainText)
{
rsaCsp.ImportParameters(publicKey);
byte[] data = Encoding.UTF8.GetBytes(plainText);
byte[] cypher = rsaCsp.Encrypt(data, fOAEP: true);
string cypherText = Convert.ToBase64String(cypher);
return cypherText;
}
public string Decrypt(string cypherText)
{
rsaCsp.ImportParameters(privateKey);
byte[] data = Convert.FromBase64String(cypherText);
byte[] decrypted = rsaCsp.Decrypt(data, fOAEP: true);
string plainText = Encoding.UTF8.GetString(decrypted);
return plainText;
}
我正在注入密钥和提供程序,以便我可以重复使用特定的密钥,这样我什至可以解密在程序的其他执行中加密的字符串。 (示例:我在一个测试中加密并保存在数据库中,在另一个测试中我在数据库中搜索加密的字符串并解密)。如果我允许 public 和私钥自动生成,这种行为就不可能发生。
private readonly RSAParameters privateKey;
private readonly RSAParameters publicKey;
private readonly RSACryptoServiceProvider rsaCsp;
public RsaCryptoService(
RSACryptoServiceProvider rsaCsp,
RSAParameters privateKey,
RSAParameters publicKey)
{
this.rsaCsp = rsaCsp;
this.privateKey = privateKey;
this.publicKey = publicKey;
}
下面是依赖的构建:
public static void InjectRsaCryptoService(this IServiceCollection services, string userName = "default", int keySize = 2048)
{
Services = services;
RsaKeyPair rsaKeyPair = SecuritySettings.RsaKeys.SingleOrDefault(p => p.UserName == userName);
if (rsaKeyPair == null)
throw new lsilvpin_securityException(
$"O usuário {userName} não está autorizado a utilizar as ferramentas de segurança.");
RSAParameters privateKey = rsaKeyPair.PrivateKey.AsParameter();
RSAParameters publicKey = rsaKeyPair.PublicKey.AsParameter();
RSACryptoServiceProvider rsaCsp = new RSACryptoServiceProvider(keySize);
rsaCsp.ImportParameters(publicKey);
rsaCsp.ImportParameters(privateKey);
Services.AddTransient(sp =>
{
IRsaCryptoService rsa = new RsaCryptoService(rsaCsp, privateKey, publicKey);
return rsa;
});
}
起初我认为它工作正常,但为了安全,我决定进行性能测试,发送随机字符串进行加密和解密。
这是我的性能测试:
[Fact]
public void TestRsaCryptionPerformance()
{
for (int i = 0; i < 1000; i++)
{
var plainText = RandomWordGenerator.Next(new RandomWordParameters(WordMaxLength: 495)) + "@eA8";
IRsaCryptoService rsa = Services
.BuildServiceProvider()
.GetRequiredService<IRsaCryptoService>();
string publicKey = rsa.GetPublicKey();
string cypherText = rsa.Encrypt(plainText);
string decrypted = rsa.Decrypt(cypherText);
Assert.True(!string.IsNullOrWhiteSpace(publicKey));
Assert.True(!string.IsNullOrWhiteSpace(cypherText));
Assert.True(!string.IsNullOrWhiteSpace(decrypted));
Assert.True(plainText.Equals(decrypted));
Debug.WriteLine($"PublicKey: {publicKey}");
Debug.WriteLine($"CypherText: {cypherText}");
Debug.WriteLine($"Decrypted: {decrypted}");
}
}
我使用以下方法生成大小在 100 到 499 之间的随机单词:
public static string Next(RandomWordParameters? randomWordParameters = null)
{
randomWordParameters ??= new RandomWordParameters();
if (randomWordParameters.CharLowerBound <= 0 || randomWordParameters.CharUpperBound <= 0)
throw new RandomGeneratorException("Os limites inferior e superior devem ser positivos.");
if (randomWordParameters.CharLowerBound >= randomWordParameters.CharUpperBound)
throw new RandomGeneratorException("O limite inferior deve ser menor do que o limite superior.");
var rd_char = new Random();
var rd_length = new Random();
var wordLength = rd_length.Next(randomWordParameters.WordMinLength, randomWordParameters.WordMaxLength);
var sb = new StringBuilder();
int sourceNumber;
for (int i = 0; i < wordLength; i++)
{
sourceNumber = rd_char.Next(randomWordParameters.CharLowerBound, randomWordParameters.CharUpperBound);
sb.Append(Convert.ToChar(sourceNumber));
}
var word = sb.ToString();
return word;
}
注意:我使用通过将 33 到 126 之间的整数传递给 Convert.ToChar(int) 方法获得的字符。
当我运行一个1000个随机单词的循环时,我总能找到下面抛出异常的一个:
Internal.Cryptography.CryptoThrowHelper.WindowsCryptographicException:'Comprimento inválido.'(无效长度)
被测字符串:a_BdH[(4/6-9m>,9a_J/^t2GCsxo{{W#j*!R![h;TMi/42Yw7Z0yWOb3f15&P:NEI7!vTFm8W.5Q1,d?I9DR>u{M0,$YxP1Cd]MC(gnd$){x[`@L9C7E>z()PS,>w :|?<|j3!KCkC%usV']958^}a2P(SHun'=VR?^qLcj_1nu"UUR|Bu{UlP =mEJ0RIJP#9O$a^g7&I|Q^]_pG0!h} RjiEu_Df8*(@eA8
我想在我正在实施的系统上使用 RSA 加密,但这个测试让我不确定。有人知道这个问题是从哪里来的吗?
注意:encrypt/decrypt 方法可以完美地处理更小、更简单的字符串。 (密码模拟长度可达 20 左右,从弱密码到非常强的密码)
根据 Klaus Gütte 和 Topaco 的评论,我对回复感到满意!
问题是由输入字符串的大小引起的,输入的限制也是根据克劳斯链接的问题输入的大小,这个大小取决于为加密密钥配置的大小Topaco 提到了!
Link 引起澄清:System.Security.Cryptography.CryptographicException : Bad length in RSACryptoserviceProvider
Link 澄清输入大小限制:https://crypto.stackexchange.com/questions/42097/what-is-the-maximum-size-of-the-plaintext-message-for-rsa-oaep/42100# 42100
更具体地说:https://www.rfc-editor.org/rfc/rfc8017#section-7.1.1
打印 RSAES-OAEP-ENCRYPT 标准
在我的特定情况下,encrypt/decrypt 方法接受的字符串的最大长度为 214(长度)。使用 100k 随机字符串进行测试,效果非常好!但是,传递一个字符串大小 >= 215 已经生成“Bad length”异常。
成功,长度 <= 214
215 失败
非常感谢大家的帮助!!
以下是我自己实现的Rsa加解密方法,我自己参考了微软的文档
public string Encrypt(string plainText)
{
rsaCsp.ImportParameters(publicKey);
byte[] data = Encoding.UTF8.GetBytes(plainText);
byte[] cypher = rsaCsp.Encrypt(data, fOAEP: true);
string cypherText = Convert.ToBase64String(cypher);
return cypherText;
}
public string Decrypt(string cypherText)
{
rsaCsp.ImportParameters(privateKey);
byte[] data = Convert.FromBase64String(cypherText);
byte[] decrypted = rsaCsp.Decrypt(data, fOAEP: true);
string plainText = Encoding.UTF8.GetString(decrypted);
return plainText;
}
我正在注入密钥和提供程序,以便我可以重复使用特定的密钥,这样我什至可以解密在程序的其他执行中加密的字符串。 (示例:我在一个测试中加密并保存在数据库中,在另一个测试中我在数据库中搜索加密的字符串并解密)。如果我允许 public 和私钥自动生成,这种行为就不可能发生。
private readonly RSAParameters privateKey;
private readonly RSAParameters publicKey;
private readonly RSACryptoServiceProvider rsaCsp;
public RsaCryptoService(
RSACryptoServiceProvider rsaCsp,
RSAParameters privateKey,
RSAParameters publicKey)
{
this.rsaCsp = rsaCsp;
this.privateKey = privateKey;
this.publicKey = publicKey;
}
下面是依赖的构建:
public static void InjectRsaCryptoService(this IServiceCollection services, string userName = "default", int keySize = 2048)
{
Services = services;
RsaKeyPair rsaKeyPair = SecuritySettings.RsaKeys.SingleOrDefault(p => p.UserName == userName);
if (rsaKeyPair == null)
throw new lsilvpin_securityException(
$"O usuário {userName} não está autorizado a utilizar as ferramentas de segurança.");
RSAParameters privateKey = rsaKeyPair.PrivateKey.AsParameter();
RSAParameters publicKey = rsaKeyPair.PublicKey.AsParameter();
RSACryptoServiceProvider rsaCsp = new RSACryptoServiceProvider(keySize);
rsaCsp.ImportParameters(publicKey);
rsaCsp.ImportParameters(privateKey);
Services.AddTransient(sp =>
{
IRsaCryptoService rsa = new RsaCryptoService(rsaCsp, privateKey, publicKey);
return rsa;
});
}
起初我认为它工作正常,但为了安全,我决定进行性能测试,发送随机字符串进行加密和解密。
这是我的性能测试:
[Fact]
public void TestRsaCryptionPerformance()
{
for (int i = 0; i < 1000; i++)
{
var plainText = RandomWordGenerator.Next(new RandomWordParameters(WordMaxLength: 495)) + "@eA8";
IRsaCryptoService rsa = Services
.BuildServiceProvider()
.GetRequiredService<IRsaCryptoService>();
string publicKey = rsa.GetPublicKey();
string cypherText = rsa.Encrypt(plainText);
string decrypted = rsa.Decrypt(cypherText);
Assert.True(!string.IsNullOrWhiteSpace(publicKey));
Assert.True(!string.IsNullOrWhiteSpace(cypherText));
Assert.True(!string.IsNullOrWhiteSpace(decrypted));
Assert.True(plainText.Equals(decrypted));
Debug.WriteLine($"PublicKey: {publicKey}");
Debug.WriteLine($"CypherText: {cypherText}");
Debug.WriteLine($"Decrypted: {decrypted}");
}
}
我使用以下方法生成大小在 100 到 499 之间的随机单词:
public static string Next(RandomWordParameters? randomWordParameters = null)
{
randomWordParameters ??= new RandomWordParameters();
if (randomWordParameters.CharLowerBound <= 0 || randomWordParameters.CharUpperBound <= 0)
throw new RandomGeneratorException("Os limites inferior e superior devem ser positivos.");
if (randomWordParameters.CharLowerBound >= randomWordParameters.CharUpperBound)
throw new RandomGeneratorException("O limite inferior deve ser menor do que o limite superior.");
var rd_char = new Random();
var rd_length = new Random();
var wordLength = rd_length.Next(randomWordParameters.WordMinLength, randomWordParameters.WordMaxLength);
var sb = new StringBuilder();
int sourceNumber;
for (int i = 0; i < wordLength; i++)
{
sourceNumber = rd_char.Next(randomWordParameters.CharLowerBound, randomWordParameters.CharUpperBound);
sb.Append(Convert.ToChar(sourceNumber));
}
var word = sb.ToString();
return word;
}
注意:我使用通过将 33 到 126 之间的整数传递给 Convert.ToChar(int) 方法获得的字符。
当我运行一个1000个随机单词的循环时,我总能找到下面抛出异常的一个:
Internal.Cryptography.CryptoThrowHelper.WindowsCryptographicException:'Comprimento inválido.'(无效长度)
被测字符串:a_BdH[(4/6-9m>,9a_J/^t2GCsxo{{W#j*!R![h;TMi/42Yw7Z0yWOb3f15&P:NEI7!vTFm8W.5Q1,d?I9DR>u{M0,$YxP1Cd]MC(gnd$){x[`@L9C7E>z()PS,>w :|?<|j3!KCkC%usV']958^}a2P(SHun'=VR?^qLcj_1nu"UUR|Bu{UlP =mEJ0RIJP#9O$a^g7&I|Q^]_pG0!h} RjiEu_Df8*(@eA8
我想在我正在实施的系统上使用 RSA 加密,但这个测试让我不确定。有人知道这个问题是从哪里来的吗?
注意:encrypt/decrypt 方法可以完美地处理更小、更简单的字符串。 (密码模拟长度可达 20 左右,从弱密码到非常强的密码)
根据 Klaus Gütte 和 Topaco 的评论,我对回复感到满意!
问题是由输入字符串的大小引起的,输入的限制也是根据克劳斯链接的问题输入的大小,这个大小取决于为加密密钥配置的大小Topaco 提到了!
Link 引起澄清:System.Security.Cryptography.CryptographicException : Bad length in RSACryptoserviceProvider
Link 澄清输入大小限制:https://crypto.stackexchange.com/questions/42097/what-is-the-maximum-size-of-the-plaintext-message-for-rsa-oaep/42100# 42100
更具体地说:https://www.rfc-editor.org/rfc/rfc8017#section-7.1.1
打印 RSAES-OAEP-ENCRYPT 标准
在我的特定情况下,encrypt/decrypt 方法接受的字符串的最大长度为 214(长度)。使用 100k 随机字符串进行测试,效果非常好!但是,传递一个字符串大小 >= 215 已经生成“Bad length”异常。
成功,长度 <= 214
215 失败
非常感谢大家的帮助!!