当前位置: 首页 > 知识库问答 >
问题:

在C#中通过JS发布解密RSA公钥加密数据

壤驷棋
2023-03-14

在C#中解密RSA加密字符串时,我得到的错误是,要解密的数据超过了256字节模的最大值。

我正在努力实现的目标:

  1. 在C#(RSA)中生成公钥/私钥对
  2. 将私钥保存在会话/临时存储中,以便在解密期间使用
  3. 向客户端/JS发送/返回公钥以用于加密
  4. 用公钥加密JS中的字符串(最多20个字符)并发送到服务器
  5. 使用步骤2中保存的私钥解密服务器中加密的字符串

工作原理:

  1. 公钥/私钥对的生成
  2. 在JS中使用库jscrypt进行加密

目前编写的代码:

C类#

    ///Source: https://stackoverflow.com/questions/17128038/c-sharp-rsa-encryption-decryption-with-transmission
    public static void GenerateKeys()
    {
        //CSP with a new 2048 bit rsa key pair
        var csp = new RSACryptoServiceProvider(2048);

        //Private key
        var privKey = csp.ExportParameters(true);

        //Public key
        var pubKey = csp.ExportParameters(false);

        string privKeyString = string.Empty;

        var sw = new System.IO.StringWriter();
        var xs = new System.Xml.Serialization.XmlSerializer(typeof(RSAParameters));
        xs.Serialize(sw, privKey);
        privKeyString = sw.ToString();

        //This will give public key in the following format which is required by the JS library
        //-----BEGIN PUBLIC KEY-----
        //XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
        //-----END PUBLIC KEY-----
        string publicKeyBase64 = ConvertPublicKeyForJS(pubKey);

        //Will be saved in sesssion for later use during decryption
        string privateKeyBase64 = Convert.ToBase64String(System.Text.Encoding.ASCII.GetBytes(privKeyString));

        //Save in session/temp location
        //Return publicKeyBase64 to JS
    }

    /// <summary>
    /// Source: https://stackoverflow.com/questions/28406888/c-sharp-rsa-public-key-output-not-correct/28407693#28407693
    /// </summary>
    /// <param name="publicKey"></param>
    /// <returns></returns>
    public static string ConvertPublicKeyForJS(RSAParameters publicKey)
    {
        string output = string.Empty;

        using (var stream = new MemoryStream())
        {
            var writer = new BinaryWriter(stream);
            writer.Write((byte)0x30); // SEQUENCE
            using (var innerStream = new MemoryStream())
            {
                var innerWriter = new BinaryWriter(innerStream);
                innerWriter.Write((byte)0x30); // SEQUENCE
                EncodeLength(innerWriter, 13);
                innerWriter.Write((byte)0x06); // OBJECT IDENTIFIER
                var rsaEncryptionOid = new byte[] { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01 };
                EncodeLength(innerWriter, rsaEncryptionOid.Length);
                innerWriter.Write(rsaEncryptionOid);
                innerWriter.Write((byte)0x05); // NULL
                EncodeLength(innerWriter, 0);
                innerWriter.Write((byte)0x03); // BIT STRING
                using (var bitStringStream = new MemoryStream())
                {
                    var bitStringWriter = new BinaryWriter(bitStringStream);
                    bitStringWriter.Write((byte)0x00); // # of unused bits
                    bitStringWriter.Write((byte)0x30); // SEQUENCE
                    using (var paramsStream = new MemoryStream())
                    {
                        var paramsWriter = new BinaryWriter(paramsStream);
                        EncodeIntegerBigEndian(paramsWriter, publicKey.Modulus); // Modulus
                        EncodeIntegerBigEndian(paramsWriter, publicKey.Exponent); // Exponent
                        var paramsLength = (int)paramsStream.Length;
                        EncodeLength(bitStringWriter, paramsLength);
                        bitStringWriter.Write(paramsStream.GetBuffer(), 0, paramsLength);
                    }
                    var bitStringLength = (int)bitStringStream.Length;
                    EncodeLength(innerWriter, bitStringLength);
                    innerWriter.Write(bitStringStream.GetBuffer(), 0, bitStringLength);
                }
                var length = (int)innerStream.Length;
                EncodeLength(writer, length);
                writer.Write(innerStream.GetBuffer(), 0, length);
            }

            var base64 = Convert.ToBase64String(stream.GetBuffer(), 0, (int)stream.Length);

            StringBuilder sb = new StringBuilder();
            sb.AppendLine("-----BEGIN PUBLIC KEY-----");
            sb.AppendLine(base64);
            sb.AppendLine("-----END PUBLIC KEY-----");

            output = sb.ToString();
        }

        return output;
    }

    private static void EncodeLength(BinaryWriter stream, int length)
    {
        if (length < 0) throw new ArgumentOutOfRangeException("length", "Length must be non-negative");
        if (length < 0x80)
        {
            // Short form
            stream.Write((byte)length);
        }
        else
        {
            // Long form
            var temp = length;
            var bytesRequired = 0;
            while (temp > 0)
            {
                temp >>= 8;
                bytesRequired++;
            }
            stream.Write((byte)(bytesRequired | 0x80));
            for (var i = bytesRequired - 1; i >= 0; i--)
            {
                stream.Write((byte)(length >> (8 * i) & 0xff));
            }
        }
    }

    private static void EncodeIntegerBigEndian(BinaryWriter stream, byte[] value, bool forceUnsigned = true)
    {
        stream.Write((byte)0x02); // INTEGER
        var prefixZeros = 0;
        for (var i = 0; i < value.Length; i++)
        {
            if (value[i] != 0) break;
            prefixZeros++;
        }
        if (value.Length - prefixZeros == 0)
        {
            EncodeLength(stream, 1);
            stream.Write((byte)0);
        }
        else
        {
            if (forceUnsigned && value[prefixZeros] > 0x7f)
            {
                // Add a prefix zero to force unsigned if the MSB is 1
                EncodeLength(stream, value.Length - prefixZeros + 1);
                stream.Write((byte)0);
            }
            else
            {
                EncodeLength(stream, value.Length - prefixZeros);
            }
            for (var i = prefixZeros; i < value.Length; i++)
            {
                stream.Write(value[i]);
            }
        }
    }

    public static string DecryptValue(string cypherText)
    {
        string plainTextData = string.Empty;

        string privKeyBase64 = ""; //get value from session/temp storage

        string privKeyString = System.Text.Encoding.ASCII.GetString(Convert.FromBase64String(privKeyBase64));

        var sr = new System.IO.StringReader(privKeyString);
        var xs = new System.Xml.Serialization.XmlSerializer(typeof(RSAParameters));
        var privKey = (RSAParameters)xs.Deserialize(sr);

        var csp = new RSACryptoServiceProvider();
        csp.ImportParameters(privKey);

        var bytesCypherText = Convert.FromBase64String(cypherText);

        //Problematic line
        var bytesPlainTextData = csp.Decrypt(bytesCypherText, false);

        plainTextData = System.Text.Encoding.Unicode.GetString(bytesPlainTextData);

        return plainTextData;
    }

