|
这里,讲一下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;
-
}
如此处生成的公钥为
-
<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产生公钥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;
-
}
-
}
获得公钥后就可以进行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;
-
}
加密后的数据提交给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;
-
}
这样,就顺利完成了。
经过测试,这样做的确得到了正确的结果。
若是有什么地方有问题,还望大家指正! |