一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務(wù)器之家:專注于服務(wù)器技術(shù)及軟件下載分享
分類導(dǎo)航

PHP教程|ASP.NET教程|JAVA教程|ASP教程|編程技術(shù)|正則表達(dá)式|

服務(wù)器之家 - 編程語(yǔ)言 - JAVA教程 - Java OpenSSL生成的RSA公私鑰進(jìn)行數(shù)據(jù)加解密詳細(xì)介紹

Java OpenSSL生成的RSA公私鑰進(jìn)行數(shù)據(jù)加解密詳細(xì)介紹

2020-07-09 11:08java教程網(wǎng) JAVA教程

這篇文章主要介紹了Java OpenSSL生成的RSA公私鑰進(jìn)行數(shù)據(jù)加解密詳細(xì)介紹的相關(guān)資料,這里提供實(shí)例代碼及說(shuō)明具體如何實(shí)現(xiàn),需要的朋友可以參考下

 Java中使用OpenSSL生成的RSA公私鑰進(jìn)行數(shù)據(jù)加解密

 

RSA是什么:RSA公鑰加密算法是1977年由Ron Rivest、Adi Shamirh和LenAdleman在(美國(guó)麻省理工學(xué)院)開發(fā)的。RSA取名來(lái)自開發(fā)他們?nèi)叩拿帧SA是目前最有影響力的公鑰加密算法,它能夠抵抗到目前為止已知的所有密碼攻擊,已被ISO推薦為公鑰數(shù)據(jù)加密標(biāo)準(zhǔn)。目前該加密方式廣泛用于網(wǎng)上銀行、數(shù)字簽名等場(chǎng)合。RSA算法基于一個(gè)十分簡(jiǎn)單的數(shù)論事實(shí):將兩個(gè)大素?cái)?shù)相乘十分容易,但那時(shí)想要對(duì)其乘積進(jìn)行因式分解卻極其困難,因此可以將乘積公開作為加密密鑰。

OpenSSL是什么:眾多的密碼算法、公鑰基礎(chǔ)設(shè)施標(biāo)準(zhǔn)以及SSL協(xié)議,或許這些有趣的功能會(huì)讓你產(chǎn)生實(shí)現(xiàn)所有這些算法和標(biāo)準(zhǔn)的想法。果真如此,在對(duì)你表示敬佩的同時(shí),還是忍不住提醒你:這是一個(gè)令人望而生畏的過程。這個(gè)工作不再是簡(jiǎn)單的讀懂幾本密碼學(xué)專著和協(xié)議文檔那么簡(jiǎn)單,而是要理解所有這些算法、標(biāo)準(zhǔn)和協(xié)議文檔的每一個(gè)細(xì)節(jié),并用你可能很熟悉的C語(yǔ)言字符一個(gè)一個(gè)去實(shí)現(xiàn)這些定義和過程。我們不知道你將需要多少時(shí)間來(lái)完成這項(xiàng)有趣而可怕的工作,但肯定不是一年兩年的問題。OpenSSL就是由Eric A. Young和Tim J. Hudson兩位絕世大好人自1995年就開始編寫的集合眾多安全算法的算法集合。通過命令或者開發(fā)庫(kù),我們可以輕松實(shí)現(xiàn)標(biāo)準(zhǔn)的公開算法應(yīng)用。

我的一個(gè)假設(shè)應(yīng)用背景:

隨著移動(dòng)互聯(lián)網(wǎng)的普及,為移動(dòng)設(shè)備開發(fā)的應(yīng)用也層出不窮。這些應(yīng)用往往伴隨著用戶注冊(cè)與密碼驗(yàn)證的功能。”網(wǎng)絡(luò)傳輸“、”應(yīng)用程序日志訪問“中的安全性都存在著隱患。密碼作為用戶的敏感數(shù)據(jù),特別需要開發(fā)者在應(yīng)用上線之前做好安全防范。處理不當(dāng),可能會(huì)造成諸如商業(yè)競(jìng)爭(zhēng)對(duì)手的惡意攻擊、第三方合作商的訴訟等問題。

RSA算法雖然有這么多好處,但是在網(wǎng)上找不到一個(gè)完整的例子來(lái)說(shuō)明如何操作。下面我就來(lái)介紹一下:

一、使用OpenSSL來(lái)生成私鑰和公鑰