JS

   function encrypt(msg, publicKey) {
        var jsEncrypt = new JSEncrypt();
        jsEncrypt.setPublicKey(publicKey);
        var encrypted = jsEncrypt.encrypt(msg);

        var base64result = btoa(encrypted);

        return base64result;            
    }

更新/变更:

如果我将密钥大小设为1024而不是2048,那么我得到的错误是,要解密的数据超过了128字节模的最大值 。根据我在调试器中看到的内容,加密字符串的总字节数为172。

更新|解决方案:

  1. 删除JS中的这一行,var base64result=btoa(加密) 。直接返回加密值即可

笔记:

  • 请忽略不正确的方法返回类型,这仍然是一个POC,许多值正在手动填充
  • 我不是安全方面的专家,所以很多代码都是从不同的来源中挑选出来的

共有1个答案

宇文俊明
2023-03-14

您面临非对称加密的限制。对于大块数据来说,它非常慢,加密字符串大小受您使用的RSA密钥大小的限制。

RSA通常用于交换对称密钥和处理大部分数据。如果您必须对大量数据使用非对称,那么您需要将有效负载分解为较小的负载并在另一边重建。

RSA只能将数据加密到密钥大小(2048位=256字节)减去填充/标头数据(PKCS#1 v1.5 padding为11字节)的最大值。

如果如您所说,您只发送了20个字符,那么请确实使用断点进行检查,以确保您的解密函数得到的密码文本足够小。如果没有,您需要回溯并检查发送错误内容的位置。

也可能是因为RSA在JSEncrypt中的标准与RSA在C中的标准不同,这可以在这里的SO答案中看到

 类似资料:
  • 我们有一个签名服务,它接受sha256哈希作为输入,并使用pkcs11和C#中的bouncy Castle库对哈希进行签名,将签名摘要编码为Bae64并将其发送给请求者。因此,本质上我们正在生成哈希的哈希并对哈希进行签名。 为了验证这一点,另一端的请求者解码Base64接收到的摘要并验证它。我在PowerShell中使用了一个. NET库,并实现了一个验证过程。请参阅下面。 现在,为了避免散列,我

  • 我找到了几个可以使用的解决方案。Net RSA Provider使用公钥对消息进行加密,并使用私钥对其解密。 但我想要的是用私钥加密,用公钥解密。 我希望在我的应用程序中存储公钥,并使用私钥加密许可证,例如在我的开发人员计算机上,将其发送到应用程序,并让信息使用公钥解密。 我怎样才能做到这一点?

  • 问题内容: 我正在编写一个用于传输文件的小型应用程序,或多或少地将其作为一种学习更多编程加密基础的方法。这个想法是生成一个RSA密钥对,交换公共密钥,并发送AES iv和密钥以进一步解密。我想用接收者的RSA公钥加密AES密钥,如下所示: 然后,我将密钥值写给接收器,并按如下方式解密: 在控制台的另一端,我将其作为输出: 此外,如果我创建一个大小为16的字节数组,并将cipher.doFinal(

  • 我想使用带有RSA算法的OpenSSL使用私钥加密文件: 现在,如果我执行解密操作: 此操作需要私钥 我知道我应该使用公钥进行加密,如果我使用私钥,我会得到一个签名。 然而,我想这样做是为了学习。

  • 说明 微信支付-获取RSA加密公钥SDK,企业付款到银行卡接口需要。 你还需要执行openssl rsa -RSAPublicKey_in -in weixin-rsa-public.pem -pubout 将命令行输出的证书内容覆盖到weixin-rsa-public.pem文件中才可使用 官方文档:https://pay.weixin.qq.com/wiki/doc/api/tools/mch

  • 我需要在C#中加密数据,以便将其传递给Java。Java代码属于第三方,但我得到了相关的源代码,因此我决定,由于Java使用Bouncy Castle库,所以我将使用C#端口。 解密工作正常。但是,解密仅在使用私钥使用encrypt时有效,而不是使用公钥。使用公钥时,解密失败,出现。 编辑: 我还添加了一个单元测试,它证明公钥等于从私钥中提取的公钥: