使用RSACryptoServiceProvider进行公钥加密

18

我已经看了CodeProject上的一篇文章,介绍了如何使用RSA提供程序进行加密和解密:

RSA私钥加密

虽然2009年的旧版本存在缺陷,但新版2012年(带有System.Numerics.BigInteger支持)似乎更可靠。但是这个版本缺少一种使用公共密钥进行加密并使用私人密钥进行解密的方法。

因此,我自己尝试了一下,但在解密时得到了垃圾数据。我不熟悉RSA提供程序,所以我在这里一无所知。很难找到更多关于它应该如何工作的信息。

有人看出问题在哪里了吗? 以下是使用公共密钥进行的加密:

// Add 4 byte padding to the data, and convert to BigInteger struct
BigInteger numData = GetBig( AddPadding( data ) );
RSAParameters rsaParams = rsa.ExportParameters( false );
//BigInteger D = GetBig( rsaParams.D ); //only for private key
BigInteger Exponent = GetBig( rsaParams.Exponent );
BigInteger Modulus = GetBig( rsaParams.Modulus );
BigInteger encData = BigInteger.ModPow( numData, Exponent, Modulus );    
return encData.ToByteArray();

我在执行这个操作时是否需要使用提供者的大写“D”呢?可能不需要,因为这是公钥,没有“D”。

然后是配对(使用私钥进行解密):

BigInteger numEncData = new BigInteger( cipherData );

RSAParameters rsaParams = rsa.ExportParameters( true );
BigInteger D = GetBig( rsaParams.D );
//BigInteger Exponent = GetBig( rsaParams.Exponent );
BigInteger Modulus = GetBig( rsaParams.Modulus );

