最近为了分析一段请求流,不得不去研究一下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直接生成公钥和密钥,一般私钥保留给服务端,公钥交给客户端。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
|
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);
}
}
|
运行结果:
1
2
3
4
5
|
公钥: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来生成也可以,不过我觉得麻烦就不弄了。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
|
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);
}
}
|
运行结果
1
2
|
加密后:.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可以这样获取到,获取到后可以保存到本地文件中。
1
2
3
4
5
6
7
8
9
|
//获取公钥
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:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
|
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);
}
}
|
运行结果:
1
2
3
|
加密后: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为一组进行数据分割。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
|
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;
}
}
|
运行结果
1
2
|
加密后:0681CEA1051892E038CEB83EDCD5D549B61F31787FEF92123E0961F2E8FAF2590BAD88A2341C9684E6B3791D03D95BDFFF852EFD79B6748364A8A2369828A6E5752FE3910EA162970BAED78ABDE37F2DB2523CB3B47A46A9B2BC2C2C828D6024913B9B52E43837BCD0A6DF74BE14EB593EB732351961EA33732CE5347281EEAE
解密后:hello world
|
最后,有一点必须强调,因为中间磨了我不少时间。
中间加密后,如果要打印出来,必须以十六进制或者BCD码的形式打印,不能new String(byte[])后,再从这个String里getbytes(),也不要用base64,不然会破坏原数据。
比如,举个例子:
1
2
3
4
5
6
7
|
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出来后,看看运行结果:
1
|
108|-56|111|34|63
|
最后一个byte由-67变为了63,这个务必注意啊~
总结
以上就是本文关于java加解密RSA使用方法代码示例的全部内容,希望对大家有所帮助。有什么问题可以留言指出,欢迎大家交流讨论。
原文链接:http://blog.csdn.net/qq_18870023/article/details/52596808