我使用的是Linux系統(tǒng),已經(jīng)安裝了OpenSSL軟件包,此時(shí)請(qǐng)驗(yàn)證你的機(jī)器上已經(jīng)安裝了OpenSSL,運(yùn)行命令應(yīng)當(dāng)出現(xiàn)如下信息:

?
1
2
3
4
5
6
7
8
[root@chaijunkun ~]# openssl version -a
OpenSSL 1.0.0-fips 29 Mar 2010
built on: Wed Jan 25 02:17:15 GMT 2012
platform: linux-x86_64
options: bn(64,64) md2(int) rc4(16x,int) des(idx,cisc,16,int) blowfish(idx) 
compiler: gcc -fPIC -DOPENSSL_PIC -DZLIB -DOPENSSL_THREADS -D_REENTRANT -DDSO_DLFCN -DHAVE_DLFCN_H -DKRB5_MIT -m64 -DL_ENDIAN -DTERMIO -Wall -O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector --param=ssp-buffer-size=4 -m64 -mtune=generic -Wa,--noexecstack -DMD32_REG_T=int -DOPENSSL_IA32_SSE2 -DOPENSSL_BN_ASM_MONT -DSHA1_ASM -DSHA256_ASM -DSHA512_ASM -DMD5_ASM -DAES_ASM -DWHIRLPOOL_ASM
OPENSSLDIR: "/etc/pki/tls"
engines: aesni dynamic 

先來(lái)生成私鑰:

?
1
2
3
4
5
[root@chaijunkun ~]# openssl genrsa -out rsa_private_key.pem 1024
Generating RSA private key, 1024 bit long modulus
.......................++++++
..++++++
e is 65537 (0x10001)

這條命令讓openssl隨機(jī)生成了一份私鑰,加密長(zhǎng)度是1024位。加密長(zhǎng)度是指理論上最大允許”被加密的信息“長(zhǎng)度的限制,也就是明文的長(zhǎng)度限制。隨著這個(gè)參數(shù)的增大(比方說(shuō)2048),允許的明文長(zhǎng)度也會(huì)增加,但同時(shí)也會(huì)造成計(jì)算復(fù)雜度的極速增長(zhǎng)。一般推薦的長(zhǎng)度就是1024位(128字節(jié))。

我們來(lái)看一下私鑰的內(nèi)容:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[root@chaijunkun ~]# cat rsa_private_key.pem 
-----BEGIN RSA PRIVATE KEY-----
MIICWwIBAAKBgQChDzcjw/rWgFwnxunbKp7/4e8w/UmXx2jk6qEEn69t6N2R1i/L
mcyDT1xr/T2AHGOiXNQ5V8W4iCaaeNawi7aJaRhtVx1uOH/2U378fscEESEG8XDq
ll0GCfB1/TjKI2aitVSzXOtRs8kYgGU78f7VmDNgXIlk3gdhnzh+uoEQywIDAQAB
AoGAaeKk76CSsp7k90mwyWP18GhLZru+vEhfT9BpV67cGLg1owFbntFYQSPVsTFm
U2lWn5HD/IcV+EGaj4fOLXdM43Kt4wyznoABSZCKKxs6uRciu8nQaFNUy4xVeOfX
PHU2TE7vi4LDkw9df1fya+DScSLnaDAUN3OHB5jqGL+Ls5ECQQDUfuxXN3uqGYKk
znrKj0j6pY27HRfROMeHgxbjnnApCQ71SzjqAM77R3wIlKfh935OIV0aQC4jQRB4
iHYSLl9lAkEAwgh4jxxXeIAufMsgjOi3qpJqGvumKX0W96McpCwV3Fsew7W1/msi
suTkJp5BBvjFvFwfMAHYlJdP7W+nEBWkbwJAYbz/eB5NAzA4pxVR5VmCd8cuKaJ4
EgPLwsjI/mkhrb484xZ2VyuICIwYwNmfXpA3yDgQWsKqdgy3Rrl9lV8/AQJAcjLi
IfigUr++nJxA8C4Xy0CZSoBJ76k710wdE1MPGr5WgQF1t+P+bCPjVAdYZm4Mkyv0
/yBXBD16QVixjvnt6QJABli6Zx9GYRWnu6AKpDAHd8QjWOnnNfNLQHue4WepEvkm
CysG+IBs2GgsXNtrzLWJLFx7VHmpqNTTC8yNmX1KFw==
-----END RSA PRIVATE KEY-----

