首页 文章

使用RSA公钥解密使用RSA私钥加密的字符串

提问于
浏览
2

这是一个未回答的问题的副本:Using an RSA Public Key to decrypt a string that was encrypted using RSA Private Key

您可以看到作者使用以下代码找到了解决方案:http://www.codeproject.com/KB/security/PrivateEncryption.aspx

使用该链接中的代码看起来很有希望 . 唯一缺少的是填充 . 我通常使用PKCS1.5填充,这是OpenSSL RSA的默认值 .

我知道这个问题的答案非常接近 . 我知道唯一阻止解密的是加密的openssl密文上的pkcs1.5填充 .

我很惊讶地看到关于这个主题的信息很少,因为在很多情况下你需要服务器来加密某些东西,签名等等,并让客户端应用程序使用公钥进行验证,解密等 .

我还广泛尝试使用RSACryptoServiceProvider来验证使用OpenSSL加密产生的哈希 . 例如,我会使用明文的SHA256哈希进行私钥加密,然后尝试对该签名进行RSACryptoServiceProvider验证,但它不起作用 . 我认为MS这样做的方式是非标准的,也许有特殊的定制工作 .

因此,另一个选择是这个问题,它只是采用私钥加密密文并使用C#对其进行解密,从而验证它的真实性 . 可以合并散列,为服务器签名并在客户端验证的数据对象 Build 简单的签名验证系统 .

我查看了PKCS1 RFC,OpenSSL rsa源代码和其他项目,我无法得到关于如何在进行RSA Decrypt时考虑PKCS1填充的可靠答案 . 我无法找到他们处理PKCS1填充的OpenSSL源代码中的哪个位置,否则,我现在可能会得到一个答案 .

另外,这是我的第一个问题,我知道这是一个未答复的问题的副本,那么,该怎么办?我也用谷歌搜索,一无所获 .

另一件我不明白的是为什么我的解密方法不起作用 . 由于填充在解密后被删除,我的解密数据应该类似于明文,并且它甚至都不接近 . 因此,我几乎可以肯定pkcs1填充意味着其他事情正在发生,特别是密文,这意味着密文必须在解密之前进行预处理以删除填充元素 .

也许简单地过滤密文来删除填充元素是最简单的解决方案......

这是我的Decrypt方法:

public static byte[] PublicDecryption(this RSACryptoServiceProvider rsa, byte[] cipherData)
    {
        if (cipherData == null)
            throw new ArgumentNullException("cipherData");

        BigInteger numEncData = new BigInteger(cipherData);

        RSAParameters rsaParams = rsa.ExportParameters(false);
        BigInteger Exponent = GetBig(rsaParams.Exponent);
        BigInteger Modulus = GetBig(rsaParams.Modulus);

        BigInteger decData = BigInteger.ModPow(numEncData, Exponent, Modulus);

        byte[] data = decData.ToByteArray();
        byte[] result = new byte[data.Length - 1];
        Array.Copy(data, result, result.Length);
        result = RemovePadding(result);
        Array.Reverse(result);
        return result;
    }

    private static byte[] RemovePadding(byte[] data)
    {
        byte[] results = new byte[data.Length - 4];
        Array.Copy(data, results, results.Length);
        return results;
    }

