本文實例講述了Java中RSA加密解密的實現方法。分享給大家供大家參考,具體如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
public static void main(String[] args) throws Exception { // TODO Auto-generated method stub HashMap<String, Object> map = RSAUtils.getKeys(); //生成公鑰和私鑰 RSAPublicKey publicKey = (RSAPublicKey) map.get( "public" ); RSAPrivateKey privateKey = (RSAPrivateKey) map.get( "private" ); //模 String modulus = publicKey.getModulus().toString(); //公鑰指數 String public_exponent = publicKey.getPublicExponent().toString(); //私鑰指數 String private_exponent = privateKey.getPrivateExponent().toString(); //明文 String ming = "123456789" ; //使用模和指數生成公鑰和私鑰 RSAPublicKey pubKey = RSAUtils.getPublicKey(modulus, public_exponent); RSAPrivateKey priKey = RSAUtils.getPrivateKey(modulus, private_exponent); //加密后的密文 String mi = RSAUtils.encryptByPublicKey(ming, pubKey); System.err.println(mi); //解密后的明文 ming = RSAUtils.decryptByPrivateKey(mi, priKey); System.err.println(ming); } |
RSAUtils.Java
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
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
|
package yyy.test.rsa; import java.math.BigInteger; import java.security.KeyFactory; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.NoSuchAlgorithmException; import java.security.interfaces.RSAPrivateKey; import java.security.interfaces.RSAPublicKey; import java.security.spec.RSAPrivateKeySpec; import java.security.spec.RSAPublicKeySpec; import java.util.HashMap; import javax.crypto.Cipher; public class RSAUtils { /** * 生成公鑰和私鑰 * @throws NoSuchAlgorithmException * */ public static HashMap<String, Object> getKeys() throws NoSuchAlgorithmException{ HashMap<String, Object> map = new HashMap<String, Object>(); KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance( "RSA" ); keyPairGen.initialize( 1024 ); KeyPair keyPair = keyPairGen.generateKeyPair(); RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic(); RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate(); map.put( "public" , publicKey); map.put( "private" , privateKey); return map; } /** * 使用模和指數生成RSA公鑰 * 注意:【此代碼用了默認補位方式,為RSA/None/PKCS1Padding,不同JDK默認的補位方式可能不同,如Android默認是RSA * /None/NoPadding】 * * @param modulus * 模 * @param exponent * 指數 * @return */ public static RSAPublicKey getPublicKey(String modulus, String exponent) { try { BigInteger b1 = new BigInteger(modulus); BigInteger b2 = new BigInteger(exponent); 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/None/PKCS1Padding,不同JDK默認的補位方式可能不同,如Android默認是RSA * /None/NoPadding】 * * @param modulus * 模 * @param exponent * 指數 * @return */ public static RSAPrivateKey getPrivateKey(String modulus, String exponent) { try { BigInteger b1 = new BigInteger(modulus); BigInteger b2 = new BigInteger(exponent); KeyFactory keyFactory = KeyFactory.getInstance( "RSA" ); RSAPrivateKeySpec keySpec = new RSAPrivateKeySpec(b1, b2); return (RSAPrivateKey) keyFactory.generatePrivate(keySpec); } catch (Exception e) { e.printStackTrace(); return null ; } } /** * 公鑰加密 * * @param data * @param publicKey * @return * @throws Exception */ public static String encryptByPublicKey(String data, RSAPublicKey publicKey) throws Exception { Cipher cipher = Cipher.getInstance( "RSA" ); cipher.init(Cipher.ENCRYPT_MODE, publicKey); // 模長 int key_len = publicKey.getModulus().bitLength() / 8 ; // 加密數據長度 <= 模長-11 String[] datas = splitString(data, key_len - 11 ); String mi = "" ; //如果明文長度大于模長-11則要分組加密 for (String s : datas) { mi += bcd2Str(cipher.doFinal(s.getBytes())); } return mi; } /** * 私鑰解密 * * @param data * @param privateKey * @return * @throws Exception */ public static String decryptByPrivateKey(String data, RSAPrivateKey privateKey) throws Exception { Cipher cipher = Cipher.getInstance( "RSA" ); cipher.init(Cipher.DECRYPT_MODE, privateKey); //模長 int key_len = privateKey.getModulus().bitLength() / 8 ; byte [] bytes = data.getBytes(); byte [] bcd = ASCII_To_BCD(bytes, bytes.length); System.err.println(bcd.length); //如果密文長度大于模長則要分組解密 String ming = "" ; byte [][] arrays = splitArray(bcd, key_len); for ( byte [] arr : arrays){ ming += new String(cipher.doFinal(arr)); } return ming; } /** * ASCII碼轉BCD碼 * */ public static byte [] ASCII_To_BCD( byte [] ascii, int asc_len) { byte [] bcd = new byte [asc_len / 2 ]; int j = 0 ; for ( int i = 0 ; i < (asc_len + 1 ) / 2 ; i++) { bcd[i] = asc_to_bcd(ascii[j++]); bcd[i] = ( byte ) (((j >= asc_len) ? 0x00 : asc_to_bcd(ascii[j++])) + (bcd[i] << 4 )); } return bcd; } public static byte asc_to_bcd( byte asc) { byte bcd; if ((asc >= '0' ) && (asc <= '9' )) bcd = ( byte ) (asc - '0' ); else if ((asc >= 'A' ) && (asc <= 'F' )) bcd = ( byte ) (asc - 'A' + 10 ); else if ((asc >= 'a' ) && (asc <= 'f' )) bcd = ( byte ) (asc - 'a' + 10 ); else bcd = ( byte ) (asc - 48 ); return bcd; } /** * BCD轉字符串 */ public static String bcd2Str( byte [] bytes) { char temp[] = new char [bytes.length * 2 ], val; for ( int i = 0 ; i < bytes.length; i++) { val = ( char ) (((bytes[i] & 0xf0 ) >> 4 ) & 0x0f ); temp[i * 2 ] = ( char ) (val > 9 ? val + 'A' - 10 : val + '0' ); val = ( char ) (bytes[i] & 0x0f ); temp[i * 2 + 1 ] = ( char ) (val > 9 ? val + 'A' - 10 : val + '0' ); } return new String(temp); } /** * 拆分字符串 */ public static String[] splitString(String string, int len) { int x = string.length() / len; int y = string.length() % len; int z = 0 ; if (y != 0 ) { z = 1 ; } String[] strings = new String[x + z]; String str = "" ; for ( int i= 0 ; i<x+z; i++) { if (i==x+z- 1 && y!= 0 ) { str = string.substring(i*len, i*len+y); } else { str = string.substring(i*len, i*len+len); } strings[i] = str; } return strings; } /** *拆分數組 */ public static byte [][] splitArray( byte [] data, int len){ int x = data.length / len; int y = data.length % len; int z = 0 ; if (y!= 0 ){ z = 1 ; } byte [][] arrays = new byte [x+z][]; byte [] arr; for ( int i= 0 ; i<x+z; i++){ arr = new byte [len]; if (i==x+z- 1 && y!= 0 ){ System.arraycopy(data, i*len, arr, 0 , y); } else { System.arraycopy(data, i*len, arr, 0 , len); } arrays[i] = arr; } return arrays; } } |
java
1
|
Cipher cipher = Cipher.getInstance( "RSA/ECB/PKCS1Padding" ); |
android
1
|
Cipher cipher = Cipher.getInstance( "RSA/ECB/NoPadding" ); |
參考:
http://stackoverflow.com/questions/6069369/rsa-encryption-difference-between-java-and-android
http://stackoverflow.com/questions/2956647/rsa-encrypt-with-base64-encoded-public-key-in-android
補充:關于RSA算法密鑰長度/密文長度/明文長度
1.密鑰長度
rsa算法初始化的時候一般要填入密鑰長度,在96-1024bits間
(1)為啥下限是96bits(12bytes)?因為加密1byte的明文,需要至少1+11=12bytes的密鑰(不懂?看下面的明文長度),低于下限96bits時,一個byte都加密不了,當然沒意義啦
(2)為啥上限是1024(128bytes)?這是算法本身決定的...當然如果某天網上出現了支持2048bits長的密鑰的rsa算法時,你當我廢話吧
2.明文長度
明文長度(bytes) <= 密鑰長度(bytes)-11.這樣的話,對于上限密鑰長度1024bits能加密的明文上限就是117bytes了.
這個規定很狗血,所以就出現了分片加密,網上很流行這個版本.很簡單,如果明文長度大于那個最大明文長度了,我就分片吧,保證每片都別超過那個值就是了.
片數=(明文長度(bytes)/(密鑰長度(bytes)-11))的整數部分+1,就是不滿一片的按一片算
3.密文長度
對,就是這個充滿了謠言,都說密文長度為密鑰長度的一半,經俺驗證,密文長度等于密鑰長度.當然這是不分片情況下的.
分片后,密文長度=密鑰長度*片數
例如96bits的密鑰,明文4bytes
每片明文長度=96/8-11=1byte,片數=4,密文長度=96/8*4=48bytes
又例如128bits的密鑰,明文8bytes
每片明文長度=128/8-11=5bytes,片數=8/5取整+1=2,密文長度=128/8*2=32
注意,對于指定長度的明文,其密文長度與密鑰長度非正比關系.如4bytes的明文,在最短密鑰96bites是,密文長度48bytes,128bits米密鑰時,密文長度為16bytes,1024bits密鑰時,密文長度128bytes.
因 為分片越多,密文長度顯然會變大,所以有人說,那就一直用1024bits的密鑰吧...拜托,現在的機器算1024bits的密鑰還是要點時間滴,別以 為你的cpu很牛逼...那么選個什么值比較合適呢?個人認為是600bits,因為我們對于一個字符串的加密,一般不是直接加密,而是將字符串hash 后,對hash值加密.現在的hash值一般都是4bytes,很少有8bytes,幾十年內應該也不會超過64bytes.那就用64bytes算吧, 密鑰長度就是(64+11)*8=600bits了.
用開源rsa算法的時候,還要注意,那個年代的人把long當4bytes用,如今 放在64位的機器上,就會死循環啊多悲催....因為有個循環里讓一個4bytes做遞減....64位機上long是8bytes,這個循環進去后個把 小時都出不來....所以要注意下哦....同理對于所有年代久遠的開源庫都得注意下...
希望本文所述對大家java程序設計有所幫助。