/Users/lyon/j4p/src/security/KeyUtils.java

1    package security; 
2     
3    import gui.In; 
4    import sun.security.pkcs.PKCS10; 
5    import sun.security.x509.CertAndKeyGen; 
6    import sun.security.x509.X500Name; 
7    import sun.security.x509.X500Signer; 
8     
9    import java.io.*; 
10   import java.security.*; 
11   import java.security.cert.Certificate; 
12   import java.security.cert.CertificateException; 
13   import java.security.cert.CertificateFactory; 
14   import java.security.cert.X509Certificate; 
15   import java.security.interfaces.DSAKey; 
16   import java.text.MessageFormat; 
17   import java.util.*; 
18    
19   import gui.In; 
20    
21   /** 
22    * DocJava, Inc. User: lyon Date: May 20, 2004 
23    * Time: 6:35:50 AM 
24    */ 
25   public class KeyUtils { 
26       public static final ResourceBundle rb = ResourceBundle.getBundle( 
27               "sun.security.util.Resources"); 
28    
29       public static void main(String[] args) { 
30           //testX500Name(); 
31           //testGenerateKeyPair(); 
32           runImportCertificate(); 
33       } 
34    
35       /** 
36        * a gui for importing certificates 
37        */ 
38       public static void runImportCertificate() { 
39           try { 
40               importCertificate(); 
41           } catch (Exception e) { 
42               In.message(e); 
43           } 
44       } 
45    
46       private static void testX500Name() { 
47           X500Name x500Name = null; 
48           try { 
49               x500Name = getX500Name(); 
50           } catch (IOException e) { 
51               e.printStackTrace(); 
52           } 
53           System.out.println(x500Name); 
54       } 
55    
56       /** 
57        * Open the <code>.keystore</code> file. 
58        * Prompt the user for an alias. 
59        * Open the new certificate from the CA. 
60        * Modify the <code>.keystore</code> file with 
61        * the new certificate. 
62        * If files are missing, recover from the 
63        * error with grace. 
64        * @throws FileNotFoundException 
65        * @throws KeyStoreException 
66        * @throws CertificateException 
67        * @throws IOException 
68        */ 
69       public static void importCertificate() 
70               throws FileNotFoundException, 
71                      KeyStoreException, 
72                      CertificateException, 
73                      IOException { 
74           final File ksf = getKeystoreFile(); 
75           final String password = getPassword(); 
76           KeyStore ks = getKeyStore(ksf, password); 
77           final String alias = getAlias(ks); 
78           X509Certificate cert = (X509Certificate) getCertificate( 
79                   getCertificateFile()); 
80           if (ks.containsAlias(alias)) 
81               ks.deleteEntry(alias); 
82           verifyCert(cert); 
83           ks.setCertificateEntry(alias, cert); 
84           save(ksf, ks, password); 
85           In.message("import complete!"); 
86       } 
87      /** 
88       * @param cert 
89       * @return true if the subjectDN and issuer are the same. 
90       */ 
91       public static boolean isSelfSigned( 
92               X509Certificate cert) { 
93           return cert.getSubjectDN().equals( 
94                   cert.getIssuerDN()); 
95       } 
96    
97       /** 
98        * Display exception if certificate 
99        * cannot be verified. Kill program. 
100       * @param cert 
101       */ 
102      public static void verifyCert( 
103              X509Certificate cert) { 
104          try { 
105              if (isSelfSigned(cert)) 
106                  cert.verify(cert.getPublicKey()); 
107          } catch (Exception e) { 
108              In.message(e); 
109              System.exit(0); 
110          } 
111      } 
112      /** 
113       * Use a certificate file to to 
114       * make a certificate instances. 
115       * @param certF 
116       * @return 
117       * @throws CertificateException 
118       * @throws FileNotFoundException 
119       * @throws IOException 
120       */ 
121      public static Certificate getCertificate( 
122              File certF) 
123              throws CertificateException, 
124                     FileNotFoundException, 
125                     IOException { 
126          CertificateFactory cf = 
127                  CertificateFactory.getInstance( 
128                          "X509"); 
129          Collection col = importCertificates(cf, 
130                                              certF); 
131          Iterator i = col.iterator(); 
132          X509Certificate c = null; 
133          while (i.hasNext()) { 
134              Certificate cert = (Certificate) i.next(); 
135              if (cert instanceof X509Certificate) 
136                  c = (X509Certificate) cert; 
137              //System.out.println(cert); 
138          } 
139          return c; 
140      } 
141      /** 
142       * get a collection of certificates from 
143       * a certificateFactory. 
144       * @param cf 
145       * @param certF 
146       * @return 
147       * @throws CertificateException 
148       * @throws FileNotFoundException 
149       * @throws IOException 
150       */ 
151      private static Collection importCertificates( 
152              CertificateFactory cf, File certF) 
153              throws CertificateException, 
154                     FileNotFoundException, 
155                     IOException { 
156          final FileInputStream fis = new FileInputStream( 
157                  certF); 
158          Collection col = null; 
159          try { 
160              col = cf.generateCertificates(fis); 
161          } catch (CertificateException e) { 
162              boolean b = gui.In.getBoolean( 
163                      "Cert exception, would you like me to try to translate it?"); 
164              if (!b) return importCertificates(cf); 
165              CertUtils.cleanThawtes(); 
166              return importCertificates(cf); 
167          } 
168          return col; 
169      } 
170   
171      private static Collection importCertificates( 
172              CertificateFactory cf) 
173              throws CertificateException, 
174                     IOException { 
175          return importCertificates(cf, 
176                                    futils.Futil.getReadFile( 
177                                            "select pcks7 cert")); 
178      } 
179   
180      /** 
181       * Prompt the user for a PKCS7 format 
182       * certificate. 
183       * @return a <code>File</code> instance that is unchecked. 
184       * That is, the file may not even exist. 
185       */ 
186      private static File getCertificateFile() { 
187          return 
188                  futils.Futil.getReadFile( 
189                          "select a cert in pkcs7 format"); 
190      } 
191      /** 
192       * List the security providers in their order of 
193       * preference. 
194       */ 
195      public static void printProviders() { 
196          System.out.println("provider list:"); 
197          Provider p[] = Security.getProviders(); 
198          for (int i = 0; i < p.length; i++) 
199              printProvider(p[i]); 
200      } 
201   
202      private static void printProvider( 
203              final Provider provider) { 
204          System.out.println( 
205                  "Name:" + provider.getName()); 
206          System.out.println( 
207                  "Provider:" + provider); 
208          System.out.println( 
209                  "Info:" + provider.getInfo()); 
210          System.out.println("algorithm:" + 
211                             provider.getProperty( 
212                                     "algorithm")); 
213          System.out.println("-----"); 
214      } 
215      /** 
216       * print out a nicely formatted version of 
217       * a given key. 
218       * @param key 
219       */ 
220      public static void printKey(Key key) { 
221          if (key instanceof DSAKey) { 
222              System.out.println("key is DSA"); 
223              System.out.println("P value is " + 
224                                 ((DSAKey) key).getParams() 
225                                 .getP()); 
226          } else { 
227              System.out.println("key is NOT DSA"); 
228              System.out.println(key); 
229          } 
230      } 
231   
232      /** 
233       * get a certificate based on a GUI prompt 
234       * to the user for a password and an alias. 
235       * @return   Certificate 
236       */ 
237      public static Certificate getCertificate() { 
238          String password = getPassword(); 
239          String alias = getAlias(); 
240          return 
241                  getCertificate(alias, 
242                                 password); 
243      } 
244      /** 
245       * Prompt the user for a password. 
246       * Do not echo it on the screen 
247       * @return a string containing the password. 
248       */ 
249      public static String getPassword() { 
250          String password = gui.In.getPassword( 
251                  "please enter keystore password"); 
252          return password; 
253      } 
254   
255      private static String getAlias() { 
256          String alias = gui.In.getString( 
257                  "please enter certificate alias"); 
258          return alias; 
259      } 
260      /** 
261       * Given an alias and password, open the 
262       * default keystore and return the certificate. 
263       * @param alias 
264       * @param password 
265       * @return a Certificate instance. 
266       */ 
267      public static Certificate getCertificate( 
268              String alias, String password) { 
269          try { 
270              // Load the keystore in the user's home directory 
271              KeyStore keystore = getKeystore( 
272                      password); 
273   
274              // Get certificate 
275   
276              return keystore.getCertificate(alias); 
277          } catch (KeyStoreException e) { 
278              e.printStackTrace(); 
279          } catch (CertificateException e) { 
280              e.printStackTrace(); 
281          } catch (NoSuchAlgorithmException e) { 
282              e.printStackTrace(); 
283          } catch (java.io.IOException e) { 
284              e.printStackTrace(); 
285          } 
286          return null; 
287      } 
288   
289      /** 
290       * Return a <code>KeyStore</code> assuming 
291       * that one already exists. 
292       * If the <code>.keystore</code> file does not 
293       * exist, then offer to create one or 
294       * look for one. 
295       * 
296       * @param password to the keystore 
297       * @return KeyStore instance 
298       */ 
299      public static KeyStore getKeystore( 
300              String password) 
301              throws KeyStoreException, 
302                     IOException, 
303                     NoSuchAlgorithmException, 
304                     CertificateException { 
305          File f = getKeystoreFile(); 
306          FileInputStream is = new FileInputStream( 
307                  f); 
308          KeyStore keystore = KeyStore.getInstance( 
309                  KeyStore.getDefaultType()); 
310          keystore.load(is, 
311                        password.toCharArray()); 
312          return keystore; 
313      } 
314   
315      public static void testGenerateKeyPair() { 
316          System.out.println("generateKeyPair:" + 
317                             generateKeyPair()); 
318      } 
319   
320      /** 
321       * Creates a keystore, then generates a 
322       * keypair for it. 
323       * 
324       * @return KeyStore with keypair in it. 
325       */ 
326      public static KeyStore generateKeyPair() { 
327          KeyStore ks = null; 
328          try { 
329              ks = generateKeyStore(); 
330              generateKeyPair(ks); 
331          } catch (Exception e) { 
332              e.printStackTrace(); 
333          } 
334          return ks; 
335      } 
336   
337      /** 
338       * Generate a keystore without reading it from 
339       * a file. Excellent for when no keystore is 
340       * found. 
341       * 
342       * @return a default type keystore instance 
343       */ 
344      public static KeyStore generateKeyStore() 
345              throws KeyStoreException, 
346                     IOException, 
347                     NoSuchAlgorithmException, 
348                     CertificateException { 
349          KeyStore ks = 
350                  KeyStore.getInstance( 
351                          KeyStore.getDefaultType()); 
352          ks.load(null, null); 
353          return ks; 
354      } 
355   
356      /** 
357       * Prompts the user for X.509 certificate 
358       * information. Generates a private and public 
359       * keypair, then add it to the keystore. Uses 
360       * RSA algorithm with a 1024 bit key size. 
361       */ 
362      public static void generateKeyPair( 
363              KeyStore ks) 
364              throws Exception { 
365          X500Name x500Name = getX500Name(); 
366          int validity = 90; 
367          int keysize = 1024; 
368          String alias = In.getString( 
369                  "enter alias"); 
370          String keyPass = In.getPassword( 
371                  "enter password"); 
372          String sigAlgName = "MD5WithRSA"; 
373          String keyAlgName = "RSA"; 
374          CertAndKeyGen keypair = new CertAndKeyGen( 
375                  keyAlgName, sigAlgName); 
376          keypair.generate(keysize); 
377          PrivateKey privKey = keypair.getPrivateKey(); 
378          X509Certificate chain[] = new X509Certificate[1]; 
379          chain[0] = 
380          keypair.getSelfCertificate(x500Name, 
381                                     validity * 24 * 
382                                     60 * 
383                                     60); 
384          ks.setKeyEntry(alias, 
385                         privKey, 
386                         keyPass.toCharArray(), 
387                         chain); 
388      } 
389   
390      /** 
391       * get a public and private key, given that a 
392       * KeyStore exists and a certificate exists 
393       * that corresponds to the the given <code> 
394       * alias</code>. 
395       */ 
396      public static KeyPair getKeyPair( 
397              KeyStore keystore, 
398              String alias, 
399              String password) { 
400          try { 
401              // Get private key 
402              Key key = keystore.getKey(alias, 
403                                        password.toCharArray()); 
404              if (key instanceof PrivateKey) { 
405                  // Get certificate of public key 
406                  java.security.cert.Certificate cert = keystore.getCertificate( 
407                          alias); 
408   
409                  // Get public key 
410                  PublicKey publicKey = cert.getPublicKey(); 
411   
412                  // Return a key pair 
413                  return new KeyPair(publicKey, 
414                                     (PrivateKey) key); 
415              } 
416          } catch (UnrecoverableKeyException e) { 
417          } catch (NoSuchAlgorithmException e) { 
418          } catch (KeyStoreException e) { 
419          } 
420          return null; 
421      } 
422   
423      public static KeyStore getKeyStore() { 
424          return getKeyStore(getPassword()); 
425      } 
426   
427      /** 
428       * Selects the <code>.keystore</code> file in 
429       * the users home directory. 
430       * 
431       * @return KeyStore 
432       */ 
433      public static KeyStore getKeyStore( 
434              String password) { 
435          File ksf = getKeystoreFile(); 
436          if (ksf.exists()) 
437              return getKeyStore(ksf, password); 
438          In.getBoolean( 
439                  "I could not find the .keystore file...sorry"); 
440          return null; 
441      } 
442   
443      public static KeyStore getKeyStore( 
444              File keyStoreFile, String password) { 
445          KeyStore keystore = null; 
446          try { 
447              FileInputStream is = new FileInputStream( 
448                      keyStoreFile); 
449              keystore = 
450              KeyStore.getInstance( 
451                      KeyStore.getDefaultType()); 
452              keystore.load(is, 
453                            password.toCharArray()); 
454              is.close(); 
455          } catch (Exception e) { 
456              In.message(e); 
457          } 
458          return keystore; 
459      } 
460      /** 
461       * Given a key store, list all the alias 
462       * elements there. Certificates are located 
463       * via the alias. 
464       * @param keystore 
465       * @return An array of alias members. 
466       */ 
467      public static String[] getAliasArray( 
468              KeyStore keystore) { 
469          Enumeration enum = null; 
470          try { 
471              // List the aliases 
472              enum = keystore.aliases(); 
473          } catch (KeyStoreException e) { 
474              e.printStackTrace(); 
475          } 
476          Vector v = new Vector(); 
477          for (; enum.hasMoreElements();) { 
478              String alias = (String) enum.nextElement(); 
479              v.addElement(alias); 
480          } 
481          String s[] = new String[v.size()]; 
482          v.copyInto(s); 
483          return s; 
484      } 
485   
486      /** 
487       * Write our key store instance out to the 
488       * given file. A GUI prints out exceptions, 
489       * should they be thrown. 
490       * 
491       * @param ksFile   a file to be created or 
492       *                 overwritten. 
493       * @param ks       the key store to be saved. 
494       * @param password verifies the file. 
495       */ 
496      public static void save(File ksFile, 
497                              KeyStore ks, 
498                              String password) { 
499          try { 
500              FileOutputStream fos = new FileOutputStream( 
501                      ksFile.getAbsolutePath()); 
502              ks.store(fos, 
503                       password.toCharArray()); 
504              fos.close(); 
505          } catch (Exception e) { 
506              In.message(e); 
507          } 
508      } 
509   
510      /** 
511       * Look for the <code>.keystore</code> 
512       * file in the home directory. If it is 
513       * not there, offer to look for it. 
514       * If the user does not have it, offer to 
515       * create one. If you have to create a keystore, 
516       * offer to create a certificate request, as well. 
517       * 
518       * @return the keystore file 
519       */ 
520      public static File getKeystoreFile() { 
521          File file = getDefaultKeyStoreFile(); 
522          if (file.exists()) return file; 
523          final String prompt = "file:" + file + 
524                                " does not exist:"; 
525          boolean b = In.getBoolean( 
526                  prompt + 
527                  "do you have a keystore?"); 
528          if (b) 
529              return futils.Futil.getReadFile( 
530                      prompt); 
531          b = 
532          In.getBoolean( 
533                  "would you like me to generate a keystore for you?"); 
534          if (b) return makeKeyStoreFile(); 
535          In.message( 
536                  "program exits try:keytool -genkey -keyalg RSA -alias docjava"); 
537          System.exit(0); 
538          return null; 
539      } 
540       /** 
541        * Ouput a file based on user prompts, 
542        * that contains the text for a Certificate 
543        * Request. This is used with a CA to obtain 
544        * a signed certificate. 
545        * @param alias 
546        * @param keyPass 
547        * @param ks 
548        */ 
549      public static void writeCertReq(String alias, 
550                                      String keyPass, 
551                                      KeyStore ks) { 
552          Object objs[] = recoverPrivateKey(ks, 
553                                            alias, 
554                                            keyPass.toCharArray()); 
555          PrivateKey privKey = (PrivateKey) objs[0]; 
556          if (keyPass == null) 
557              keyPass = 
558              new String((char[]) objs[1]); 
559          PKCS10 request = null; 
560          try { 
561              Certificate cert = ks.getCertificate( 
562                      alias); 
563              request = new PKCS10( 
564                      cert.getPublicKey()); 
565              String sigAlgName = "MD5WithRSA"; 
566              Signature signature = Signature.getInstance( 
567                      sigAlgName); 
568              signature.initSign(privKey); 
569              X500Name subject = new X500Name( 
570                      ((X509Certificate) cert).getSubjectDN() 
571                      .toString()); 
572              X500Signer signer = new X500Signer( 
573                      signature, subject); 
574              request.encodeAndSign(signer); 
575              request.print(System.out); 
576              boolean b = In.getBoolean( 
577                      "would you like to save the certificate request to a file?"); 
578              if (!b) return; 
579              File f = futils.Futil.getWriteFile( 
580                      "select file.cert"); 
581              FileOutputStream fos = new FileOutputStream( 
582                      f); 
583              PrintStream ps = new PrintStream(fos); 
584              request.print(ps); 
585              fos.close(); 
586          } catch (Exception e) { 
587              In.message(e); 
588          } 
589      } 
590   
591      private static Object[] recoverPrivateKey( 
592              KeyStore ks, 
593              String alias, 
594              char keyPass[]) { 
595          Key key = null; 
596          try { 
597              key = ks.getKey(alias, keyPass); 
598          } catch (Exception e) { 
599              In.message(e); 
600          } 
601          return (new Object[]{ 
602              (PrivateKey) key, keyPass 
603          }); 
604      } 
605   
606      /** 
607       * Creates the default .keystore file, 
608       * assuming that it does not already exist. 
609       * Prompts the user to create a key pair. 
610       */ 
611      public static File makeKeyStoreFile() { 
612          File f = getDefaultKeyStoreFile(); 
613          if (f.exists()) { 
614              In.message( 
615                      "file:" + f + 
616                      " exists, program terminates"); 
617              System.exit(0); 
618          } 
619          try { 
620              KeyStore ks = KeyUtils.generateKeyStore(); 
621              generateKeyPair(ks); 
622              String pswd = In.getPassword( 
623                      "enter keystore password"); 
624              KeyUtils.save(f, ks, pswd); 
625              In.message("file:" + f + " created"); 
626              promptForCertReq(ks, pswd); 
627          } catch (Exception e) { 
628              In.message(e); 
629          } 
630          return f; 
631      } 
632   
633      private static void promptForCertReq( 
634              KeyStore ks, String pswd) { 
635          boolean b = In.getBoolean( 
636                  "would you like me to generate a certificate request?"); 
637          if (!b) return; 
638          writeCertReq(getAlias(ks), pswd, ks); 
639      } 
640   
641      /** 
642       * Check to make sure this file exists. 
643       * 
644       * @return .keystore file in users home. 
645       */ 
646      public static File getDefaultKeyStoreFile() { 
647          return new File(System.getProperty( 
648                  "user.home") + 
649                          File.separatorChar + 
650                          ".keystore"); 
651      } 
652   
653   
654      private static void testGetAlias() { 
655          String password = gui.In.getString( 
656                  "please enter keystore password"); 
657          KeyStore keystore = getKeyStore(password); 
658          String s[] = getAliasArray(keystore); 
659          for (int i = 0; i < s.length; i++) 
660              System.out.println("alias:" + s[i]); 
661      } 
662   
663      private static void print(Certificate c) { 
664          System.out.println("-----" + 
665                             "\n" + 
666                             c.getClass().toString() + 
667                             "\n" + c); 
668      } 
669   
670      private static void print(Certificate[] c) { 
671          for (int i = 0; i < c.length; i++) 
672              print(c[i]); 
673      } 
674   
675      /** 
676       * Given a keystore instance, provide 
677       * a multiple choice GUI that enables the 
678       * user to select a certificate alias. 
679       * @param keyStore 
680       * @return A string version of the certificate alias 
681       */ 
682      public static String getAlias(KeyStore keyStore) { 
683          String a[] = getAliasArray(keyStore); 
684          return In.multiPrompt(a, 
685                                "select an alias", 
686                                "alias dialog") 
687                  .toString(); 
688      } 
689      /** 
690       * Given an X509 Certificate, 
691       * print out all the relevant detail. 
692       * @param cert 
693       * @param out 
694       * @throws Exception 
695       */ 
696      public static void printX509Cert( 
697              X509Certificate cert, 
698              PrintStream out) 
699              throws Exception { 
700          MessageFormat form = new MessageFormat( 
701                  rb.getString( 
702                          "*PATTERN* printX509Cert")); 
703          Object source[] = { 
704              cert.getSubjectDN().toString(), cert.getIssuerDN() 
705                  .toString(), cert.getSerialNumber() 
706                  .toString(16), cert.getNotBefore() 
707                  .toString(), cert.getNotAfter() 
708                  .toString(), getCertFingerPrint( 
709                          "MD5", cert), getCertFingerPrint( 
710                                  "SHA1", cert) 
711          }; 
712          out.println( 
713                  form.format(((Object) (source)))); 
714      } 
715   
716      private static void byte2hex(byte b, 
717                                  StringBuffer buf) { 
718          char hexChars[] = { 
719              '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 
720              'A', 'B', 'C', 'D', 'E', 'F' 
721          }; 
722          int high = (b & 0xf0) >> 4; 
723          int low = b & 0xf; 
724          buf.append(hexChars[high]); 
725          buf.append(hexChars[low]); 
726      } 
727   
728      private static String toHexString( 
729              byte block[]) { 
730          StringBuffer buf = new StringBuffer(); 
731          int len = block.length; 
732          for (int i = 0; i < len; i++) { 
733              byte2hex(block[i], buf); 
734              if (i < len - 1) 
735                  buf.append(":"); 
736          } 
737          return buf.toString(); 
738      } 
739   
740      private static String getCertFingerPrint( 
741              String mdAlg, Certificate cert) 
742              throws Exception { 
743          byte encCertInfo[] = cert.getEncoded(); 
744          MessageDigest md = MessageDigest.getInstance( 
745                  mdAlg); 
746          byte digest[] = md.digest(encCertInfo); 
747          return toHexString(digest); 
748      } 
749      /** 
750       * Prompt the user for all the details 
751       * needed to generate a self-signed 
752       * x500 certificate. This is 
753       * stored in a datastructure called 
754       * the <code>X500Name</code> 
755       * @return 
756       * @throws IOException 
757       */ 
758      public static X500Name getX500Name() 
759              throws IOException { 
760          boolean promptP = false; 
761          String commonName = "Unknown"; 
762          String organizationalUnit = "Unknown"; 
763          String organization = "Unknown"; 
764          String city = "Unknown"; 
765          String state = "Unknown"; 
766          String country = "Unknown"; 
767          X500Name name; 
768          do { 
769              commonName = 
770              In.getString( 
771                      "What is your first and last name?"); 
772              organizationalUnit = 
773              In.getString("What is the name of your" + 
774                           " organizational unit?"); 
775              organization = 
776              In.getString("What is the name " + 
777                           "of your organization?"); 
778              city = 
779              In.getString( 
780                      "What is the name " + 
781                      "of your City or Locality?"); 
782              state = 
783              In.getString( 
784                      "What is the name of " + 
785                      "your State or Province?"); 
786              country = 
787              In.getString( 
788                      "What is the two-letter " + 
789                      "country code for this unit?"); 
790              name = 
791              new X500Name(commonName, 
792                           organizationalUnit, 
793                           organization, 
794                           city, 
795                           state, 
796                           country); 
797              promptP = 
798              In.getBoolean( 
799                      "Is " + name + " correct?"); 
800          } while (!promptP); 
801          return name; 
802      } 
803  } 
804