这里,讲一下RSA算法加解密在C#和Java之间交互的问题,这两天纠结了很久,也看了很多其他人写的文章,颇受裨益,但没能解决我的实际问题,终于,还是被我捣鼓出来了。
首先,介绍一下写这代码的目的:完成webService验证问题,服务器端采用C#开发,客户端采用Java开发。服务器端给客户端提供公钥,已进行数据加密,客户端加密后提数据提交给服务器,服务器用私钥对数据解密,进行验证。
这里遇到的主要问题是C#RSACryptoServiceProvider类产生的公钥、私钥都是xml字符串数据,而javaRSA算法要求的Modulus、Exponent都是BigInteger类型,两者间的转换才是问题所在。
关于Java和C#各自独立的进行RSA加密解密,大家可以看整两篇文章,javaRSA加密解密实现()和C#中RSA加密解密和签名与验证的实现。
接下来讲一下实现步骤:
首先由C#RSACryptoServiceProvider类生成公钥、私钥
- publicDictionary<string,string>createKeyPair()
- {
- Dictionary<string,string>keyPair=newDictionary<string,string>();
- RSACryptoServiceProviderprovider=newRSACryptoServiceProvider(1024);
- keyPair.Add("PUBLIC",provider.ToXmlString(false));
- keyPair.Add("PRIVATE",provider.ToXmlString(true));
- returnkeyPair;
- }
/// <summary>
/// 生成公钥、私钥
/// </summary>
/// <returns>公钥、私钥,公钥键"PUBLIC",私钥键"PRIVATE"</returns>
public Dictionary<string, string> createKeyPair()
{
Dictionary<string, string> keyPair = new Dictionary<string, string>();
RSACryptoServiceProvider provider = new RSACryptoServiceProvider(1024);
keyPair.Add("PUBLIC", provider.ToXmlString(false));
keyPair.Add("PRIVATE", provider.ToXmlString(true));
return keyPair;
}
如此处生成的公钥为
- <RSAKeyValue>
- <Modulus>t+56m5jXXonAJAKC7mgkhAZX5gWJTZojbSloLpLBGEWiebFaM+aUUKALfRx83/HaUV79ZiR3zuLJOLBdALx1cmcPk/b9fdNblLmzqi4cfSnfmMLWh05xf+ZS1pKHSKQtui3dfuu+3XH6Ak+S38dpIZUj/hihQQuKysN6GJ9h+c8=
- </Modulus>
- <Exponent>AQAB</Exponent>
- </RSAKeyValue>
<RSAKeyValue>
<Modulus>t+56m5jXXonAJAKC7mgkhAZX5gWJTZojbSloLpLBGEWiebFaM+aUUKALfRx83/HaUV79ZiR3zuLJOLBdALx1cmcPk/b9fdNblLmzqi4cfSnfmMLWh05xf+ZS1pKHSKQtui3dfuu+3XH6Ak+S38dpIZUj/hihQQuKysN6GJ9h+c8=
</Modulus>
<Exponent>AQAB</Exponent>
</RSAKeyValue>
在客户端(Java)对C#提供的公钥提取Modulus和Exponent
- publicstaticHashMap<String,String>rsaParameters(StringxmlPublicKey)throwsMalformedURLException,DocumentException{
- HashMap<String,String>map=newHashMap<String,String>();
- Documentdoc=DocumentHelper.parseText(xmlPublicKey);
- Stringmudulus=(String)doc.getRootElement().element("Modulus").getData();
- Stringexponent=(String)doc.getRootElement().element("Exponent").getData();
- map.put("mudulus",mudulus);
- map.put("exponent",exponent);
- returnmap;
- }
/**
* 返回包含模数modulus和指数exponent的haspMap
* @return
* @throws MalformedURLException
* @throws DocumentException
*/
public static HashMap<String,String> rsaParameters(String xmlPublicKey) throws MalformedURLException, DocumentException{
HashMap<String ,String> map = new HashMap<String, String>();
Document doc = DocumentHelper.parseText(xmlPublicKey);
String mudulus = (String) doc.getRootElement().element("Modulus").getData();
String exponent = (String) doc.getRootElement().element("Exponent").getData();
map.put("mudulus", mudulus);
map.put("exponent", exponent);
return map;
}
用Modulus和Exponent产生公钥RSAPublicKey(java)
这里有个关键步骤先对Mudolus和Exponent进行Base64解码,这个是由于C#生成的密钥对,其参数已经过Base64编码成String类型,而java
RSA参数是未经base64编码的byte[]类型。
至于Base64编码、解码方法,参考这篇文章,java 编码和解码,想详细。
- publicstaticbyte[]decodeBase64(Stringinput)throwsException{
- Classclazz=Class.forName("com.sun.org.apache.xerces.internal.impl.dv.util.Base64");
- MethodmainMethod=clazz.getMethod("decode",String.class);
- mainMethod.setAccessible(true);
- ObjectretObj=mainMethod.invoke(null,input);
- return(byte[])retObj;
- }
- publicstaticPublicKeygetPublicKey(Stringmodulus,Stringexponent){
- try{
- byte[]m=decodeBase64(modulus);
- byte[]e=decodeBase64(exponent);
- BigIntegerb1=newBigInteger(1,m);
- BigIntegerb2=newBigInteger(1,e);
- KeyFactorykeyFactory=KeyFactory.getInstance("RSA");
- RSAPublicKeySpeckeySpec=newRSAPublicKeySpec(b1,b2);
- return(RSAPublicKey)keyFactory.generatePublic(keySpec);
- }catch(Exceptione){
- e.printStackTrace();
- returnnull;
- }
- }
public static byte[] decodeBase64(String input) throws Exception{
Class clazz=Class.forName("com.sun.org.apache.xerces.internal.impl.dv.util.Base64");
Method mainMethod= clazz.getMethod("decode", String.class);
mainMethod.setAccessible(true);
Object retObj=mainMethod.invoke(null, input);
return (byte[])retObj;
}
/**
* 返回RSA公钥
* @param modules
* @param exponent
* @return
*/
public static PublicKey getPublicKey(String modulus, String exponent){
try {
byte[] m = decodeBase64(modulus);
byte[] e = decodeBase64(exponent);
BigInteger b1 = new BigInteger(1,m);
BigInteger b2 = new BigInteger(1,e);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
RSAPublicKeySpec keySpec = new RSAPublicKeySpec(b1, b2);
return (RSAPublicKey) keyFactory.generatePublic(keySpec);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
获得公钥后就可以进行RSA加密处理了,这里还有一点需要提的是,RSA加密解密都有最大长度限制,加密最大长度为117字节,解密最大长度是128字节,此外,此处加密得到的数据是经过Base64编码处理的
- publicstaticStringencrypt(byte[]source,PublicKeypublicKey)throwsException{
- StringencryptData="";
- try{
- Ciphercipher=Cipher.getInstance("RSA");
- cipher.init(Cipher.ENCRYPT_MODE,publicKey);
- intlength=source.length;
- intoffset=0;
- byte[]cache;
- ByteArrayOutputStreamoutStream=newByteArrayOutputStream();
- inti=0;
- while(length-offset>0){
- if(length-offset>MAXENCRYPTSIZE){
- cache=cipher.doFinal(source,offset,MAXENCRYPTSIZE);
- }else{
- cache=cipher.doFinal(source,offset,length-offset);
- }
- outStream.write(cache,0,cache.length);
- i++;
- offset=i*MAXENCRYPTSIZE;
- }
- returnencodeBase64(outStream.toByteArray());
- }catch(NoSuchAlgorithmExceptione){
- e.printStackTrace();
- }catch(NoSuchPaddingExceptione){
- e.printStackTrace();
- }catch(InvalidKeyExceptione){
- e.printStackTrace();
- }catch(IllegalBlockSizeExceptione){
- e.printStackTrace();
- }catch(BadPaddingExceptione){
- e.printStackTrace();
- }
- returnencryptData;
- }
public static String encrypt(byte[] source, PublicKey publicKey) throws Exception {
String encryptData ="";
try {
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
int length = source.length;
int offset = 0;
byte[] cache;
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
int i = 0;
while(length - offset > 0){
if(length - offset > MAXENCRYPTSIZE){
cache = cipher.doFinal(source, offset, MAXENCRYPTSIZE);
}else{
cache = cipher.doFinal(source, offset, length - offset);
}
outStream.write(cache, 0, cache.length);
i++;
offset = i * MAXENCRYPTSIZE;
}
return encodeBase64(outStream.toByteArray());
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
}
return encryptData;
}
加密后的数据提交给C#服务器端进行解密,当然,这里也要注意最大长度限制问题
-
-
- publicstaticstringdecrypt(stringencryptData,stringprivateKey)
- {
- stringdecryptData="";
- try
- {
- RSACryptoServiceProviderprovider=newRSACryptoServiceProvider();
- provider.FromXmlString(privateKey);
- byte[]bEncrypt=Convert.FromBase64String(encryptData);
- intlength=bEncrypt.Length;
- intoffset=0;
- stringcache;
- inti=0;
- while(length-offset>0)
- {
- if(length-offset>MAXDECRYPTSIZE)
- {
- cache=Encoding.UTF8.GetString(provider.Decrypt(getSplit(bEncrypt,offset,MAXDECRYPTSIZE),false));
- }
- else
- {
- cache=Encoding.UTF8.GetString(provider.Decrypt(getSplit(bEncrypt,offset,length-offset),false));
- }
- decryptData+=cache;
- i++;
- offset=i*MAXDECRYPTSIZE;
- }
- }
- catch(Exceptione)
- {
- throwe;
- }
- returndecryptData;
- }
-
-
- privatestaticbyte[]getSplit(byte[]input,intoffset,intlength)
- {
- byte[]output=newbyte[length];
- for(inti=offset;i<offset+length;i++)
- {
- output[i-offset]=input[i];
- }
- returnoutput;
- }
/// <summary>
/// RSA解密
/// </summary>
/// <param name="encryptData">经过Base64编码的密文</param>
/// <param name="privateKey">私钥</param>
/// <returns>RSA解密后的数据</returns>
public static string decrypt(string encryptData, string privateKey)
{
string decryptData = "";
try
{
RSACryptoServiceProvider provider = new RSACryptoServiceProvider();
provider.FromXmlString(privateKey);
byte[] bEncrypt = Convert.FromBase64String(encryptData);
int length = bEncrypt.Length;
int offset = 0;
string cache ;
int i = 0;
while (length - offset > 0)
{
if (length - offset > MAXDECRYPTSIZE)
{
cache = Encoding.UTF8.GetString(provider.Decrypt(getSplit(bEncrypt, offset, MAXDECRYPTSIZE), false));
}
else
{
cache = Encoding.UTF8.GetString(provider.Decrypt(getSplit(bEncrypt, offset, length - offset), false));
}
decryptData += cache;
i++;
offset = i*MAXDECRYPTSIZE;
}
}
catch(Exception e)
{
throw e;
}
return decryptData;
}
/// <summary>
/// 截取字节数组部分字节
/// </summary>
/// <param name="input"></param>
/// <param name="offset">起始偏移位</param>
/// <param name="length">截取长度</param>
/// <returns></returns>
private static byte[] getSplit(byte[] input, int offset, int length)
{
byte[] output = new byte[length];
for (int i = offset; i < offset + length; i++)
{
output[i - offset] = input[i];
}
return output;
}
这样,就顺利完成了。
经过测试,这样做的确得到了正确的结果。
若是有什么地方有问题,还望大家指正!
----------------------------------------------------------------------------------------
C# Java间进行RSA加密解密交互(二)
C# Java间进行RSA加密解密交互(三)
转自:http://blog.csdn.net/dslinmy/article/details/37362661