java加解密RSA使用方法代码示例
最近为了分析一段请求流,不得不去研究一下rsa加密。
首先,强调一点:密钥的“钥”读“yue”,不是“yao”,额。。。
网上关于rsa的原理一抓一大把的,这里只是简单说说我的理解:
1. 两个足够大的互质数p, q;
2. 用于模运算的模 n=p*q;
3. 公钥ku(e, n)中的e满足 1<e< (p-1)(q-1),且与(p-1)(q-1)互质;
4. 密钥kr(d, n)中的d满足 d*e % (p-1)(q-1)= 1,%是取余运算。
因为公钥是公开的,所以我知道了e和n,那么根据2,3,4式子的关系,我们只要从n的值推出p, q的值则可计算出d的值,也就能找到密钥。
然而,关键就在这里, n=p*q,如果两个互质数p和q足够大,那么根据目前的计算机技术和其他工具,至今也没能从n分解出p和q,这是数学上的一个难题,也正是这个难题成为了rsa加密至今被广泛使用的原因。换句话说,只要密钥长度n足够大(一般1024足矣),基本上不可能从公钥信息推出私钥信息。
好了,这里作为研究的随笔,记录一下java如何使用,以下主要有三种方法,基本大同小异,只是获取公钥私钥的途径不一样就是了:
方法一:
利用keypairgenerator直接生成公钥和密钥,一般私钥保留给服务端,公钥交给客户端。
public class rsacryptography { public static string data="hello world"; public static void main(string[] args) throws exception { // todo auto-generated method stub keypair keypair=genkeypair(1024); //获取公钥,并以base64格式打印出来 publickey publickey=keypair.getpublic(); system.out.println("公钥:"+new string(base64.getencoder().encode(publickey.getencoded()))); //获取私钥,并以base64格式打印出来 privatekey privatekey=keypair.getprivate(); system.out.println("私钥:"+new string(base64.getencoder().encode(privatekey.getencoded()))); //公钥加密 byte[] encryptedbytes=encrypt(data.getbytes(), publickey); system.out.println("加密后:"+new string(encryptedbytes)); //私钥解密 byte[] decryptedbytes=decrypt(encryptedbytes, privatekey); system.out.println("解密后:"+new string(decryptedbytes)); } //生成密钥对 public static keypair genkeypair(int keylength) throws exception{ keypairgenerator keypairgenerator=keypairgenerator.getinstance("rsa"); keypairgenerator.initialize(1024); return keypairgenerator.generatekeypair(); } //公钥加密 public static byte[] encrypt(byte[] content, publickey publickey) throws exception{ cipher cipher=cipher.getinstance("rsa");//java默认"rsa"="rsa/ecb/pkcs1padding" cipher.init(cipher.encrypt_mode, publickey); return cipher.dofinal(content); } //私钥解密 public static byte[] decrypt(byte[] content, privatekey privatekey) throws exception{ cipher cipher=cipher.getinstance("rsa"); cipher.init(cipher.decrypt_mode, privatekey); return cipher.dofinal(content); } }
运行结果:
公钥:migfma0gcsqgsib3dqebaquaa4gnadcbiqkbgqcsl6v7xnkvr9+notekzm1fjhdl7odqa66hrg5d/wgq1xif22mex7pnnc8prbrscjqzjbzq3znvmv5xqrvscgbqampfmixetu6lifqhogpth9ghzsemanqp0ssd1tkhcpl2njk/hzabwybzpbjlidgfcmotehnfudlimgcusmv0awidaqab 私钥:miicdqibadanbgkqhkig9w0baqefaascal8wggjbageaaogbajkxpxtc2rvh342i16rmbuwmd0vugoodrqgsbkp/cbdvcgxbaz7humc1zw9effjwlbklvnddmdwzxleqtviizupow8wyhd627qwj9aegam0f2cfmx6zqeqnsxj3voqdw8vy2ot+flptzghm9uowj2b9wyi16gcvr2ugwyk6wxxrragmbaaecgya8ybjx5jxcfgek3hzsqrz4obiqq+d0go+7xrjjahz5+g8t2mb19ozg9vicgrednkbiexh5lcvehxytvrfpsaaagoa9dfkktaqmiq15z3xxtgihxg2dxdfj1gnyhnjhml8rsff2nsfqarrga8y36k0ozq240sdls6gbbmmohruraqjbaom6fw7cvxfmvzl0jbzmdl3spk3ssnm6tfxady39w1g9rmghkqs2xoubce06ic/m9pxjnpmuxhgvytiyldc6nbkcqqcgj5o/sa0wyqqvw+wxqvleblnd9zt2qog5wvyrmokp+uye3swsfktz1ysd5djoyqprc/lbcx7x+a8qrqldrw1dakamhwj4vamtd5fg4e2s74fauw4dmuzt3okwxvupnhe/m3nkslcjrmpmxpk9ux/ycf0iac4dcgz0qal+lx8+p+opaka6kol+agtliwbgv8wayadxpias8gtbcto9d7irhnlly7suvankwot+hwxvjpkvulnhmyz8on4irrlfv+m0go79akauwv5nipi7ekscrzemiarjoyxgpfv2pqnrqzbqm5xvxtbucpmuopnyk/9zm33riwwjn6uyv9hfg7e6hnsk2qir 加密后:v?,y9?檂o庬鉤h﹎m_?$惇櫤?p崃?4蹥bhhn?25/?6t駩樁w草遏鬼碙&柀&*軄q晛1鱋a祉@眽`剪啃`噾?>x/运婣?hi砛奊瑘i?$b捞 ?毟"st 解密后:hello world
方法二:
实际上,方法一只是用来生成密钥就ok了,生成的密钥需要保存到本地文件中,所以一般不会在客户端调用keypairgenerator进行密钥的生成操作。
这里,我们可以将方法一得到的密钥保存到文件,下次我们直接读取就可以了。我假设以string的形式保存在文件内,那么接下来直接使用读取到的string生成密钥即可。
当然,你也可以使用openssl来生成也可以,不过我觉得麻烦就不弄了。
public class rsacryptography { public static string data="hello world"; public static string publickeystring="migfma0gcsqgsib3dqebaquaa4gnadcbiqkbgqcislp98m/56hexx/9fdm8iuieqozy6kn2jmcbzs5/bhj+u4pzichjfggylwnd8nwn4byr2kxxyo8qgvc8rprzckn0oslqlgzgmnvosldw80uxq90zsvhdtohusfhw8bv//b4evunjbb8g9tpvxr6p5ej6fmor/ky2dvfqcqm4+5qidaqab"; public static string privatekeystring="miicdqibadanbgkqhkig9w0baqefaascal8wggjbageaaogbaihis/3wz/nod7ff/0umzyk4grcjplqsfykxxtlln8gen5tg9kgkel+cbivad3w1afgfivathhi7xcc9zyulfkkq3q5iuoubkay2+hkupdzrrer3rmxucnm4e5iufdwg//8hh69q0kehyd22lxgvo/kqnouyhh+rjz1uvajazj7lagmbaaecgyavh26vsggy0yl/asw/qztzn837w93hf3cvyiaokxlerl/lvbjz5otshq09f2iaxbfedfmy5cb9r0w/aly851jxri8wakx2w2fnllzhha01fmlnlosumoirf++jcbsajdcrciir8esvnub6ymbcrx/fqhdx3+t/vubsafxyt9tsgqjbalshurnovzs1qjctl6pkns0v5qio88szyp7lzgq0eyglvfupdllx8/mrsdi4dhermtcutucatzgqu20uai0emyecqqc6il1kdkw8peeb0jzmhbs+cmcsbgatiat4pfo1b/i9/bo0qnrgdqycjt3j9ux22dpybdpdtmjmrnrakfb4bjdfakbmrdwtzovc88il2mcc98sjcii5wdl3yseyozto7icmzuh/zlfzm5ctslq8/hdiqvarnj4jwzia/q6fg6e8ko2hakb0ek1vlf/ox7e5gkk533hmuu8xgwn6i5bhnbyd06qyqytbbthmbrfsay4uh91qwd3u9gawqoczognft/o03v5lakbqq8jzd2lhifey+9cf1hshd5wqbjjkppib57ck08hn7vulx5epj02q8ahdzketaw+esqjwpngsu5wpqsy2uyno"; public static void main(string[] args) throws exception { // todo auto-generated method stub //获取公钥 publickey publickey=getpublickey(publickeystring); //获取私钥 privatekey privatekey=getprivatekey(privatekeystring); //公钥加密 byte[] encryptedbytes=encrypt(data.getbytes(), publickey); system.out.println("加密后:"+new string(encryptedbytes)); //私钥解密 byte[] decryptedbytes=decrypt(encryptedbytes, privatekey); system.out.println("解密后:"+new string(decryptedbytes)); } //将base64编码后的公钥字符串转成publickey实例 public static publickey getpublickey(string publickey) throws exception{ byte[ ] keybytes=base64.getdecoder().decode(publickey.getbytes()); x509encodedkeyspec keyspec=new x509encodedkeyspec(keybytes); keyfactory keyfactory=keyfactory.getinstance("rsa"); return keyfactory.generatepublic(keyspec); } //将base64编码后的私钥字符串转成privatekey实例 public static privatekey getprivatekey(string privatekey) throws exception{ byte[ ] keybytes=base64.getdecoder().decode(privatekey.getbytes()); pkcs8encodedkeyspec keyspec=new pkcs8encodedkeyspec(keybytes); keyfactory keyfactory=keyfactory.getinstance("rsa"); return keyfactory.generateprivate(keyspec); } //公钥加密 public static byte[] encrypt(byte[] content, publickey publickey) throws exception{ cipher cipher=cipher.getinstance("rsa");//java默认"rsa"="rsa/ecb/pkcs1padding" cipher.init(cipher.encrypt_mode, publickey); return cipher.dofinal(content); } //私钥解密 public static byte[] decrypt(byte[] content, privatekey privatekey) throws exception{ cipher cipher=cipher.getinstance("rsa"); cipher.init(cipher.decrypt_mode, privatekey); return cipher.dofinal(content); } }
运行结果
加密后:.a羫閍q瘿u鉵やas祏@q??>?:sq3?a_l'遥`t?6蔍nk%ic尊[鹰#枋リ礼?1i獙 l躕锇仩?"糍>ij弻脶諾晦5udq積嬨璼ey冖u'輵\l〥% 解密后:hello world
方法三:
除了保存密钥字符串之外,其他的做法一般是只保存 模n(modulus),公钥和私钥的e和d(exponent)。
其中,n, e, d可以这样获取到,获取到后可以保存到本地文件中。
//获取公钥 rsapublickey publickey=(rsapublickey) getpublickey(publickeystring); biginteger modulus1=publickey.getmodulus(); biginteger exponent1=publickey.getpublicexponent(); //获取私钥 rsaprivatekey privatekey=(rsaprivatekey) getprivatekey(privatekeystring); biginteger modulus2=privatekey.getmodulus(); biginteger exponent2=privatekey..getprivateexponent();
这里,假设我已经从文件中读取到了modulus和exponent:
public class rsacryptography { public static string data="hello world"; public static string modulusstring="95701876885335270857822974167577168764621211406341574477817778908798408856077334510496515211568839843884498881589280440763139683446418982307428928523091367233376499779842840789220784202847513854967218444344438545354682865713417516385450114501727182277555013890267914809715178404671863643421619292274848317157"; public static string publicexponentstring="65537"; public static string privateexponentstring="15118200884902819158506511612629910252530988627643229329521452996670429328272100404155979400725883072214721713247384231857130859555987849975263007110480563992945828011871526769689381461965107692102011772019212674436519765580328720044447875477151172925640047963361834004267745612848169871802590337012858580097"; public static void main(string[] args) throws exception { // todo auto-generated method stub //由n和e获取公钥 publickey publickey=getpublickey(modulusstring, publicexponentstring); //由n和d获取私钥 privatekey privatekey=getprivatekey(modulusstring, privateexponentstring); //公钥加密 byte[] encryptedbytes=encrypt(data.getbytes(), publickey); system.out.println("加密后:"+new string(encryptedbytes)); //私钥解密 byte[] decryptedbytes=decrypt(encryptedbytes, privatekey); system.out.println("解密后:"+new string(decryptedbytes)); } //将base64编码后的公钥字符串转成publickey实例 public static publickey getpublickey(string modulusstr, string exponentstr) throws exception{ biginteger modulus=new biginteger(modulusstr); biginteger exponent=new biginteger(exponentstr); rsapublickeyspec publickeyspec=new rsapublickeyspec(modulus, exponent); keyfactory keyfactory=keyfactory.getinstance("rsa"); return keyfactory.generatepublic(publickeyspec); } //将base64编码后的私钥字符串转成privatekey实例 public static privatekey getprivatekey(string modulusstr, string exponentstr) throws exception{ biginteger modulus=new biginteger(modulusstr); biginteger exponent=new biginteger(exponentstr); rsaprivatekeyspec privatekeyspec=new rsaprivatekeyspec(modulus, exponent); keyfactory keyfactory=keyfactory.getinstance("rsa"); return keyfactory.generateprivate(privatekeyspec); } //公钥加密 public static byte[] encrypt(byte[] content, publickey publickey) throws exception{ cipher cipher=cipher.getinstance("rsa");//java默认"rsa"="rsa/ecb/pkcs1padding" cipher.init(cipher.encrypt_mode, publickey); return cipher.dofinal(content); } //私钥解密 public static byte[] decrypt(byte[] content, privatekey privatekey) throws exception{ cipher cipher=cipher.getinstance("rsa"); cipher.init(cipher.decrypt_mode, privatekey); return cipher.dofinal(content); } }
运行结果:
加密后:g[>x锽.+?"s謉q琾b?,\??戮陇⒑e*y浌x蜤??眲z秔豨wm麞衹靫〩2孲*?x?9赉埤腴暎?~_邊槻u☉疀群廥5z钛? 媷bdh}>i1运癑障a杽 解密后:hello world
这里三种方式总结起来也就是
1,.keypairgenerator获取key;
2. string获取key;
3. modulus和exponent获取key。
--------------------后来,我发现,数据太长抛异常了,好吧---------------------------
然而,当加密的数据太长的时候则需要分组加密,不然数据过长会抛异常,如“encryt data is too much”,或者“data length is longer than 127”等。
上面三个方法使用的key的n值(modulus)是1024bit的,也就是128byte,根据rsa加密规则,加密1 byte字节的数据,需要12 byte,即其他11byte可能用于记录其他信息什么的,这里我就不清楚了,而1024bit长度的key则最多可以加密128-11=117byte的数据,所以,对于超过117byte的数据,我们需要以117byte为一组进行数据分割。
public class rsacryptography { public static string data="hello world"; public static string modulusstring="95701876885335270857822974167577168764621211406341574477817778908798408856077334510496515211568839843884498881589280440763139683446418982307428928523091367233376499779842840789220784202847513854967218444344438545354682865713417516385450114501727182277555013890267914809715178404671863643421619292274848317157"; public static string publicexponentstring="65537"; public static string privateexponentstring="15118200884902819158506511612629910252530988627643229329521452996670429328272100404155979400725883072214721713247384231857130859555987849975263007110480563992945828011871526769689381461965107692102011772019212674436519765580328720044447875477151172925640047963361834004267745612848169871802590337012858580097"; public static void main(string[] args) throws exception { // todo auto-generated method stub //由n和e获取公钥 publickey publickey=getpublickey(modulusstring, publicexponentstring); //由n和d获取私钥 privatekey privatekey=getprivatekey(modulusstring, privateexponentstring); //公钥加密 string encrypted=encrypt(data, publickey); system.out.println("加密后:"+encrypted); //私钥解密 string decrypted=decrypt(encrypted, privatekey); system.out.println("解密后:"+new string(decrypted)); } //将base64编码后的公钥字符串转成publickey实例 public static publickey getpublickey(string modulusstr, string exponentstr) throws exception{ biginteger modulus=new biginteger(modulusstr); biginteger exponent=new biginteger(exponentstr); rsapublickeyspec publickeyspec=new rsapublickeyspec(modulus, exponent); keyfactory keyfactory=keyfactory.getinstance("rsa"); return keyfactory.generatepublic(publickeyspec); } //将base64编码后的私钥字符串转成privatekey实例 public static privatekey getprivatekey(string modulusstr, string exponentstr) throws exception{ biginteger modulus=new biginteger(modulusstr); biginteger exponent=new biginteger(exponentstr); rsaprivatekeyspec privatekeyspec=new rsaprivatekeyspec(modulus, exponent); keyfactory keyfactory=keyfactory.getinstance("rsa"); return keyfactory.generateprivate(privatekeyspec); } //公钥加密,并转换成十六进制字符串打印出来 public static string encrypt(string content, publickey publickey) throws exception{ cipher cipher=cipher.getinstance("rsa");//java默认"rsa"="rsa/ecb/pkcs1padding" cipher.init(cipher.encrypt_mode, publickey); int splitlength=((rsapublickey)publickey).getmodulus().bitlength()/8-11; byte[][] arrays=splitbytes(content.getbytes(), splitlength); stringbuffer sb=new stringbuffer(); for(byte[] array : arrays){ sb.append(bytestohexstring(cipher.dofinal(array))); } return sb.tostring(); } //私钥解密,并转换成十六进制字符串打印出来 public static string decrypt(string content, privatekey privatekey) throws exception{ cipher cipher=cipher.getinstance("rsa"); cipher.init(cipher.decrypt_mode, privatekey); int splitlength=((rsaprivatekey)privatekey).getmodulus().bitlength()/8; byte[] contentbytes=hexstring2bytes(content); byte[][] arrays=splitbytes(contentbytes, splitlength); stringbuffer sb=new stringbuffer(); for(byte[] array : arrays){ sb.append(new string(cipher.dofinal(array))); } return sb.tostring(); } //拆分byte数组 public static byte[][] splitbytes(byte[] bytes, int splitlength){ int x; //商,数据拆分的组数,余数不为0时+1 int y; //余数 y=bytes.length%splitlength; if(y!=0){ x=bytes.length/splitlength+1; }else{ x=bytes.length/splitlength; } byte[][] arrays=new byte[x][]; byte[] array; for(int i=0; i<x; i++){ if(i==x-1 && bytes.length%splitlength!=0){ array=new byte[bytes.length%splitlength]; system.arraycopy(bytes, i*splitlength, array, 0, bytes.length%splitlength); }else{ array=new byte[splitlength]; system.arraycopy(bytes, i*splitlength, array, 0, splitlength); } arrays[i]=array; } return arrays; } //byte数组转十六进制字符串 public static string bytestohexstring(byte[] bytes) { stringbuffer sb = new stringbuffer(bytes.length); string stemp; for (int i = 0; i < bytes.length; i++) { stemp = integer.tohexstring(0xff & bytes[i]); if (stemp.length() < 2) sb.append(0); sb.append(stemp.touppercase()); } return sb.tostring(); } //十六进制字符串转byte数组 public static byte[] hexstring2bytes(string hex) { int len = (hex.length() / 2); hex=hex.touppercase(); byte[] result = new byte[len]; char[] achar = hex.tochararray(); for (int i = 0; i < len; i++) { int pos = i * 2; result[i] = (byte) (tobyte(achar[pos]) << 4 | tobyte(achar[pos + 1])); } return result; } private static byte tobyte(char c) { byte b = (byte) "0123456789abcdef".indexof(c); return b; } }
运行结果
加密后:0681cea1051892e038ceb83edcd5d549b61f31787fef92123e0961f2e8faf2590bad88a2341c9684e6b3791d03d95bdfff852efd79b6748364a8a2369828a6e5752fe3910ea162970baed78abde37f2db2523cb3b47a46a9b2bc2c2c828d6024913b9b52e43837bcd0a6df74be14eb593eb732351961ea33732ce5347281eeae 解密后:hello world
最后,有一点必须强调,因为中间磨了我不少时间。
中间加密后,如果要打印出来,必须以十六进制或者bcd码的形式打印,不能new string(byte[])后,再从这个string里getbytes(),也不要用base64,不然会破坏原数据。
比如,举个例子:
byte[ ] bytes=new byte[ ]{108, -56, 111, 34, -67}; byte[ ] newbytes=new string(bytes).getbytes(); stringbuffer sb=new stringbuffer(); for(int i=0; i<newbytes.length; i++){ sb.append(newbytes[i]+"|"); } system.out.println(sb.tostring());
将一个byte数组new string后再getbytes出来后,看看运行结果:
108|-56|111|34|63
最后一个byte由-67变为了63,这个务必注意啊~
总结
以上就是本文关于java加解密rsa使用方法代码示例的全部内容,希望对大家有所帮助。欢迎参阅:java大数字运算之biginteger 、java探索之thread+io文件的加密解密代码实例等,有什么问题可以留言指出,欢迎大家交流讨论。
上一篇: Servlet实现简单文件上传功能