BigInteger decData = BigInteger.ModPow( numEncData, D, 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;

我需要在这里使用“D”还是指数符号?

显然我需要加密工作双向私钥公钥和公钥私钥。 非常感谢任何帮助!

2个回答

23

使用这个编码/解码示例

        byte[] toEncryptData = Encoding.ASCII.GetBytes("hello world");

        //Generate keys
        RSACryptoServiceProvider rsaGenKeys = new RSACryptoServiceProvider();
        string privateXml = rsaGenKeys.ToXmlString(true);
        string publicXml = rsaGenKeys.ToXmlString(false);

        //Encode with public key
        RSACryptoServiceProvider rsaPublic = new RSACryptoServiceProvider();
        rsaPublic.FromXmlString(publicXml);
        byte[] encryptedRSA = rsaPublic.Encrypt(toEncryptData, false);
        string EncryptedResult = Encoding.Default.GetString(encryptedRSA);


        //Decode with private key
        var rsaPrivate = new RSACryptoServiceProvider();
        rsaPrivate.FromXmlString(privateXml);
        byte[] decryptedRSA = rsaPrivate.Decrypt(encryptedRSA, false);
        string originalResult = Encoding.Default.GetString(decryptedRSA);

2
原始问题要求提供双向解密的示例。您的代码仅展示了如何使用私钥进行解密,而未展示如何使用公钥进行解密。 - Kraang Prime
1
在RSA的情况下,不需要使用公钥进行解密。除非您是指RSA签名。 - Ivan P.
1
不,我的意思是使用公钥解密。例如,在php中,使用openssl_private_encrypt() - 在服务器上保持私钥私有。现在在“客户端”中,需要使用公钥对其进行解密(示例客户端可以是用AuthentiCode编写的c#程序并进行“签名”)。将公共证书嵌入程序中(作为签名过程的一部分)非常方便。现在,想象一下这种情况的另一面--- 大失败。而且滚动哈希需要解析以“验证”数据---很容易被黑客攻击。 - Kraang Prime
我要说的是,虽然这不是你的错,世界上其他人变得疯狂并认为将私钥分发给客户端应用程序是明智的做法——或者更好的做法是,在客户端使用自签名的私钥并将大量公钥存储在服务器上——这些公钥很容易被模仿,并且对于像软件许可证这样需要真正安全性的事情来说是100%无用的。 - Kraang Prime
不太清楚为什么微软在使用加密时会出现解密问题,但幸运的是,使用BigInteger与加密导出参数相结合就可以(相当)轻松地解决这个问题。当然,公共加密和私有解密也有用处,比如你向客户分发的程序需要向服务器发送数据。一般来说,从公/私钥的开始,发送方使用私钥,接收方使用公钥(进行解密)。不确定人们何时或如何混淆了这一点。即使是 thawte 仍然提到这种方式。 - Kraang Prime
显示剩余2条评论

13

这里为您提供一个例子:

    public static void rsaPlayground()
    {
        byte[] data = new byte[] { 1, 2, 3, 4, 5 };
        RSACryptoServiceProvider csp = new RSACryptoServiceProvider();//make a new csp with a new keypair
        var pub_key = csp.ExportParameters(false); // export public key
        var priv_key = csp.ExportParameters(true); // export private key

        var encData = csp.Encrypt(data, false); // encrypt with PKCS#1_V1.5 Padding
        var decBytes = MyRSAImpl.plainDecryptPriv(encData, priv_key); //decrypt with own BigInteger based implementation
        var decData = decBytes.SkipWhile(x => x != 0).Skip(1).ToArray();//strip PKCS#1_V1.5 padding

    }

    public class MyRSAImpl 
    {

        private static byte[] rsaOperation(byte[] data, BigInteger exp, BigInteger mod)
        {
            BigInteger bData = new BigInteger(
                data    //our data block
                .Reverse()  //BigInteger has another byte order
                .Concat(new byte[] { 0 }) // append 0 so we are allways handling positive numbers
                .ToArray() // constructor wants an array
            );
            return 
                BigInteger.ModPow(bData, exp, mod) // the RSA operation itself
                .ToByteArray() //make bytes from BigInteger
                .Reverse() // back to "normal" byte order
                .ToArray(); // return as byte array

            /*
             * 
             * A few words on Padding:
             * 
             * you will want to strip padding after decryption or apply before encryption 
             * 
             */
        }

        public static byte[] plainEncryptPriv(byte[] data, RSAParameters key) 
        {
            MyRSAParams myKey = MyRSAParams.fromRSAParameters(key);
            return rsaOperation(data, myKey.privExponent, myKey.Modulus);
        }
        public static byte[] plainEncryptPub(byte[] data, RSAParameters key)
        {
            MyRSAParams myKey = MyRSAParams.fromRSAParameters(key);
            return rsaOperation(data, myKey.pubExponent, myKey.Modulus);
        }
        public static byte[] plainDecryptPriv(byte[] data, RSAParameters key)
        {
            MyRSAParams myKey = MyRSAParams.fromRSAParameters(key);
            return rsaOperation(data, myKey.privExponent, myKey.Modulus);
        }
        public static byte[] plainDecryptPub(byte[] data, RSAParameters key)
        {
            MyRSAParams myKey = MyRSAParams.fromRSAParameters(key);
            return rsaOperation(data, myKey.pubExponent, myKey.Modulus);
        }

    }

    public class MyRSAParams
    {
        public static MyRSAParams fromRSAParameters(RSAParameters key)
        {
            var ret = new MyRSAParams();
            ret.Modulus = new BigInteger(key.Modulus.Reverse().Concat(new byte[] { 0 }).ToArray());
            ret.privExponent = new BigInteger(key.D.Reverse().Concat(new byte[] { 0 }).ToArray());
            ret.pubExponent = new BigInteger(key.Exponent.Reverse().Concat(new byte[] { 0 }).ToArray());

            return ret;
        }
        public BigInteger Modulus;
        public BigInteger privExponent;
        public BigInteger pubExponent;
    }

1
“D” 元素在公钥中未找到。只有在使用私钥进行解密时才会设置它,因此您的示例将失败,除非将私钥与应用程序一起分发。 - Kraang Prime
嗯...也许事实是,如果你没有私钥,就无法使用它进行解密...是的...那么这与一个仅展示RSA背后数学计算方法的例子有什么关系呢? - DarkSquirrel42
不正确,你只能在解密过程中使用公钥来解密使用私钥加密的数据。无论是使用公钥还是私钥进行解密,上面的示例都需要私钥。请查看您的示例,您就会明白。对于所有的解密方法,您都需要同时拥有公钥和私钥才能使其正常工作,这是错误的。 - Kraang Prime
1
具体来说,MyRSAParams.fromRSAParameters 寻找私钥和公钥指数 - 即 D 值。 - Kraang Prime
2
叹气...所以...你的意思是构造函数不尊重公钥?...自己写一个...这是一个展示如何进行数学计算和读取密钥组件的示例...没有免费的复制粘贴生产代码和保证...随时可以发布你自己的答案... - DarkSquirrel42
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接