各位达人,小弟最近在做项目时候遇到一个关于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”不一样。
/// 通过公钥解密
/// </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、程序计算出本地key后传给经销商。
2、经销商算出公私钥对,用RSA私钥加密收到的key。
3、经销商将加密后的key和公钥发放给使用者
4、使用者在软件中进行认证。我要做的就是认证。加密后的key和公钥是知道的。我要把加密后的key用给我的公钥解密。
但是这个过程其实是没有意义的。
经销商的私钥是他自己保留的,公钥是可以发布给所有人的,用私钥加密后的内容,理论上说,可以被网上所有的人所阅读的(任何人得到加密后的文本后,只要找到公钥,就可以解密)所以这个加密本身是没有意义的。而唯一能确定的是,这段密文,肯定是经销商用他的私钥加密的,是这个经销商发出的,他人是无法仿冒的,这就叫签名。
而正真的加密,其实是要用接受方的公钥加密,然后传给接受方,这样就只有接受方可以解密并阅读,这才是正真的加密办法。
具体的加密流程如下:
生成随机key
对称加密(明文,随机key)->密文
rsa加密(随机key,接受方公钥)->加密的key
密文+加密的key传输给接受方
rsa解密(加密的key,接受方私钥)->随机key
对称解密(密文,随机key)->明文
而签名过程就不一样:
rsa签名(明文,发出方私钥)->签名串
明文+签名串传输给接受方
rsa验证签名(明文,发出方公钥,签名串)->true/false
注:签名的时候,会先对明文进行hash运算,这样可以减少加密的量
应该说是非对称加密。
1976年,美国学者Dime和Henman为解决信息公开传送和密钥管理问题,提出一种新的密钥交换协议,允许在不安全的媒体上的通讯双方交换信息,安全地达成一致的密钥,这就是“公开密钥系统”。相对于“对称加密算法”这种方法也叫做“非对称加密算法”。
与对称加密算法不同,非对称加密算法需要两个密钥:公开密钥(publickey)和私有密钥(privatekey)。公开密钥与私有密钥是一对,如果用公开密钥对数据进行加密,只有用对应的私有密钥才能解密;如果用私有密钥对数据进行加密,那么只有用对应的公开密钥才能解密。因为加密和解密使用的是两个不同的密钥,所以这种算法叫作非对称加密算法。 这种基础知识LZ你有空要去补。
1)、生成公钥和私钥
公钥对外公开,随UI工具一同发布,私钥用于License管理工具的生成License的签名使用。
2)UI工具注册界面能够自动生成用于生成license的key,key的获得方法是获取主机第一块硬盘序列号,并把这个序列号发送给DMX
3)使用RSA算法,用私钥对客户发送过来的序列号进行加密,生成密文,并对密文进行Base64编码,生成License文件中的签名内容,传递给客户
4)在UI工具的注册页面,输入license签名内容,进行注册;
5)UI工具运行,首先读取license签名内容,Base64解码,用公钥解密,把解密的内容和系统的第一个硬盘序列号比对,一致则通过,否则进入注册页面,等待注册。就是这样的过程,这是原需求文档。
http://msdn.microsoft.com/zh-cn/library/9tsc5d0z.aspx
纯java去实现比较容易,拿java的私钥在.net里面签名是个难点。看那位高人出来指点一下。关注~~~
一定要用.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);
}
} }}
我前面的定义里面已经说了。
上面只是一个例子,看得懂的人都知道是可以互换的。这个我做的是公钥解密的例子,关于这个解密的函数,Key是个参数项。也就是说可能是私钥可能是公钥。
AsymmetricKeyParameter 是第三方的类,不是.net类库的。这个第三方是仿照Java写的,基本实现了ASN.1协议的。
.net的非对称加密很好没有实现ASN.1,封装的也不好。具体前人已经写过一些对比文章了,我也不罗嗦了。
第三方类库地址 http://www.bouncycastle.org/csharp
参考文章 http://www.cnblogs.com/jobs/archive/2006/09/22/512297.html
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);
一般私匙是保存在密匙容器里
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);
RSA.ImportParameters(TempRSA.ExportParameters(false));
byte[] EnCode = RSA.Encrypt(Code, false);
这里你导出的是公钥啊。
.net 的RSACryptoServiceProvider 真实现公钥加密,私钥解密,所以你的代码是有问题的如果要实现实现私钥加密,公钥解密,可以使用RSACryptoServiceProvider 的签名和验证功能,其实这个不是很实际意义上的RSA加密解密
没用?你觉得有用不?
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())