內(nèi)容都是標(biāo)準(zhǔn)的ASCII字符,開頭一行和結(jié)尾一行有明顯的標(biāo)記,真正的私鑰數(shù)據(jù)是中間的不規(guī)則字符。

2015年3月24日補(bǔ)充:密鑰文件最終將數(shù)據(jù)通過Base64編碼進(jìn)行存儲(chǔ)。可以看到上述密鑰文件內(nèi)容每一行的長(zhǎng)度都很規(guī)律。這是由于RFC2045中規(guī)定:The encoded output stream must be represented in lines of no more than 76 characters each。也就是說(shuō)Base64編碼的數(shù)據(jù)每行最多不超過76字符,對(duì)于超長(zhǎng)數(shù)據(jù)需要按行分割。

接下來(lái)根據(jù)私鑰生成公鑰:

?
1
2
[root@chaijunkun ~]# openssl rsa -in rsa_private_key.pem -out rsa_public_key.pem -pubout
 writing RSA key 

再來(lái)看一下公鑰的內(nèi)容:

?
1
2
3
4
5
6
7
[root@chaijunkun ~]# cat rsa_public_ley.pem 
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQChDzcjw/rWgFwnxunbKp7/4e8w
/UmXx2jk6qEEn69t6N2R1i/LmcyDT1xr/T2AHGOiXNQ5V8W4iCaaeNawi7aJaRht
Vx1uOH/2U378fscEESEG8XDqll0GCfB1/TjKI2aitVSzXOtRs8kYgGU78f7VmDNg
XIlk3gdhnzh+uoEQywIDAQAB
-----END PUBLIC KEY-----

這時(shí)候的私鑰還不能直接被使用,需要進(jìn)行PKCS#8編碼:

?
1
[root@chaijunkun ~]# openssl pkcs8 -topk8 -in rsa_private_key.pem -out pkcs8_rsa_private_key.pem -nocrypt

命令中指明了輸入私鑰文件為rsa_private_key.pem,輸出私鑰文件為pkcs8_rsa_private_key.pem,不采用任何二次加密(-nocrypt)

再來(lái)看一下,編碼后的私鑰文件是不是和之前的私鑰文件不同了:

?
1
[root@chaijunkun ~]# cat pkcs8_rsa_private_key.pem
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
-----BEGIN PRIVATE KEY-----
MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAKEPNyPD+taAXCfG
6dsqnv/h7zD9SZfHaOTqoQSfr23o3ZHWL8uZzINPXGv9PYAcY6Jc1DlXxbiIJpp4
1rCLtolpGG1XHW44f/ZTfvx+xwQRIQbxcOqWXQYJ8HX9OMojZqK1VLNc61GzyRiA
ZTvx/tWYM2BciWTeB2GfOH66gRDLAgMBAAECgYBp4qTvoJKynuT3SbDJY/XwaEtm
u768SF9P0GlXrtwYuDWjAVue0VhBI9WxMWZTaVafkcP8hxX4QZqPh84td0zjcq3j
DLOegAFJkIorGzq5FyK7ydBoU1TLjFV459c8dTZMTu+LgsOTD11/V/Jr4NJxIudo
MBQ3c4cHmOoYv4uzkQJBANR+7Fc3e6oZgqTOesqPSPqljbsdF9E4x4eDFuOecCkJ
DvVLOOoAzvtHfAiUp+H3fk4hXRpALiNBEHiIdhIuX2UCQQDCCHiPHFd4gC58yyCM
6Leqkmoa+6YpfRb3oxykLBXcWx7DtbX+ayKy5OQmnkEG+MW8XB8wAdiUl0/tb6cQ
FaRvAkBhvP94Hk0DMDinFVHlWYJ3xy4pongSA8vCyMj+aSGtvjzjFnZXK4gIjBjA
2Z9ekDfIOBBawqp2DLdGuX2VXz8BAkByMuIh+KBSv76cnEDwLhfLQJlKgEnvqTvX
TB0TUw8avlaBAXW34/5sI+NUB1hmbgyTK/T/IFcEPXpBWLGO+e3pAkAGWLpnH0Zh
Fae7oAqkMAd3xCNY6ec180tAe57hZ6kS+SYLKwb4gGzYaCxc22vMtYksXHtUeamo
1NMLzI2ZfUoX
-----END PRIVATE KEY-----