2 回答

  • 1

    问题不在于填充 . 实际上,从解密的密文中删除填充值实际上非常简单 . 问题在于此位置的软件:您可以看到作者使用此处的一些代码找到了解决方案:http://www.codeproject.com/KB/security/PrivateEncryption.aspx

    并且使用Microsoft的System.Numeric实现,它根本无法处理更大的整数...

    为了解决这个问题,我查看了codeproject站点上以前的代码版本,最后得到了这个PublicDecrypt方法 .

    public static byte[] PublicDecryption(this RSACryptoServiceProvider rsa, byte[] cipherData)
        {
            if (cipherData == null)
                throw new ArgumentNullException("cipherData");
    
            BigInteger numEncData = new BigInteger(cipherData);
            RSAParameters rsaParams = rsa.ExportParameters(false);
            BigInteger Exponent = new BigInteger(rsaParams.Exponent);
            BigInteger Modulus = new BigInteger(rsaParams.Modulus);
    
            BigInteger decData2 = numEncData.modPow(Exponent, Modulus);
            byte[] data = decData2.getBytes();
            bool first = false;
            List<byte> bl = new List<byte>();
            for (int i = 0; i < data.Length; ++i)
            {
                if (!first && data[i] == 0x00)
                {
                    first = true;
                }
                else if (first)
                {
                    if (data[i] == 0x00)
                    {
                        return bl.ToArray();
                    }
                    bl.Add(data[i]);
                }
            }
            if (bl.Count > 0)
                return bl.ToArray();
            return new byte[0];
        }
    

    这将使用rsautl实用程序或Perl Crypt :: OpenSSL :: RSA private_encrypt方法完美地解密openssl创建的密文 .

    另一个重大变化是放弃了微软的BitInteger库,它根本无效 . 我最终使用了代码项目文章中提到的那个,并在此处找到:http://www.codeproject.com/Articles/2728/C-BigInteger-Class

    这里的关键是将库中的maxintsize设置为一个值,该值根据您使用的密钥大小有多大 . 对于4096位,值500工作正常(大约模数的长度) .

    这是调用方法:

    var encmsg3 = "JIA7qtOrbBthptILxnurAeiQM3JzSoi5WiPCpZrIIqURKfVQMN1BrondF9kyNzbjTs1DaEKEuMBVwKExZe22yCvXXpm8pwcEGc9EHcVK2MPqNo89tIF8LJcaDqBMwLvxdaa/QgebtpmOVN/TIWfuiV8KR+Wn07KwsgV+3SALbNgbOIR3RtCx3IiQ3tZzybJb08+ZKwJEOT011uwvvGmtJskQgq9PC8kr1RPXMgaP6xMX7PHFJ8ORhkuWOFfCh+R4NhY1cItVhnRewKpIC2qVlpzUYRAgKIKdCXuZDqUQdIponR29eTovvLb0DvKQCLTf9WI1SzUm6pKRn0vLsQL7L3UYHWl43ISrTpDdp+3oclhgRF3uITR4WCvoljephbGc6Gelk5z3Vi6lN0oQaazJ7zIen+a/Ts7ZX3KKlwPl4/lAFRjdjoqu7u4IAK7O7u1Jf2xDiGw18C/eGt8UHl09zU4qQf9/u+7gtJ+10z2NERlLSaCDjVqslwmmxu81pG2gCv8LfpR4JlPaFfBZMGfGBihyGryWhJwizUXXo8wgdoYbHRXe8/gL19qro0ea5pA9aAhDjTpX1Zzbwu2rUU7j6wtwQtUDJOGXXCw1VOHsx6WXeW196RkqG72ucVIaSAlx5TFJv8cnj6werEx1Ung5456gth3gj19zHc8E/Mwcpsk=";
                byte[] enc = Convert.FromBase64String(encmsg3);
                var dec = rsa2.PublicDecryption(enc);
                Debug.Print("PLAINTEXT: " + Encoding.UTF8.GetString(dec));
    

    有人需要完全复制它的唯一最后一件事就是将私钥变为openssl格式,以便它们可以在openssl和C#之间来回传递私钥和公钥 .

    我使用openssl.net,创建了一个RSA实例,并使用bignumbers设置所有变量 . 这是代码:

    RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
            rsa.FromXmlString(Properties.Resources.RSAParameters);
    
            RSAParameters par = rsa.ExportParameters(true); // export the private key
    
    
            using (OpenSSL.Crypto.RSA rsaos = new OpenSSL.Crypto.RSA())
            using (BigNumber bnmod = BigNumber.FromArray(par.Modulus))
            using (BigNumber bnexp = BigNumber.FromArray(par.Exponent))
            using (BigNumber bnD = BigNumber.FromArray(par.D))
            using (BigNumber bnP = BigNumber.FromArray(par.P))
            using (BigNumber bnQ = BigNumber.FromArray(par.Q))
            using (BigNumber bnDmodP = BigNumber.FromArray(par.DP))
            using (BigNumber bnDmodQ = BigNumber.FromArray(par.DQ))
            using (BigNumber bnInverse = BigNumber.FromArray(par.InverseQ))
            {
                rsaos.PublicExponent = bnexp;
                rsaos.PublicModulus = bnmod;
    
                rsaos.IQmodP = bnInverse;
                rsaos.DmodP1 = bnDmodP;
                rsaos.DmodQ1 = bnDmodQ;
                rsaos.SecretPrimeFactorP = bnP;
                rsaos.SecretPrimeFactorQ = bnQ;
                rsaos.PrivateExponent = bnD;
                string privatekey = rsaos.PrivateKeyAsPEM;   
                string publickey = rsaos.PublicKeyAsPEM
            }
    

    有了它,您可以轻松创建RSA密钥,将所有内容导出到OpenSSL,并在合理范围内加密/解密您想要的任何内容 . 处理私钥加密,然后进行公钥解密就足够了 .

    凉 .

  • -1

    PublicDecryption函数中的行存在问题:BigInteger numEncData = new BigInteger(cipherData);

    它应该是:BigInteger numEncData = GetBig(cipherData);

    该行也将被删除:Array.Reverse(result);

    您可能会遇到一些填充问题,但如果您能够正确获取数据,则应该很容易纠正 .

相关问题