如何使用先前使用 RSACryptoServiceProvider 加密的 RSACng 解密数据

Posted

技术标签:

【中文标题】如何使用先前使用 RSACryptoServiceProvider 加密的 RSACng 解密数据【英文标题】:How to decrypt data using RSACng that is previously encrypted with RSACryptoServiceProvider 【发布时间】:2019-04-19 13:00:36 【问题描述】:

我正在从 RSACryptoServiceProvider 迁移到 RSACng 以获取新版本。但是,作为 CAPI 的 RSACryptoserviceProvider 使用 Little Endian 架构,而作为 CNG API 的 RSACng 使用 Big Endian 架构,问题是我如何使用之前使用 RSACryptoService 提供程序 (CAPI) 加密的 CNG Api 解密数据?

我已经尝试过 Array.reverse(cypherText) 并尝试使用 CNG Api 解密,但它抛出错误,“参数不正确”。

    我还尝试了解密一半密码文本的方法,因为 CNG API 使用 RSAEncryptionPadding.OaepSHA512 填充,而 CAPI 使用 OAEP 填充。

我的 RSACryptoServiceProvider 类如下:-

 public static void EncryptWithSystemKeyRSACryptoService(byte[] 
 plainBytes, bool representsUnicodeString, out string cypherText)
 
                CspParameters cp = new CspParameters();
                cp.KeyContainerName = regValue.ToString();
                cp.Flags = CspProviderFlags.UseMachineKeyStore;
                cp.KeyNumber = (int)KeyNumber.Exchange;
                byte[] encBlockData=null;
                using (RSACryptoServiceProvider rsaCSP = new RSACryptoServiceProvider(cp))
                
                    res = CryptResult.GeneralError;
                    int keysize = rsaCSP.KeySize;


                     //This encrypts data and uses FOAEP padding
                    encBlockData = rsaCSP.Encrypt(plainBytes, true);
                
                    //Should i have to reverse the Byte order?
                    // I am doing Array.reverse for encrypted data as it follows little endian architecture and CNG Api follows Big Endian architecture
                    Array.Reverse(encBlockData);
                    cypherText = BitConverter.ToString(encBlockData );
                    cypherText = cypherText.Replace("-", "");
                    cypherText = cypherText.ToLower();
 

这就是我使用 RSACryptoservice Provider (CAPI) 加密数据的方式 我的 RSACng 课程如下:-

           //I am calling this to use RSACng API to get private keys
           private static byte[] SetPrivateAndPublicKeysAndDecrypt(byte[] cyphertext)
           
                cp.KeyContainerName = regValue.ToString();
                cp.Flags = CspProviderFlags.UseMachineKeyStore;
                cp.KeyNumber = (int)KeyNumber.Exchange;

             using (RSACryptoServiceProvider rsaCSP = new 
             RSACryptoServiceProvider(cp))
            
                res = CryptResult.GeneralError;
                keysize = rsaCSP.KeySize;
                q = rsaCSP.ExportCspBlob(false);
                RSAp = rsaCSP.ExportParameters(true);

            
          //created cngKey
            cngKey = CngKey.Import(q, CngKeyBlobFormat.GenericPublicBlob);
             //created RSACng instance
             RSACng rsacng = new RSACng(cngKey)
            
              KeySize = keysize
            ;
              rsacng.ImportParameters(RSAp);
             //Decrypt using RSACng API using OAEPSHA512 padding
               var plainText= crypto.Decrypt(cyphertext, RSAEncryptionPadding.OaepSHA512);
        return plainText;
            

预期结果应该是->明文成功解密

实际结果-> 异常捕获-> '参数不正确'。

【问题讨论】:

CAPI返回数据小端,而RSACryptoServiceProvider返回数据大端,无需反转。但是您的 Decrypt 填充算法必须与用于加密的算法相匹配(OaepSha1) 它确实有效..我使用了 OAEPSha1 填充并且有效。非常感谢 【参考方案1】:

RSA 密文被定义为在PKCS#1 中使用静态大小、无符号、大端编码(它指定了大多数库实现的 PKCS#1 v1.5 RSA 加密和 OAEP 加密)。该函数称为I2OSP within that standard,密文的大小(以完整字节为单位)应与密钥大小相同。如果它不是大端,那么它不符合 RSA / OAEP,换句话说。

对于普通的 ASN.1 编码密钥也是如此:它们根据 DER(可区分编码规则)使用动态大小、签名、大端编码。这些密钥在 PKCS#1、PKCS#8 和 X.509 标准中定义,尽管它们也可能嵌入在与 PKCS#12 兼容的密钥存储中 - 例如。有时,密钥也会被 PEM 编码,以使它们与需要文本而不是二进制的协议兼容。

所以永远不必反转使用标准编码之一的密文或密钥。计算在内部以小端(或不)执行是无关紧要的。几乎所有现代密码或其他密码原语都是如此。输入/输出简单地以具有特定顺序的字节定义,而不是数字。只有非常低级别的功能可能会在例如字,这使问题变得混乱(但您不会在 MS API 中找到它)。

只有微软自己的专有(蹩脚)密钥编码可能使用小端序。


bartonjs 当然是correct in the comments;您需要匹配填充方法,并且用于 OAEP(或者更确切地说,OAEP 中的掩码生成函数 MGF1)的默认哈希是 SHA-1。还有很多其他的陷阱需要避免,例如对明文/密文进行正确的编码/解码。

【讨论】:

非常感谢让我知道填充算法应该是 OAEPSha-1

以上是关于如何使用先前使用 RSACryptoServiceProvider 加密的 RSACng 解密数据的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 RxAlamofire 取消先前的请求?

如何使用先前保存的 coreData 加载 TextEditor

如何使用先前状态更新字符串的状态

如何使用 Promises 链接和共享先前的结果 [重复]

如何使用 MongoDB/Mongoose 中的先前值更新字段 [重复]

如何使用先前堆栈中的名称打印变量参数?