至此,可用的密鑰對(duì)已經(jīng)生成好了,私鑰使用pkcs8_rsa_private_key.pem,公鑰采用rsa_public_key.pem。

2014年5月20日補(bǔ)充:最近又遇到RSA加密的需求了,而且對(duì)方要求只能使用第一步生成的未經(jīng)過PKCS#8編碼的私鑰文件。后來(lái)查看相關(guān)文獻(xiàn)得知第一步生成的私鑰文件編碼是PKCS#1格式,這種格式Java其實(shí)是支持的,只不過多寫兩行代碼而已:

?
1
2
3
4
RSAPrivateKeyStructure asn1PrivKey = new RSAPrivateKeyStructure((ASN1Sequence) ASN1Sequence.fromByteArray(priKeyData));
RSAPrivateKeySpec rsaPrivKeySpec = new RSAPrivateKeySpec(asn1PrivKey.getModulus(), asn1PrivKey.getPrivateExponent());
KeyFactory keyFactory= KeyFactory.getInstance("RSA");
PrivateKey priKey= keyFactory.generatePrivate(rsaPrivKeySpec);

首先將PKCS#1的私鑰文件讀取出來(lái)(注意去掉減號(hào)開頭的注釋內(nèi)容),然后使用Base64解碼讀出的字符串,便得到priKeyData,也就是第一行代碼中的參數(shù)。最后一行得到了私鑰。接下來(lái)的用法就沒什么區(qū)別了。
參考文獻(xiàn):https://community.Oracle.com/thread/1529240?start=0&tstart=0

二、編寫Java代碼實(shí)際測(cè)試

2012年2月23日補(bǔ)充:在標(biāo)準(zhǔn)JDK中只是規(guī)定了JCE(JCE (Java Cryptography Extension) 是一組包,它們提供用于加密、密鑰生成和協(xié)商以及 Message Authentication Code(MAC)算法的框架和實(shí)現(xiàn)。它提供對(duì)對(duì)稱、不對(duì)稱、塊和流密碼的加密支持,它還支持安全流和密封的對(duì)象。)接口,但是內(nèi)部實(shí)現(xiàn)需要自己或者第三方提供。因此我們這里使用bouncycastle的開源的JCE實(shí)現(xiàn)包,下載地址:http://bouncycastle.org/latest_releases.html,我使用的是bcprov-jdk16-146.jar,這是在JDK1.6環(huán)境下使用的。如果需要其他JDK版本下的實(shí)現(xiàn),可以在之前的下載頁(yè)面中找到對(duì)應(yīng)版本。

下面來(lái)看一下我實(shí)現(xiàn)的代碼:

?
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
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
package net.csdn.blog.chaijunkun;
 
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.security.InvalidKeyException;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
 
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
 
import org.bouncycastle.jce.provider.BouncyCastleProvider;
 
import sun.misc.BASE64Decoder;
 
public class RSAEncrypt {
   
  private static final String DEFAULT_PUBLIC_KEY= 
    "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQChDzcjw/rWgFwnxunbKp7/4e8w" + "\r" +
    "/UmXx2jk6qEEn69t6N2R1i/LmcyDT1xr/T2AHGOiXNQ5V8W4iCaaeNawi7aJaRht" + "\r" +
    "Vx1uOH/2U378fscEESEG8XDqll0GCfB1/TjKI2aitVSzXOtRs8kYgGU78f7VmDNg" + "\r" +
    "XIlk3gdhnzh+uoEQywIDAQAB" + "\r";
   
