c# - 在 C# 中导入和导出 RSA 密钥
问题描述
我正在尝试导出和重新导入公共和私有 RSA 密钥。我使用 System.Security.Cryptography.RSA 类来做到这一点。但它没有按预期工作。请假设以下代码示例
RSA rsa = RSA.Create();
File.WriteAllBytes("TestPrivateKey", rsa.ExportRSAPrivateKey());
File.WriteAllBytes("TestPublicKey", rsa.ExportRSAPublicKey());
rsa.ImportRSAPrivateKey(File.ReadAllBytes("TestPrivateKey"), out _);
rsa.ImportRSAPublicKey(File.ReadAllBytes("TestPublicKey"), out _);
string textToBeEncrypted = "Hello World";
byte[] bytesToBeEncrypted = Encoding.UTF8.GetBytes(textToBeEncrypted);
byte[] encryptedBytes = rsa.Encrypt(bytesToBeEncrypted, RSAEncryptionPadding.OaepSHA256);
byte[] decryptedBytes = rsa.Decrypt(encryptedBytes, RSAEncryptionPadding.OaepSHA256);
string result = Encoding.UTF8.GetString(decryptedBytes);
当我不使用导出和导入时,加密和解密工作正常。但是当我使用它们时,我得到一个 Internal.Cryptography.CryptoThrowHelper.WindowsCryptographicException 说明在 rsa.Decrypt 函数上找不到密钥。我不是密码学专家,我知道还有其他方法可以实现我正在做的事情(有很多例子),但它们都有些复杂。这在这里看起来很简单。但为什么它不起作用?重新导入后密钥应该不一样吗?
解决方案
RSA.Create()
返回一个封装了私钥的对象。对象的类型取决于平台,例如,在 Windows 10 机器上的 .NET Core 3.1RsaCng
下,会创建一个封装私有 2048 位密钥的实例。在下文中,该对象被表示为RSA 对象。
从这样一个创建的 RSA 对象中,可以以不同的格式导出私有 RSA 密钥。由于私钥也包含公钥数据,所以也可以导出公钥。
同样,可以将私有或公共 RSA 密钥导入 RSA 对象。因此,先前的密钥将被覆盖。这也发生在双重导入的情况下。第二次导入的键会覆盖第一次导入的键 (1)。
为了完整性:导出/导入方法使用 PKCS#1 格式的导出/导入私钥和公钥,DER 编码。它们仅在 .NET Core 中受支持,此处仅从 3.0 版本开始。
此外,.NET 允许使用封装私钥 (2) 的 RSA 对象进行加密。这纯粹是正式的,为了加密,使用了公钥的数据(正如已经说过的,私钥也包含),即在这种情况下它也有效地用公钥加密。
有了这个,可以解释在发布的代码中抛出的异常:
使用RSA.Create()
带有私钥的 RSA 对象被创建。私钥和公钥再次导出和导入,首先是私钥,然后是公钥。双重导入导致私钥被公钥覆盖,因此实际上只导入了公钥(第 1 节)。这允许加密但不允许解密,这会导致相应的异常。
正如评论中已经指出的那样,使用单独的 RSA 对象进行加密和解密是有意义的,这样每个 RSA 对象只需要一个导入。这会自动避免一个导入在错误的时间覆盖另一个。此外,这种实现更加现实,因为加密和解密通常是分开实现的,因此不可避免地使用不同的 RSA 对象。
编辑:关于评论中的场景:
注释中描述的场景也可以解释:
如果导入以相反的顺序进行,即首先是公钥,然后是私钥,则实际上只导入私钥(第 1 节)。这可用于加密(第 2 节)和解密。
如果根本不进行导出/导入,则 RSA 对象会封装初始私钥,该私钥也可用于加密和解密。
所以两者都可以工作并且不会抛出异常。
推荐阅读
- javascript - Rest API 响应负载大小会影响响应时间吗?
- xamarin.forms - FirebaseMessagingService OnNewToken() 在 Android OnCreate() 之前被调用,导致无法在 DeviceInstallationService 上保留 Token
- jquery - DataTables-自动滚动
- python - 导入“不和谐”无法解决
- logstash - 为多个输入配置logstash(端口和日志文件路径)
- haskell - 你怎么能用 Haskell 中的无限列表在一行中重写这个函数呢?
- stripe-payments - 在 Stripe 中将 proration_behavior 设置为 none
- r - 如何根据x轴改变折线图的颜色?
- docker - 容器内 docker 主机的自定义主机名
- pytorch - 将 Apex AMP 与 PyTorch 优化器一起使用会导致属性错误