各位达人,小弟最近在做项目时候遇到一个关于RSA加密解密的问题。大家都知道.NET库提供的RSACryptoServiceProvider类只支持公钥加密,私钥解密,而现在我遇到的事情是需要私钥加密公钥解密。而我所做的只是公钥解密(公私钥对是java生成的,只提供给我公钥)。针对这个问题,我也在网上找了一些例子,但是还是没有实现。我在网上找到了一个C#写的BigInteger类,并且按照               /// <summary>
        /// 通过公钥解密
        /// </summary>
        /// <param name="dataBytes">待解密字符数组</param>
        /// <returns>解密结果</returns>
        public string DecryptByPublicKey(byte[] dataBytes, byte[] keyN, byte[] keyE)
        {
            //大整数N
            BigInteger biN = new BigInteger(keyN);
            //公钥大素数
            BigInteger biE = new BigInteger(keyE);
            //解密
            return DecryptBytes(dataBytes, biE, biN);
        }             /// <summary>
        /// 解密字符数组
        /// </summary>
        /// <param name="dataBytes">待解密字符数组</param>
        /// <param name="KeyNum">密钥大素数</param>
        /// <param name="nNum">大整数N</param>
        /// <returns>解密结果</returns>
        private string DecryptBytes(byte[] dataBytes, BigInteger KeyNum, BigInteger nNum)
        {
            int len = dataBytes.Length;
            int len1 = 0;
            int blockLen = 0;
            if (len % 128 == 0)
            {
                len1 = len / 128;
            }
            else
            {
                len1 = len / 128 + 1;
            }
            List<byte> tempbytes = new List<byte>();
            for (int i = 0; i < len1; i++)
            {
                if (len >= 128)
                {
                    blockLen = 128;
                }
                else
                {
                    blockLen = len;
                }
                byte[] oText = new byte[blockLen];
                Array.Copy(dataBytes, i * 128, oText, 0, blockLen);
                BigInteger biText = new BigInteger(oText);
                BigInteger biEnText = biText.modPow(KeyNum, nNum);
                byte[] testbyte= biEnText.getBytes();
                string str = Encoding.UTF8.GetString(testbyte);
                tempbytes.AddRange(testbyte);
                len -= blockLen;
            }
            return System.Text.Encoding.UTF8.GetString(tempbytes.ToArray());
        }我调用的方法是:
         private void DecryptByPublicKeyTest()
        {
            //key
            byte[] key = Encoding.UTF8.GetBytes("12345");
            //用私钥加密key
            RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
            byte[] desc = rsa.Encrypt(key, false);
            string getsource = Encoding.UTF8.GetString(rsa.Decrypt(desc, false));
            RSAParameters para = rsa.ExportParameters(false);            string gets = DecryptByPublicKey(desc, para.Modulus, para.Exponent);
        }但是解密出来的跟原来的“12345”不一样。