  private static final String DEFAULT_PRIVATE_KEY=
    "MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAKEPNyPD+taAXCfG" + "\r" +
    "6dsqnv/h7zD9SZfHaOTqoQSfr23o3ZHWL8uZzINPXGv9PYAcY6Jc1DlXxbiIJpp4" + "\r" +
    "1rCLtolpGG1XHW44f/ZTfvx+xwQRIQbxcOqWXQYJ8HX9OMojZqK1VLNc61GzyRiA" + "\r" +
    "ZTvx/tWYM2BciWTeB2GfOH66gRDLAgMBAAECgYBp4qTvoJKynuT3SbDJY/XwaEtm" + "\r" +
    "u768SF9P0GlXrtwYuDWjAVue0VhBI9WxMWZTaVafkcP8hxX4QZqPh84td0zjcq3j" + "\r" +
    "DLOegAFJkIorGzq5FyK7ydBoU1TLjFV459c8dTZMTu+LgsOTD11/V/Jr4NJxIudo" + "\r" +
    "MBQ3c4cHmOoYv4uzkQJBANR+7Fc3e6oZgqTOesqPSPqljbsdF9E4x4eDFuOecCkJ" + "\r" +
    "DvVLOOoAzvtHfAiUp+H3fk4hXRpALiNBEHiIdhIuX2UCQQDCCHiPHFd4gC58yyCM" + "\r" +
    "6Leqkmoa+6YpfRb3oxykLBXcWx7DtbX+ayKy5OQmnkEG+MW8XB8wAdiUl0/tb6cQ" + "\r" +
    "FaRvAkBhvP94Hk0DMDinFVHlWYJ3xy4pongSA8vCyMj+aSGtvjzjFnZXK4gIjBjA" + "\r" +
    "2Z9ekDfIOBBawqp2DLdGuX2VXz8BAkByMuIh+KBSv76cnEDwLhfLQJlKgEnvqTvX" + "\r" +
    "TB0TUw8avlaBAXW34/5sI+NUB1hmbgyTK/T/IFcEPXpBWLGO+e3pAkAGWLpnH0Zh" + "\r" +
    "Fae7oAqkMAd3xCNY6ec180tAe57hZ6kS+SYLKwb4gGzYaCxc22vMtYksXHtUeamo" + "\r" +
    "1NMLzI2ZfUoX" + "\r";
 
  /**
   * 私鑰
   */
  private RSAPrivateKey privateKey;
 
  /**
   * 公鑰
   */
  private RSAPublicKey publicKey;
   
