欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

java加解密RSA使用方法代码示例

程序员文章站 2024-04-02 10:17:52
最近为了分析一段请求流,不得不去研究一下rsa加密。 首先,强调一点:密钥的“钥”读“yue”,不是“yao”,额。。。 网上关于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弻脶諾晦5udq積嬨璼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秔豨wm麞衹靫〩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文件的加密解密代码实例等,有什么问题可以留言指出,欢迎大家交流讨论。