解决方案 »

  1.   

    嗯,我看了看msdn,可以用SignData/VerifyData来签名和验证,应该够用了
      

  2.   

    可能我没说明白,对方是用java写的RSA加密的,他是用私钥加密,告诉我公钥,然后让我解密。
    例如:
    1、程序计算出本地key后传给经销商。
    2、经销商算出公私钥对,用RSA私钥加密收到的key。
    3、经销商将加密后的key和公钥发放给使用者
    4、使用者在软件中进行认证。我要做的就是认证。加密后的key和公钥是知道的。我要把加密后的key用给我的公钥解密。
      

  3.   

    我明白,就是用rsa来加密并传递一个随机key,并用这个key来进行软件认证。对吧。
    但是这个过程其实是没有意义的。
    经销商的私钥是他自己保留的,公钥是可以发布给所有人的,用私钥加密后的内容,理论上说,可以被网上所有的人所阅读的(任何人得到加密后的文本后,只要找到公钥,就可以解密)所以这个加密本身是没有意义的。而唯一能确定的是,这段密文,肯定是经销商用他的私钥加密的,是这个经销商发出的,他人是无法仿冒的,这就叫签名。
    而正真的加密,其实是要用接受方的公钥加密,然后传给接受方,这样就只有接受方可以解密并阅读,这才是正真的加密办法。
    具体的加密流程如下:
    生成随机key
    对称加密(明文,随机key)->密文
    rsa加密(随机key,接受方公钥)->加密的key
    密文+加密的key传输给接受方
    rsa解密(加密的key,接受方私钥)->随机key
    对称解密(密文,随机key)->明文
    而签名过程就不一样:
    rsa签名(明文,发出方私钥)->签名串
    明文+签名串传输给接受方
    rsa验证签名(明文,发出方公钥,签名串)->true/false
    注:签名的时候,会先对明文进行hash运算,这样可以减少加密的量
      

  4.   

    所以对lz的需求,你只要要求经销商把key和加密后的内容一起发给你,就可以用对方的公钥来验证key是否是从经销商那里发出来的
      

  5.   

    估计是key的数据结构有问题,msdn里面应该有这个说明吧
      

  6.   

    去找第三方,以前做过不过源码被偶丢失了。关键是C#没有实现标准ASN1.1协议。 或者你叫Java方提供给你Key的Modulus & q 等值,你直接Load key进去也是可以的。再或者直接用C++/java之类的调用 
      

  7.   

    http://support.microsoft.com/kb/246071/zh-cn
    应该说是非对称加密。
    1976年,美国学者Dime和Henman为解决信息公开传送和密钥管理问题,提出一种新的密钥交换协议,允许在不安全的媒体上的通讯双方交换信息,安全地达成一致的密钥,这就是“公开密钥系统”。相对于“对称加密算法”这种方法也叫做“非对称加密算法”。   
       
    与对称加密算法不同,非对称加密算法需要两个密钥:公开密钥(publickey)和私有密钥(privatekey)。公开密钥与私有密钥是一对,如果用公开密钥对数据进行加密,只有用对应的私有密钥才能解密;如果用私有密钥对数据进行加密,那么只有用对应的公开密钥才能解密。因为加密和解密使用的是两个不同的密钥,所以这种算法叫作非对称加密算法。  这种基础知识LZ你有空要去补。
      

  8.   

    具体步骤如下:
    1)、生成公钥和私钥
    公钥对外公开,随UI工具一同发布,私钥用于License管理工具的生成License的签名使用。 
    2)UI工具注册界面能够自动生成用于生成license的key,key的获得方法是获取主机第一块硬盘序列号,并把这个序列号发送给DMX
    3)使用RSA算法,用私钥对客户发送过来的序列号进行加密,生成密文,并对密文进行Base64编码,生成License文件中的签名内容,传递给客户
    4)在UI工具的注册页面,输入license签名内容,进行注册;
    5)UI工具运行,首先读取license签名内容,Base64解码,用公钥解密,把解密的内容和系统的第一个硬盘序列号比对,一致则通过,否则进入注册页面,等待注册。就是这样的过程,这是原需求文档。
      

  9.   

    改下标题啊,我查了下,叫“用RSA实现私钥签名,公钥验证”更合理
      

  10.   

    看看msdn的demo
    http://msdn.microsoft.com/zh-cn/library/9tsc5d0z.aspx
      

  11.   

    这就没错了,RSACryptoServiceProvider.VerirySign需要3个参数,一是明文,用硬盘序列号,2是签名内容,用base64解码后的字串,3是加密方法,选择一个合适的就可以了
      

  12.   

    up
    纯java去实现比较容易,拿java的私钥在.net里面签名是个难点。看那位高人出来指点一下。关注~~~
      

  13.   

    总算在垃圾里面找到了以前的代码
    一定要用.net 实现的话,推荐 http://www.bouncycastle.org/
    我是用VS2005 在 2007年实现的,版本可能老了点。希望对你有帮助using System;
    using System.IO;
    using System.Security.Cryptography;
    using Org.BouncyCastle.Asn1.Pkcs;
    using Org.BouncyCastle.Asn1.X509;
    using Org.BouncyCastle.Crypto;
    using Org.BouncyCastle.Crypto.Agreement;
    using Org.BouncyCastle.Crypto.Parameters;
    using Org.BouncyCastle.Math;
    using Org.BouncyCastle.Math.EC;
    using Org.BouncyCastle.Pkcs;
    using Org.BouncyCastle.Security;
    using Org.BouncyCastle.Utilities.Encoders;
    using Org.BouncyCastle.X509;namespace RSAKeyTest
    {
        public class KeyRSA
        {
             public static void Main(string[] args)
            {            try
                {
                    byte[] b1 = System.Convert.FromBase64String("AAAAgK3VW9wGpQybzYMQaJ7GX3lSHr9+mvNDmx4883Sv3L8Wa/s+GAFjXru6yvX2hHPgY9HX3rpNhfVo7PC9dY1gGyAytMuYGLt6rRAptJqzygZL3GeL//abfyYgnRUFSDmEznhnM1pQfSko9TH9XKKCpNYeYSDTFpJw19R/65tR7obG2Hlc5XdhwtWe24n9jl5AsP3Klp/FY/z0sq9w9Boz6r4=");
                    byte[] b2 = new byte[128];
                    for (int i = 0; i < 128; i++)
                    {
                        b2[i] = b1[i + 4];
                        Console.WriteLine("byte[" + i + "]:" + b2[i]);
                    }                byte[] b3 = new byte[16];
                    for (int i = 0; i < 16; i++)
                    {
                        b3[i] = b2[i];                }                //Console.WriteLine("Plan decrynt text:" + new String(b2));                byte[] encodedata = null;
                    encodedata = System.Convert.FromBase64String("MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDF+vv+Mm+fweJBXM0Is2DDRczTmipTQmxFDnohGMggOU+gFFgeZ8JSx4lMHU+MCw96kQqFaII6UAA11Jc4ZP26dTkvdulX09y3HglTNw4eVidV/h4aA6Anx3bpPU9NjgHhG1oEOXiYexmmOlhrvTU3ToSGpl5z533hGErNqSBP9QIDAQAB");
                    AsymmetricKeyParameter pubKey = PublicKeyFactory.CreateKey(encodedata);                byte[] decSessionKey = decrypt(pubKey, b2);                int keyLen = 128/8;
                    byte[] sessionKey = new byte[keyLen];
                    int decSessionKeyLen = decSessionKey.Length;
                    for (int i = 0; i < keyLen; i++)
                    {
                        sessionKey[i] = decSessionKey[decSessionKeyLen - keyLen + i];
                    }
                    string base64SessionKey = Convert.ToBase64String(sessionKey);
                    Console.WriteLine("out2:" + base64SessionKey);
                    // Console.WriteLine("pubkey:" + new sun.misc.BASE64Encoder().encode( publicKey.getEncoded()));
                    // byte[] result = KeyRSA.decrypt2(publicKey,b2);
                    //Console.WriteLine("Decrytpt text(base64):" + new sun.misc.BASE64Encoder().encode(result));
                    //Console.WriteLine("out2:" + new sun.misc.BASE64Encoder().encode(decrypt(publicKey,b2)));                //pubKeySpec.
                    //Key pubkey = new RSAPublicKey();
                }
                catch
                {            }        }        public static byte[] decrypt(AsymmetricKeyParameter key, byte[] raw)
            {
              try {
                  IBufferedCipher c = CipherUtilities.GetCipher("RSA");
                  c.Init(false, key);
                  int blockSize = c.GetBlockSize();
                   c.ProcessBytes(raw);
                 
                  byte[] outbytes = c.DoFinal();
                  return outbytes;
              } catch (Exception e) {
                   throw (e);
              }
          }          }}
      

  14.   

    有区别吗? 非对称加密,即可以公钥加密也可以私钥加密啊。解密的时候公钥加的密就用私钥解,私钥加的就用公钥解。
    我前面的定义里面已经说了。
    上面只是一个例子,看得懂的人都知道是可以互换的。这个我做的是公钥解密的例子,关于这个解密的函数,Key是个参数项。也就是说可能是私钥可能是公钥。
      

  15.   

    当然不一样,JAVA产生的私钥没办法在.net里面AsymmetricKeyParameter,公钥确是可以的。同意的,通过JAVA产生的私钥也没办法在获得RSAParameters,公钥却可以通过将modulus和exponent都翻转来获取。
      

  16.   

    当然不一样,JAVA产生的私钥没办法在.net里面转换为AsymmetricKeyParameter,公钥确是可以的,你的例子上就可以看出来。同样的,通过JAVA产生的私钥也没办法在转换为System.Security.Cryptography里的RSAParameters对象,公钥却可以通过将modulus和exponent都翻转来获取System.Security.Cryptography里的RSAParameters对象,MSDN就有讲到。不要想当然了,如果可以转换,还望多加指教,贴个例子瞧瞧。我自己做了测试,没有成功,公钥是可以的,转换后的公钥可以验证加密的数据,我的理解RSA的公钥只要modulus,exponent,私钥却需要更多的信息内容,例如D,N等。上面的回复错别字太多,请见谅,我没办法修改,只能重新回复一下了。
      

  17.   


    AsymmetricKeyParameter 是第三方的类,不是.net类库的。这个第三方是仿照Java写的,基本实现了ASN.1协议的。
    .net的非对称加密很好没有实现ASN.1,封装的也不好。具体前人已经写过一些对比文章了,我也不罗嗦了。
    第三方类库地址 http://www.bouncycastle.org/csharp
    参考文章 http://www.cnblogs.com/jobs/archive/2006/09/22/512297.html
      

  18.   


       RSACryptoServiceProvider TempRSA = new RSACryptoServiceProvider();
                RSACryptoServiceProvider RSA = new RSACryptoServiceProvider();
           
                //导入私匙 来加密
                RSA.ImportParameters(TempRSA.ExportParameters(false));
                byte[] EnCode = RSA.Encrypt(Code, false);
              
                //导入公匙来解密
                RSA.ImportParameters(TempRSA.ExportParameters(true));
                byte[] DeCode = RSA.Decrypt(EnCode, false);            string temps = System.Text.Encoding.UTF8.GetString(DeCode);
    一般私匙是保存在密匙容器里
      

  19.   


      byte[] Code = System.Text.Encoding.UTF8.GetBytes("1232324");
             
                RSACryptoServiceProvider TempRSA = new RSACryptoServiceProvider();
                RSACryptoServiceProvider RSA = new RSACryptoServiceProvider();
           
                //导入私匙 来加密
                RSA.ImportParameters(TempRSA.ExportParameters(false));
                byte[] EnCode = RSA.Encrypt(Code, false);
              
                //导入公匙来解密
                RSA.ImportParameters(TempRSA.ExportParameters(true));
                byte[] DeCode = RSA.Decrypt(EnCode, false);            string temps = System.Text.Encoding.UTF8.GetString(DeCode);
      

  20.   

    哇,楼上不要误导人啊            //导入私匙 来加密
                RSA.ImportParameters(TempRSA.ExportParameters(false));
                byte[] EnCode = RSA.Encrypt(Code, false);
    这里你导出的是公钥啊。
      

  21.   

    to raineo:不要误人哦!
    .net 的RSACryptoServiceProvider 真实现公钥加密,私钥解密,所以你的代码是有问题的如果要实现实现私钥加密,公钥解密,可以使用RSACryptoServiceProvider 的签名和验证功能,其实这个不是很实际意义上的RSA加密解密
      

  22.   

    RSA算法是不可能实现私钥加密,公钥解密的(至少目前的数学家做不到)。RSA算法的意图是:公钥加密,私钥解密,公钥不可解密。从而保护信息安全。
      

  23.   

    正苦着找呀。到底dotnet能否实现私钥加密、公钥解密?
      

  24.   

    34楼,你公司做了一个web系统,要把它卖给客户,但有授权时间,你必须把 授权时间写入 加密的授权文件中,然后 在客户运行系统时来进行比较,比较的时候就要解密出授权文件中的时间,但私钥不能给客户,所以只能实现私钥加密公钥解密。
    没用?你觉得有用不?
      

  25.   

    35楼:
    web系统:license.dat 它是授权密文
    web中有公钥值E N 如KeyUtil.StringTokey("algorithm:RSA;format:X.509;encoded:MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQChlTqQxO+NE+QScBSRn0GsLU4JW+MI4+9r4/FEPkeDNS9cAkhl8pKI0KA9oyOuJzB1xFs3AY0ZBzzO7UfDIZoIA8ZVfrNLRwRpa5Kd50qIq+H8fvqyc6WtGk9vl+phjE0rXioM/lWzo8d7Or3J6wbezhSISUA5Qkb6+I99p3pubQIDAQAB",new BouncyCastleProvider())