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
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
|
package com.ylsoft.cert; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.security.InvalidKeyException; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.KeyStore; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; import java.security.PrivateKey; import java.security.SecureRandom; import java.security.Signature; import java.security.SignatureException; import java.security.UnrecoverableKeyException; import java.security.cert.Certificate; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.util.Date; import java.util.Vector; import sun.misc.BASE64Encoder; import sun.security.util.ObjectIdentifier; import sun.security.x509.AlgorithmId; import sun.security.x509.CertAndKeyGen; import sun.security.x509.CertificateAlgorithmId; import sun.security.x509.CertificateExtensions; import sun.security.x509.CertificateSerialNumber; import sun.security.x509.CertificateValidity; import sun.security.x509.CertificateVersion; import sun.security.x509.CertificateX509Key; import sun.security.x509.ExtendedKeyUsageExtension; import sun.security.x509.Extension; import sun.security.x509.KeyIdentifier; import sun.security.x509.KeyUsageExtension; import sun.security.x509.SubjectKeyIdentifierExtension; import sun.security.x509.X500Name; import sun.security.x509.X500Signer; import sun.security.x509.X509CertImpl; import sun.security.x509.X509CertInfo; /** * 首先生成CA的根證書,然后有CA的根證書簽署生成ScriptX的證書 * * @author Administrator * */ public class GenX509Cert { /** 提供強加密隨機數生成器 (RNG)* */ private SecureRandom sr; public GenX509Cert() throws NoSuchAlgorithmException, NoSuchProviderException { // 返回實現指定隨機數生成器 (RNG) 算法的 SecureRandom 對象。 sr = SecureRandom.getInstance( "SHA1PRNG" , "SUN" ); } public void createCert(X509Certificate certificate, PrivateKey rootPrivKey, KeyPair kp) throws CertificateException, IOException, InvalidKeyException, NoSuchAlgorithmException, NoSuchProviderException, SignatureException { // X.509 v1 證書的抽象類。此類提供了一種訪問 X.509 v1 證書所有屬性的標準方式。 byte certbytes[] = certificate.getEncoded(); // The X509CertImpl class represents an X.509 certificate. X509CertImpl x509certimpl = new X509CertImpl(certbytes); // The X509CertInfo class represents X.509 certificate information. X509CertInfo x509certinfo = (X509CertInfo) x509certimpl .get( "x509.info" ); // This class defines the X509Key attribute for the Certificate. x509certinfo.set( "key" , new CertificateX509Key(kp.getPublic())); // This class defines the Extensions attribute for the Certificate CertificateExtensions certificateextensions = new CertificateExtensions(); certificateextensions.set( "SubjectKeyIdentifier" , new SubjectKeyIdentifierExtension(( new KeyIdentifier(kp .getPublic())).getIdentifier())); x509certinfo.set( "extensions" , certificateextensions); // 設置issuer域 X500Name issuer = new X500Name( "CN=RootCA,OU=hackwp,O=wp,L=BJ,S=BJ,C=CN" ); x509certinfo.set( "issuer.dname" , issuer); // Constructs a name from a conventionally formatted string, such as // "CN=Dave, OU=JavaSoft, O=Sun Microsystems, C=US". (RFC 1779 or RFC // 2253 style). X500Name subject = new X500Name( "CN=scriptx, OU=wps, O=wps, L=BJ, ST=BJ, C=CN" ); x509certinfo.set( "subject.dname" , subject); // 此 Signature 類用來為應用程序提供數字簽名算法功能。返回實現指定簽名算法的 Signature 對象。 Signature signature = Signature.getInstance( "MD5WithRSA" ); // 初始化這個用于簽名的對象。如果使用其他參數再次調用此方法,此調用的結果將無效。 signature.initSign(kp.getPrivate()); // This class provides a binding between a Signature object and an // authenticated X.500 name (from an X.509 certificate chain), which is // needed in many public key signing applications. X500Signer signer = new X500Signer(signature, issuer); // This class identifies algorithms, such as cryptographic transforms, // each of which may be associated with parameters. AlgorithmId algorithmid = signer.getAlgorithmId(); // This class defines the AlgorithmId for the Certificate. x509certinfo .set( "algorithmID" , new CertificateAlgorithmId(algorithmid)); // 開始時間 Date bdate = new Date(); // 結束時間 Date edate = new Date(); // 天 小時 分 秒 毫秒 edate.setTime(bdate.getTime() + 3650 * 24L * 60L * 60L * 1000L); // validity為有效時間長度 單位為秒,This class defines the interval for which the // certificate is valid.證書的有效時間 CertificateValidity certificatevalidity = new CertificateValidity( bdate, edate); x509certinfo.set( "validity" , certificatevalidity); // This class defines the SerialNumber attribute for the Certificate. // 設置有效期域(包含開始時間和到期時間)域名等同與x509certinfo.VALIDITY x509certinfo.set( "serialNumber" , new CertificateSerialNumber( ( int ) ( new Date().getTime() / 1000L))); // 設置序列號域,This class defines the version of the X509 Certificate. CertificateVersion cv = new CertificateVersion(CertificateVersion.V3); x509certinfo.set(X509CertInfo.VERSION, cv); // 設置版本號 只有v1 ,v2,v3這幾個合法值 /** * 以上是證書的基本信息 如果要添加用戶擴展信息 則比較麻煩 首先要確定version必須是v3否則不行 然后按照以下步驟 */ ObjectIdentifier oid = new ObjectIdentifier( new int [] { 2 , 5 , 29 , 15 }); // 生成擴展域的id 是個int數組 第1位最大2 第2位最大39 最多可以幾位不明.... String userData = "Digital Signature, Non-Repudiation, Key Encipherment, Data Encipherment (f0)" ; byte l = ( byte ) userData.length(); // 數據總長17位 byte f = 0x04 ; byte [] bs = new byte [userData.length() + 2 ]; bs[ 0 ] = f; bs[ 1 ] = l; for ( int i = 2 ; i < bs.length; i++) { bs[i] = ( byte ) userData.charAt(i - 2 ); } Extension ext = new Extension(oid, true , bs); // 生成一個extension對象 參數分別為 oid,是否關鍵擴展,byte[]型的內容值 // 其中內容的格式比較怪異 第一位是flag 這里取4暫時沒出錯 估計用來說明數據的用處的 第2位是后面的實際數據的長度,然后就是數據 // 密鑰用法 KeyUsageExtension keyUsage = new KeyUsageExtension(); keyUsage.set(KeyUsageExtension.DIGITAL_SIGNATURE, true ); keyUsage.set(KeyUsageExtension.NON_REPUDIATION, true ); keyUsage.set(KeyUsageExtension.KEY_ENCIPHERMENT, true ); keyUsage.set(KeyUsageExtension.DATA_ENCIPHERMENT, true ); // 增強密鑰用法 ObjectIdentifier ekeyOid = new ObjectIdentifier( new int [] { 1 , 3 , 6 , 1 , 5 , 5 , 7 , 3 , 3 }); Vector<ObjectIdentifier> vkeyOid = new Vector<ObjectIdentifier>(); vkeyOid.add(ekeyOid); ExtendedKeyUsageExtension exKeyUsage = new ExtendedKeyUsageExtension( vkeyOid); CertificateExtensions exts = new CertificateExtensions(); exts.set( "keyUsage" , keyUsage); exts.set( "extendedKeyUsage" , exKeyUsage); // 如果有多個extension則都放入CertificateExtensions 類中, x509certinfo.set(X509CertInfo.EXTENSIONS, exts); // 設置extensions域 X509CertImpl x509certimpl1 = new X509CertImpl(x509certinfo); x509certimpl1.sign(rootPrivKey, "MD5WithRSA" ); // 使用另一個證書的私鑰來簽名此證書 這里使用 md5散列 用rsa來加密 BASE64Encoder base64 = new BASE64Encoder(); FileOutputStream fos = new FileOutputStream( new File( "f:\\ScriptX.crt" )); base64.encodeBuffer(x509certimpl1.getEncoded(), fos); try { Certificate[] certChain = { x509certimpl1 }; savePfx( "scriptx" , kp.getPrivate(), "123456" , certChain, "f:\\ScriptX.pfx" ); FileInputStream in = new FileInputStream( "F:\\ScriptX.pfx" ); KeyStore inputKeyStore = KeyStore.getInstance( "pkcs12" ); inputKeyStore.load(in, "123456" .toCharArray()); Certificate cert = inputKeyStore.getCertificate( "scriptx" ); System.out.print(cert.getPublicKey()); PrivateKey privk = (PrivateKey) inputKeyStore.getKey( "scriptx" , "123456" .toCharArray()); FileOutputStream privKfos = new FileOutputStream( new File( "f:\\ScriptX.pvk" )); privKfos.write(privk.getEncoded()); System.out.print(privk); // base64.encode(key.getEncoded(), privKfos); in.close(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } // 生成文件 x509certimpl1.verify(certificate.getPublicKey(), null ); } /** * 保存此根證書信息KeyStore Personal Information Exchange * * @param alias * @param privKey * @param pwd * @param certChain * @param filepath * @throws Exception */ public void savePfx(String alias, PrivateKey privKey, String pwd, Certificate[] certChain, String filepath) throws Exception { // 此類表示密鑰和證書的存儲設施。 // 返回指定類型的 keystore 對象。此方法從首選 Provider 開始遍歷已注冊安全提供者列表。返回一個封裝 KeyStoreSpi // 實現的新 KeyStore 對象,該實現取自第一個支持指定類型的 Provider。 KeyStore outputKeyStore = KeyStore.getInstance( "pkcs12" ); System.out.println( "KeyStore類型:" + outputKeyStore.getType()); // 從給定輸入流中加載此 KeyStore。可以給定一個密碼來解鎖 keystore(例如,駐留在硬件標記設備上的 keystore)或檢驗 // keystore 數據的完整性。如果沒有指定用于完整性檢驗的密碼,則不會執行完整性檢驗。如果要創建空 // keystore,或者不能從流中初始化 keystore,則傳遞 null 作為 stream 的參數。注意,如果此 keystore // 已經被加載,那么它將被重新初始化,并再次從給定輸入流中加載。 outputKeyStore.load( null , pwd.toCharArray()); // 將給定密鑰(已經被保護)分配給給定別名。如果受保護密鑰的類型為 // java.security.PrivateKey,則它必須附帶證明相應公鑰的證書鏈。如果底層 keystore 實現的類型為 // jks,則必須根據 PKCS #8 標準中的定義將 key 編碼為 // EncryptedPrivateKeyInfo。如果給定別名已經存在,則與別名關聯的 keystore // 信息將被給定密鑰(還可能包括證書鏈)重寫。 outputKeyStore .setKeyEntry(alias, privKey, pwd.toCharArray(), certChain); // KeyStore.PrivateKeyEntry pke=new // KeyStore.PrivateKeyEntry(kp.getPrivate(),certChain); // KeyStore.PasswordProtection password=new // KeyStore.PasswordProtection("123456".toCharArray()); // outputKeyStore.setEntry("scriptx", pke, password); FileOutputStream out = new FileOutputStream(filepath); // 將此 keystore 存儲到給定輸出流,并用給定密碼保護其完整性。 outputKeyStore.store(out, pwd.toCharArray()); out.close(); } public void saveJks(String alias, PrivateKey privKey, String pwd, Certificate[] certChain, String filepath) throws Exception { KeyStore outputKeyStore = KeyStore.getInstance( "jks" ); System.out.println(outputKeyStore.getType()); outputKeyStore.load( null , pwd.toCharArray()); outputKeyStore .setKeyEntry(alias, privKey, pwd.toCharArray(), certChain); // KeyStore.PrivateKeyEntry pke=new // KeyStore.PrivateKeyEntry(kp.getPrivate(),certChain); // KeyStore.PasswordProtection password=new // KeyStore.PasswordProtection("123456".toCharArray()); // outputKeyStore.setEntry("scriptx", pke, password); FileOutputStream out = new FileOutputStream(filepath); outputKeyStore.store(out, pwd.toCharArray()); out.close(); } /** * 頒布根證書,自己作為CA * * @throws NoSuchAlgorithmException * @throws NoSuchProviderException * @throws InvalidKeyException * @throws IOException * @throws CertificateException * @throws SignatureException * @throws UnrecoverableKeyException */ public void createRootCA() throws NoSuchAlgorithmException, NoSuchProviderException, InvalidKeyException, IOException, CertificateException, SignatureException, UnrecoverableKeyException { // 參數分別為公鑰算法、簽名算法 providername(因為不知道確切的 只好使用null 既使用默認的provider) // Generate a pair of keys, and provide access to them. CertAndKeyGen cak = new CertAndKeyGen( "RSA" , "MD5WithRSA" , null ); // Sets the source of random numbers used when generating keys. cak.setRandom(sr); // Generates a random public/private key pair, with a given key size. cak.generate( 1024 ); // Constructs a name from a conventionally formatted string, such as // "CN=Dave, OU=JavaSoft, O=Sun Microsystems, C=US". (RFC 1779 or RFC // 2253 style) X500Name subject = new X500Name( "CN=RootCA,OU=hackwp,O=wp,L=BJ,S=BJ,C=CN" ); // Returns a self-signed X.509v3 certificate for the public key. The // certificate is immediately valid. No extensions. // Such certificates normally are used to identify a "Certificate // Authority" (CA). Accordingly, they will not always be accepted by // other parties. However, such certificates are also useful when you // are bootstrapping your security infrastructure, or deploying system // prototypes.自簽名的根證書 X509Certificate certificate = cak.getSelfCertificate(subject, new Date(), 3650 * 24L * 60L * 60L); X509Certificate[] certs = { certificate }; try { savePfx( "RootCA" , cak.getPrivateKey(), "123456" , certs, "f:\\RootCa.pfx" ); } catch (Exception e) { e.printStackTrace(); } // 后一個long型參數代表從現在開始的有效期 單位為秒(如果不想從現在開始算 可以在后面改這個域) BASE64Encoder base64 = new BASE64Encoder(); FileOutputStream fos = new FileOutputStream( new File( "f:\\RootCa.crt" )); // fos.write(certificate.getEncoded()); // 生成(保存)cert文件 base64加密 當然也可以不加密 base64.encodeBuffer(certificate.getEncoded(), fos); fos.close(); } public void signCert() throws NoSuchAlgorithmException, CertificateException, IOException, UnrecoverableKeyException, InvalidKeyException, NoSuchProviderException, SignatureException { try { KeyStore ks = KeyStore.getInstance( "pkcs12" ); FileInputStream ksfis = new FileInputStream( "f:\\RootCa.pfx" ); char [] storePwd = "123456" .toCharArray(); char [] keyPwd = "123456" .toCharArray(); // 從給定輸入流中加載此 KeyStore。 ks.load(ksfis, storePwd); ksfis.close(); // 返回與給定別名關聯的密鑰(私鑰),并用給定密碼來恢復它。必須已經通過調用 setKeyEntry,或者以 // PrivateKeyEntry // 或 SecretKeyEntry 為參數的 setEntry 關聯密鑰與別名。 PrivateKey privK = (PrivateKey) ks.getKey( "RootCA" , keyPwd); // 返回與給定別名關聯的證書。如果給定的別名標識通過調用 setCertificateEntry 創建的條目,或者通過調用以 // TrustedCertificateEntry 為參數的 setEntry // 創建的條目,則返回包含在該條目中的可信證書。如果給定的別名標識通過調用 setKeyEntry 創建的條目,或者通過調用以 // PrivateKeyEntry 為參數的 setEntry 創建的條目,則返回該條目中證書鏈的第一個元素。 X509Certificate certificate = (X509Certificate) ks .getCertificate( "RootCA" ); createCert(certificate, privK, genKey()); } catch (KeyStoreException e) { // TODO Auto-generated catch block e.printStackTrace(); } } public KeyPair genKey() throws NoSuchAlgorithmException { KeyPairGenerator kpg = KeyPairGenerator.getInstance( "RSA" ); kpg.initialize( 1024 , sr); System.out.print(kpg.getAlgorithm()); KeyPair kp = kpg.generateKeyPair(); return kp; } public static void main(String[] args) { try { GenX509Cert gcert = new GenX509Cert(); gcert.createRootCA(); gcert.signCert(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } } |
以上這篇純Java實現數字證書生成簽名的簡單實例就是小編分享給大家的全部內容了,希望能給大家一個參考,也希望大家多多支持服務器之家。