RSACryptoServiceProvider 是否正常工作?
Is RSACryptoServiceProvider working correctly?
我正在使用 .NET 的 RSA 实现,有两件事对我来说很奇怪。我想确认它是否正常运行。
背景
使用 System.Security.Cryptography.RSACryptoServiceProvider
with 2048-bit keyword size to perform asymmetric encryption/decrpytion, initially following the example in this question, "AES 256 Encryption: public and private key how can I generate and use it .net".
作为第一个实现,这似乎可行:
public const int CSPPARAMETERS_FLAG = 1; // Specifies RSA: https://msdn.microsoft.com/en-us/library/ms148034(v=vs.110).aspx
public const bool USE_OAEP_PADDING = false;
public const int KEYWORD_SIZE = 2048;
public static byte[] Encrypt(byte[] publicKey, byte[] dataToEncrypt)
{
var cspParameters = new System.Security.Cryptography.CspParameters(CSPPARAMETERS_FLAG);
byte[] encryptedData = null;
using (var rsaProvider = new System.Security.Cryptography.RSACryptoServiceProvider(cspParameters))
{
try
{
rsaProvider.PersistKeyInCsp = false;
rsaProvider.ImportCspBlob(publicKey);
encryptedData = rsaProvider.Encrypt(dataToEncrypt, USE_OAEP_PADDING);
}
finally
{
rsaProvider.PersistKeyInCsp = false;
rsaProvider.Clear();
}
}
return encryptedData;
}
public static byte[] Decrypt(byte[] privateKey, byte[] dataToDecrypt)
{
var cspParameters = new System.Security.Cryptography.CspParameters(CSPPARAMETERS_FLAG);
byte[] encryptedData = null;
using (var rsaProvider = new System.Security.Cryptography.RSACryptoServiceProvider(cspParameters))
{
try
{
rsaProvider.PersistKeyInCsp = false;
rsaProvider.ImportCspBlob(privateKey);
encryptedData = rsaProvider.Decrypt(dataToDecrypt, USE_OAEP_PADDING);
}
finally
{
rsaProvider.PersistKeyInCsp = false;
rsaProvider.Clear();
}
}
return encryptedData;
}
在进一步研究这些方法之后,我从 the example 生成的 public 密钥似乎在开始时有很多非常可预测的数据,它有 276 个字节长。
显然rsaProvider.ExportCspBlob(bool includePrivateParameters)
is a functional alternative to rsaProvider.ExportParameters(bool includePrivateParameters)
; the main difference is that the blob is already serialized as a byte[]
while the other emits the object version, RSAParameters
。
关于方法的两点观察:
.Exponent
总是0x010001
$=65537$。
- 与序列化类型版本相比,导出的 blob 包含 17 个额外字节。
rsaProvider.ExportCspBlob()
:
- Public 密钥为 276 字节。
- 私钥为1172字节。
RSAParameters
:
- Public 密钥是 259 字节。
.Exponent.Length = 3
.Modulus .Length = 256
- 私钥为1155字节。
.D .Length = 256
.DP .Length = 128
.DQ .Length = 128
.Exponent.Length = 3
.InverseQ.Length = 128
.Modulus .Length = 256
.P .Length = 128
.Q .Length = 128
- 额外的 17 个字节似乎位于二进制 blob 的 header。
担忧
由此,有两点顾虑:
- 指数不是随机的可以吗?
- 如果指数被定义为常量,那么它似乎是我可以去掉序列化的另外 3 个字节?
- 另一个问题,Should RSA public exponent be only in {3, 5, 17, 257 or 65537} due to security considerations?,似乎暗示 $\left{3, 5, 17, 257, 65537\right}$ 都是指数的常用值,所以
0x101
$=65537$ 似乎是合理的,如果总是使用相同的常数指数确实没有害处的话。
- 多出来的17个字节是信息泄露吗?
- 它们是否表示密钥长度和方法等选项参数?
- 当我已经知道发送方和接收方都使用相同的 hard-coded 方法时,传输选项参数信息是个好主意吗?
问题
RSACryptoServiceProvider
的行为是否值得关注,或者这些事情是否正常?
更新 1
在 Should RSA public exponent be only in {3, 5, 17, 257 or 65537} due to security considerations? 中,被接受的答案首先注意到:
There is no known weakness for any short or long public exponent for RSA, as long as the public exponent is "correct" (i.e. relatively prime to p-1 for all primes p which divide the modulus).
如果是这样,那么我猜 0x010001
$=65537$ 的 apparently-constant 指数就足够了,只要它与 $p-1$ 互质即可。因此,大概 RSA 的 .NET 实现会检查这种情况。
但是,如果不满足该条件,RSACryptoServiceProvider
会做什么?如果它选择不同的指数,那么只要指数不是 0x010001
,它似乎就会泄露有关 $p$ 的信息。或者,如果选择了不同的键,那么我们似乎可以假设指数总是 0x010001
并从序列化中省略它。
一切正常,non-alarming。
public 指数 e 很短并且 non-random 是完全可以的。 e = 216+1 = 65537 = 0x010001 是常见且安全的。一些权威机构强制执行(或包含它的某些范围)。使用它(or/and 比 public 模数的位大小大得多的东西)可以针对一些最糟糕的 RSA 填充提供一些保护。
不是,public密钥中多出的17个字节不太可能是信息泄露;它们更可能是您使用的软件为 RSA public 密钥选择的数据格式的 header 部分。我的猜测是您遇到了 answer 中详述的 MS-specific 格式(也许在字节序内),它也恰好使用 276 字节用于具有 2048 位 public模数。在那种情况下,您应该发现额外的字节总是相同的(因此它们显然没有泄漏)。还有无数更微妙的方法可以泄露有关私钥的信息,例如 public 模数本身。
实践中使用的很多RSA密钥生成器,包括我猜的RSACryptoServiceProvider
,首先选择e,然后稍微避免生成素数p 这样 gcd(e, p-1) ≠ 1. 因为 e = 65537 是素数,满足 ( p % e ) ≠ 1 就足够了,这很容易检查,或者由生成 [=20 的过程保证=]p.
我正在使用 .NET 的 RSA 实现,有两件事对我来说很奇怪。我想确认它是否正常运行。
背景
使用 System.Security.Cryptography.RSACryptoServiceProvider
with 2048-bit keyword size to perform asymmetric encryption/decrpytion, initially following the example in this question, "AES 256 Encryption: public and private key how can I generate and use it .net".
作为第一个实现,这似乎可行:
public const int CSPPARAMETERS_FLAG = 1; // Specifies RSA: https://msdn.microsoft.com/en-us/library/ms148034(v=vs.110).aspx
public const bool USE_OAEP_PADDING = false;
public const int KEYWORD_SIZE = 2048;
public static byte[] Encrypt(byte[] publicKey, byte[] dataToEncrypt)
{
var cspParameters = new System.Security.Cryptography.CspParameters(CSPPARAMETERS_FLAG);
byte[] encryptedData = null;
using (var rsaProvider = new System.Security.Cryptography.RSACryptoServiceProvider(cspParameters))
{
try
{
rsaProvider.PersistKeyInCsp = false;
rsaProvider.ImportCspBlob(publicKey);
encryptedData = rsaProvider.Encrypt(dataToEncrypt, USE_OAEP_PADDING);
}
finally
{
rsaProvider.PersistKeyInCsp = false;
rsaProvider.Clear();
}
}
return encryptedData;
}
public static byte[] Decrypt(byte[] privateKey, byte[] dataToDecrypt)
{
var cspParameters = new System.Security.Cryptography.CspParameters(CSPPARAMETERS_FLAG);
byte[] encryptedData = null;
using (var rsaProvider = new System.Security.Cryptography.RSACryptoServiceProvider(cspParameters))
{
try
{
rsaProvider.PersistKeyInCsp = false;
rsaProvider.ImportCspBlob(privateKey);
encryptedData = rsaProvider.Decrypt(dataToDecrypt, USE_OAEP_PADDING);
}
finally
{
rsaProvider.PersistKeyInCsp = false;
rsaProvider.Clear();
}
}
return encryptedData;
}
在进一步研究这些方法之后,我从 the example 生成的 public 密钥似乎在开始时有很多非常可预测的数据,它有 276 个字节长。
显然rsaProvider.ExportCspBlob(bool includePrivateParameters)
is a functional alternative to rsaProvider.ExportParameters(bool includePrivateParameters)
; the main difference is that the blob is already serialized as a byte[]
while the other emits the object version, RSAParameters
。
关于方法的两点观察:
.Exponent
总是0x010001
$=65537$。- 与序列化类型版本相比,导出的 blob 包含 17 个额外字节。
rsaProvider.ExportCspBlob()
:- Public 密钥为 276 字节。
- 私钥为1172字节。
RSAParameters
:- Public 密钥是 259 字节。
.Exponent.Length = 3
.Modulus .Length = 256
- 私钥为1155字节。
.D .Length = 256
.DP .Length = 128
.DQ .Length = 128
.Exponent.Length = 3
.InverseQ.Length = 128
.Modulus .Length = 256
.P .Length = 128
.Q .Length = 128
- Public 密钥是 259 字节。
- 额外的 17 个字节似乎位于二进制 blob 的 header。
担忧
由此,有两点顾虑:
- 指数不是随机的可以吗?
- 如果指数被定义为常量,那么它似乎是我可以去掉序列化的另外 3 个字节?
- 另一个问题,Should RSA public exponent be only in {3, 5, 17, 257 or 65537} due to security considerations?,似乎暗示 $\left{3, 5, 17, 257, 65537\right}$ 都是指数的常用值,所以
0x101
$=65537$ 似乎是合理的,如果总是使用相同的常数指数确实没有害处的话。
- 多出来的17个字节是信息泄露吗?
- 它们是否表示密钥长度和方法等选项参数?
- 当我已经知道发送方和接收方都使用相同的 hard-coded 方法时,传输选项参数信息是个好主意吗?
问题
RSACryptoServiceProvider
的行为是否值得关注,或者这些事情是否正常?
更新 1
在 Should RSA public exponent be only in {3, 5, 17, 257 or 65537} due to security considerations? 中,被接受的答案首先注意到:
There is no known weakness for any short or long public exponent for RSA, as long as the public exponent is "correct" (i.e. relatively prime to p-1 for all primes p which divide the modulus).
如果是这样,那么我猜 0x010001
$=65537$ 的 apparently-constant 指数就足够了,只要它与 $p-1$ 互质即可。因此,大概 RSA 的 .NET 实现会检查这种情况。
但是,如果不满足该条件,RSACryptoServiceProvider
会做什么?如果它选择不同的指数,那么只要指数不是 0x010001
,它似乎就会泄露有关 $p$ 的信息。或者,如果选择了不同的键,那么我们似乎可以假设指数总是 0x010001
并从序列化中省略它。
一切正常,non-alarming。
public 指数 e 很短并且 non-random 是完全可以的。 e = 216+1 = 65537 = 0x010001 是常见且安全的。一些权威机构强制执行(或包含它的某些范围)。使用它(or/and 比 public 模数的位大小大得多的东西)可以针对一些最糟糕的 RSA 填充提供一些保护。
不是,public密钥中多出的17个字节不太可能是信息泄露;它们更可能是您使用的软件为 RSA public 密钥选择的数据格式的 header 部分。我的猜测是您遇到了 answer 中详述的 MS-specific 格式(也许在字节序内),它也恰好使用 276 字节用于具有 2048 位 public模数。在那种情况下,您应该发现额外的字节总是相同的(因此它们显然没有泄漏)。还有无数更微妙的方法可以泄露有关私钥的信息,例如 public 模数本身。
实践中使用的很多RSA密钥生成器,包括我猜的RSACryptoServiceProvider
,首先选择e,然后稍微避免生成素数p 这样 gcd(e, p-1) ≠ 1. 因为 e = 65537 是素数,满足 ( p % e ) ≠ 1 就足够了,这很容易检查,或者由生成 [=20 的过程保证=]p.