  /**
   * 字節(jié)數(shù)據(jù)轉(zhuǎn)字符串專用集合
   */
  private static final char[] HEX_CHAR= {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
   
 
  /**
   * 獲取私鑰
   * @return 當(dāng)前的私鑰對(duì)象
   */
  public RSAPrivateKey getPrivateKey() {
    return privateKey;
  }
 
  /**
   * 獲取公鑰
   * @return 當(dāng)前的公鑰對(duì)象
   */
  public RSAPublicKey getPublicKey() {
    return publicKey;
  }
 
  /**
   * 隨機(jī)生成密鑰對(duì)
   */
  public void genKeyPair(){
    KeyPairGenerator keyPairGen= null;
    try {
      keyPairGen= KeyPairGenerator.getInstance("RSA");
    } catch (NoSuchAlgorithmException e) {
      e.printStackTrace();
    }
    keyPairGen.initialize(1024, new SecureRandom());
    KeyPair keyPair= keyPairGen.generateKeyPair();
    this.privateKey= (RSAPrivateKey) keyPair.getPrivate();
    this.publicKey= (RSAPublicKey) keyPair.getPublic();
  }
 
  /**
   * 從文件中輸入流中加載公鑰
   * @param in 公鑰輸入流
   * @throws Exception 加載公鑰時(shí)產(chǎn)生的異常
   */
  public void loadPublicKey(InputStream in) throws Exception{
    try {
      BufferedReader br= new BufferedReader(new InputStreamReader(in));
      String readLine= null;
      StringBuilder sb= new StringBuilder();
      while((readLine= br.readLine())!=null){
        if(readLine.charAt(0)=='-'){
          continue;
        }else{
          sb.append(readLine);
          sb.append('\r');
        }
      }
      loadPublicKey(sb.toString());
    } catch (IOException e) {
      throw new Exception("公鑰數(shù)據(jù)流讀取錯(cuò)誤");
    } catch (NullPointerException e) {
      throw new Exception("公鑰輸入流為空");
    }
  }
 
 
  /**
   * 從字符串中加載公鑰
   * @param publicKeyStr 公鑰數(shù)據(jù)字符串
   * @throws Exception 加載公鑰時(shí)產(chǎn)生的異常
   */
  public void loadPublicKey(String publicKeyStr) throws Exception{
    try {
      BASE64Decoder base64Decoder= new BASE64Decoder();
      byte[] buffer= base64Decoder.decodeBuffer(publicKeyStr);
      KeyFactory keyFactory= KeyFactory.getInstance("RSA");
      X509EncodedKeySpec keySpec= new X509EncodedKeySpec(buffer);
      this.publicKey= (RSAPublicKey) keyFactory.generatePublic(keySpec);
    } catch (NoSuchAlgorithmException e) {
      throw new Exception("無(wú)此算法");
    } catch (InvalidKeySpecException e) {
      throw new Exception("公鑰非法");
    } catch (IOException e) {
      throw new Exception("公鑰數(shù)據(jù)內(nèi)容讀取錯(cuò)誤");
    } catch (NullPointerException e) {
      throw new Exception("公鑰數(shù)據(jù)為空");
    }
  }
 
  /**
   * 從文件中加載私鑰
   * @param keyFileName 私鑰文件名
   * @return 是否成功
   * @throws Exception
   */
  public void loadPrivateKey(InputStream in) throws Exception{
    try {
      BufferedReader br= new BufferedReader(new InputStreamReader(in));
      String readLine= null;
      StringBuilder sb= new StringBuilder();
      while((readLine= br.readLine())!=null){
        if(readLine.charAt(0)=='-'){
          continue;
        }else{
          sb.append(readLine);
          sb.append('\r');
        }
      }
      loadPrivateKey(sb.toString());
    } catch (IOException e) {
      throw new Exception("私鑰數(shù)據(jù)讀取錯(cuò)誤");
    } catch (NullPointerException e) {
      throw new Exception("私鑰輸入流為空");
    }
  }
 
  public void loadPrivateKey(String privateKeyStr) throws Exception{
    try {
      BASE64Decoder base64Decoder= new BASE64Decoder();
      byte[] buffer= base64Decoder.decodeBuffer(privateKeyStr);
      PKCS8EncodedKeySpec keySpec= new PKCS8EncodedKeySpec(buffer);
      KeyFactory keyFactory= KeyFactory.getInstance("RSA");
      this.privateKey= (RSAPrivateKey) keyFactory.generatePrivate(keySpec);
    } catch (NoSuchAlgorithmException e) {
      throw new Exception("無(wú)此算法");
    } catch (InvalidKeySpecException e) {
      throw new Exception("私鑰非法");
    } catch (IOException e) {
      throw new Exception("私鑰數(shù)據(jù)內(nèi)容讀取錯(cuò)誤");
    } catch (NullPointerException e) {
      throw new Exception("私鑰數(shù)據(jù)為空");
    }
  }
 
  /**
   * 加密過程
   * @param publicKey 公鑰
   * @param plainTextData 明文數(shù)據(jù)
   * @return
   * @throws Exception 加密過程中的異常信息
   */
  public byte[] encrypt(RSAPublicKey publicKey, byte[] plainTextData) throws Exception{
    if(publicKey== null){
      throw new Exception("加密公鑰為空, 請(qǐng)?jiān)O(shè)置");
    }
    Cipher cipher= null;
    try {
      cipher= Cipher.getInstance("RSA", new BouncyCastleProvider());
      cipher.init(Cipher.ENCRYPT_MODE, publicKey);
      byte[] output= cipher.doFinal(plainTextData);
      return output;
    } catch (NoSuchAlgorithmException e) {
      throw new Exception("無(wú)此加密算法");
    } catch (NoSuchPaddingException e) {
      e.printStackTrace();
      return null;
    }catch (InvalidKeyException e) {
      throw new Exception("加密公鑰非法,請(qǐng)檢查");
    } catch (IllegalBlockSizeException e) {
      throw new Exception("明文長(zhǎng)度非法");
    } catch (BadPaddingException e) {
      throw new Exception("明文數(shù)據(jù)已損壞");
    }
  }
 
  /**
   * 解密過程
   * @param privateKey 私鑰
   * @param cipherData 密文數(shù)據(jù)
   * @return 明文
   * @throws Exception 解密過程中的異常信息
   */
  public byte[] decrypt(RSAPrivateKey privateKey, byte[] cipherData) throws Exception{
    if (privateKey== null){
      throw new Exception("解密私鑰為空, 請(qǐng)?jiān)O(shè)置");
    }
    Cipher cipher= null;
    try {
      cipher= Cipher.getInstance("RSA", new BouncyCastleProvider());
      cipher.init(Cipher.DECRYPT_MODE, privateKey);
      byte[] output= cipher.doFinal(cipherData);
      return output;
    } catch (NoSuchAlgorithmException e) {
      throw new Exception("無(wú)此解密算法");
    } catch (NoSuchPaddingException e) {
      e.printStackTrace();
      return null;
    }catch (InvalidKeyException e) {
      throw new Exception("解密私鑰非法,請(qǐng)檢查");
    } catch (IllegalBlockSizeException e) {
      throw new Exception("密文長(zhǎng)度非法");
    } catch (BadPaddingException e) {
      throw new Exception("密文數(shù)據(jù)已損壞");
    }    
  }
 
   
  /**
   * 字節(jié)數(shù)據(jù)轉(zhuǎn)十六進(jìn)制字符串
   * @param data 輸入數(shù)據(jù)
   * @return 十六進(jìn)制內(nèi)容
   */
  public static String byteArrayToString(byte[] data){
    StringBuilder stringBuilder= new StringBuilder();
    for (int i=0; i<data.length; i++){
      //取出字節(jié)的高四位 作為索引得到相應(yīng)的十六進(jìn)制標(biāo)識(shí)符 注意無(wú)符號(hào)右移
      stringBuilder.append(HEX_CHAR[(data[i] & 0xf0)>>> 4]);
      //取出字節(jié)的低四位 作為索引得到相應(yīng)的十六進(jìn)制標(biāo)識(shí)符
      stringBuilder.append(HEX_CHAR[(data[i] & 0x0f)]);
      if (i<data.length-1){
        stringBuilder.append(' ');
      }
    }
    return stringBuilder.toString();
  }
 
 
  public static void main(String[] args){
    RSAEncrypt rsaEncrypt= new RSAEncrypt();
    //rsaEncrypt.genKeyPair();
 
    //加載公鑰
    try {
      rsaEncrypt.loadPublicKey(RSAEncrypt.DEFAULT_PUBLIC_KEY);
      System.out.println("加載公鑰成功");
    } catch (Exception e) {
      System.err.println(e.getMessage());
      System.err.println("加載公鑰失敗");
    }
 
    //加載私鑰
    try {
      rsaEncrypt.loadPrivateKey(RSAEncrypt.DEFAULT_PRIVATE_KEY);
      System.out.println("加載私鑰成功");
    } catch (Exception e) {
      System.err.println(e.getMessage());
      System.err.println("加載私鑰失敗");
    }
 
    //測(cè)試字符串
    String encryptStr= "Test String chaijunkun";
 
    try {
      //加密
      byte[] cipher = rsaEncrypt.encrypt(rsaEncrypt.getPublicKey(), encryptStr.getBytes());
      //解密
      byte[] plainText = rsaEncrypt.decrypt(rsaEncrypt.getPrivateKey(), cipher);
      System.out.println("密文長(zhǎng)度:"+ cipher.length);
      System.out.println(RSAEncrypt.byteArrayToString(cipher));
      System.out.println("明文長(zhǎng)度:"+ plainText.length);
      System.out.println(RSAEncrypt.byteArrayToString(plainText));
      System.out.println(new String(plainText));
    } catch (Exception e) {
      System.err.println(e.getMessage());
    }
  }
}

代碼中我提供了兩種加載公鑰和私鑰的方式。
按流來(lái)讀?。哼m合在Android應(yīng)用中按ID索引資源得到InputStream的方式;
按字符串來(lái)讀取:就像代碼中展示的那樣,將密鑰內(nèi)容按行存儲(chǔ)到靜態(tài)常量中,按String類型導(dǎo)入密鑰。

運(yùn)行上面的代碼,會(huì)顯示如下信息:

?
1
2
3
4
5
6
7
加載公鑰成功
加載私鑰成功
密文長(zhǎng)度:128
35 b4 6f 49 69 ae a3 85 a2 a5 0d 45 75 00 23 23 e6 70 69 b4 59 ae 72 6f 6d d3 43 e1 d3 44 85 eb 04 57 2c 46 3e 70 09 4d e6 4c 83 50 c7 56 75 80 c7 e1 31 64 57 c8 e3 46 a7 ce 57 31 ac cd 21 89 89 8f c1 24 c1 22 0c cb 70 6a 0d fa c9 38 80 ba 2e e1 29 02 ed 45 9e 88 e9 23 09 87 af ad ab ac cb 61 03 3c a1 81 56 a5 de c4 79 aa 3e 48 ee 30 3d bc 5b 47 50 75 9f fd 22 87 9e de b1 f4 e8 b2
明文長(zhǎng)度:22
54 65 73 74 20 53 74 72 69 6e 67 20 63 68 61 69 6a 75 6e 6b 75 6e
Test String chaijunkun

在main函數(shù)中我注釋掉了”rsaEncrypt.genKeyPair()“,這個(gè)方法是用來(lái)隨機(jī)生成密鑰對(duì)的(只生成、使用,不存儲(chǔ))。當(dāng)不使用文件密鑰時(shí),可以將載入密鑰的代碼注釋,啟用本方法,也可以跑通代碼。
加載公鑰與加載私鑰的不同點(diǎn)在于公鑰加載時(shí)使用的是X509EncodedKeySpec(X509編碼的Key指令),私鑰加載時(shí)使用的是PKCS8EncodedKeySpec(PKCS#8編碼的Key指令)。

2012年2月22日補(bǔ)充:在android軟件開發(fā)的過程中,發(fā)現(xiàn)上述代碼不能正常工作,主要原因在于sun.misc.BASE64Decoder類在android開發(fā)包中不存在。因此需要特別在網(wǎng)上尋找rt.jar的源代碼,至于JDK的src.zip中的源代碼,這個(gè)只是JDK中的部分源代碼,上述的幾個(gè)類的代碼都沒有。經(jīng)過尋找并添加,上述代碼在android應(yīng)用中能夠很好地工作。其中就包含這個(gè)類的對(duì)應(yīng)代碼。另外此類還依賴于CEFormatException、CEStreamExhausted、CharacterDecoder和CharacterEncoder類和異常定義。

2012年2月23日補(bǔ)充:起初,我寫這篇文章是想不依賴于任何第三方包來(lái)實(shí)現(xiàn)RSA的加密與解密,然而后續(xù)遇到了問題。由于在加密方法encrypt和解密方法decrypt中都要建立一個(gè)Cipher對(duì)象,這個(gè)對(duì)象只能通過getInstance來(lái)獲取實(shí)例。它有兩種:第一個(gè)是只指定算法,不指定提供者Provider的;第二個(gè)是兩個(gè)都要指定的。起初沒有指定,代碼依然能夠跑通,但是你會(huì)發(fā)現(xiàn),每次加密的結(jié)果都不一樣。后來(lái)分析才知道Cipher對(duì)象使用的公私鑰是內(nèi)部自己隨機(jī)生成的,不是代碼中指定的公私鑰。奇怪的是,這種不指定Provider的代碼能夠在android應(yīng)用中跑通,而且每次加密的結(jié)果都相同。我想,android的SDK中除了系統(tǒng)的一些開發(fā)函數(shù)外,自己也實(shí)現(xiàn)了JDK的功能,可能在它自己的JDK中已經(jīng)提供了相應(yīng)的Provider,才使得每次加密結(jié)果相同。當(dāng)我像網(wǎng)上的示例代碼那樣加入了bouncycastle的Provider后,果然每次加密的結(jié)果都相同了。

感謝閱讀,希望能幫助到大家,謝謝大家對(duì)本站的支持!

原文鏈接:http://blog.csdn.net/chaijunkun/article/details/7275632

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 动漫美丽妇人1~2在线看 | 亚洲性爱区 | 精品麻豆| 国产精品久久久久不卡绿巨人 | 精品视频手机在线观看免费 | 嫩草视频在线观看视频播放 | 精品区2区3区4区产品乱码9 | 高h折磨调教古代 | 偷拍自拍校园春色 | 精品视频一区二区三区免费 | 免费精品国产在线观看 | 日本高清在线观看天码888 | 日本护士厕所xxx | 亚洲日本va中文字幕 | 免费午夜剧场 | 欧美激情综合 | 污到你怀疑人生 | 69日本xxxhd | 美女的让男人桶爽免费看 | 我将她侵犯1~6樱花动漫在线看 | 免费被靠视频动漫 | 日韩欧美高清 | 含羞草传媒网站免费进入欢迎 | 青青青在线观看国产精品 | 国产精品一区二区三区免费视频 | 深夜免费在线视频 | 亚洲国产成人精品无码区APP | 蜜桃传媒在线 | 国产高清路线一路线二2022 | 91精品国产91久久久久久 | 日本中文字幕在线观看视频 | 秋葵丝瓜茄子草莓榴莲樱桃 | 亚洲大片免费观看 | 男插女的下面免费视频夜色 | 九九99香蕉在线视频免费 | 青草热久精品视频在线观看 | 日本aaaaa高清免费看 | 欧美在线欧美 | 热99精品只有里视频最新 | 女生被草 | 99久久精品在免费线18 |