Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: net/jxta/impl/membership/pse/PSEUtils.java


1   /*
2    *
3    * $Id: PSEUtils.java,v 1.11 2004/08/31 21:39:54 bondolo Exp $
4    *
5    * Copyright (c) 2001 Sun Microsystems, Inc.  All rights reserved.
6    *
7    * Redistribution and use in source and binary forms, with or without
8    * modification, are permitted provided that the following conditions
9    * are met:
10   *
11   * 1. Redistributions of source code must retain the above copyright
12   *    notice, this list of conditions and the following disclaimer.
13   *
14   * 2. Redistributions in binary form must reproduce the above copyright
15   *    notice, this list of conditions and the following disclaimer in
16   *    the documentation and/or other materials provided with the
17   *    distribution.
18   *
19   * 3. The end-user documentation included with the redistribution,
20   *    if any, must include the following acknowledgment:
21   *       "This product includes software developed by the
22   *       Sun Microsystems, Inc. for Project JXTA."
23   *    Alternately, this acknowledgment may appear in the software itself,
24   *    if and wherever such third-party acknowledgments normally appear.
25   *
26   * 4. The names "Sun", "Sun Microsystems, Inc.", "JXTA" and "Project JXTA"
27   *    must not be used to endorse or promote products derived from this
28   *    software without prior written permission. For written
29   *    permission, please contact Project JXTA at http://www.jxta.org.
30   *
31   * 5. Products derived from this software may not be called "JXTA",
32   *    nor may "JXTA" appear in their name, without prior written
33   *    permission of Sun.
34   *
35   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
36   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
37   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
38   * DISCLAIMED.  IN NO EVENT SHALL SUN MICROSYSTEMS OR
39   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
40   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
41   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
42   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
43   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
44   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
45   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
46   * SUCH DAMAGE.
47   *
48   * ====================================================================
49   *
50   * This software consists of voluntary contributions made by many
51   * individuals on behalf of Project JXTA.  For more
52   * information on Project JXTA, please see
53   * <http://www.jxta.org/>.
54   *
55   * This license is based on the BSD license adopted by the Apache Foundation.
56   */
57  
58  package net.jxta.impl.membership.pse;
59  
60  import java.io.BufferedReader;
61  import java.io.BufferedWriter;
62  import java.io.ByteArrayOutputStream;
63  import java.io.IOException;
64  import java.io.InputStream;
65  import java.io.Reader;
66  import java.io.StringReader;
67  import java.io.StringWriter;
68  import java.math.BigInteger;
69  import java.security.AlgorithmParameters;
70  import java.security.KeyFactory;
71  import java.security.KeyPair;
72  import java.security.KeyPairGenerator;
73  import java.security.MessageDigest;
74  import java.security.PrivateKey;
75  import java.security.Provider;
76  import java.security.SecureRandom;
77  import java.security.Security;
78  import java.security.Signature;
79  import java.security.cert.Certificate;
80  import java.security.cert.X509Certificate;
81  import java.security.spec.KeySpec;
82  import java.util.ArrayList;
83  import java.util.Arrays;
84  import java.util.Calendar;
85  import java.util.Date;
86  import java.util.Hashtable;
87  import java.util.Iterator;
88  import java.util.List;
89  import java.util.Map;
90  import javax.crypto.Cipher;
91  import javax.crypto.EncryptedPrivateKeyInfo;
92  import javax.crypto.SecretKey;
93  import javax.crypto.SecretKeyFactory;
94  import javax.crypto.spec.PBEKeySpec;
95  import javax.crypto.spec.PBEParameterSpec;
96  import javax.security.auth.x500.X500Principal;
97  
98  import java.security.InvalidKeyException;
99  import java.security.NoSuchAlgorithmException;
100 import java.security.SignatureException;
101 import java.security.spec.InvalidKeySpecException;
102 
103 import org.apache.log4j.Level;
104 import org.apache.log4j.Logger;
105 
106 import org.bouncycastle.asn1.x509.X509NameTokenizer;
107 import org.bouncycastle.jce.X509Principal;
108 import org.bouncycastle.jce.X509V3CertificateGenerator;
109 import org.bouncycastle.jce.provider.BouncyCastleProvider;
110 
111 import net.jxta.impl.util.BASE64InputStream;
112 import net.jxta.impl.util.BASE64OutputStream;
113 
114 /**
115  *  Singleton class of static utility methods.
116  *
117  *  <p/>Properties:
118  *
119  *  <p/>net.jxta.impl.membership.pse.PSEUtils.PBEParamsClass - if defined the 
120  *  name of the class which will be aliased to the OID 1.2.840.113549.1.5.3 
121  **/
122 public final class PSEUtils {
123     
124     /**
125      *  Log4J Logger
126      **/
127     private static final transient Logger LOG = Logger.getLogger(PSEUtils.class.getName());
128     
129     /**
130      *  Singleton instance.
131      **/
132     private static final PSEUtils UTILS = new PSEUtils();
133     
134     /**
135      *  A SecureRandom for generating keys.
136      **/
137     final transient SecureRandom srng = new SecureRandom();
138     
139     /**
140      *  A provider we construct. This provider primarily exists to provide
141      *  aliases for 
142      **/
143     public static class PSEProvider extends Provider {
144         
145         public PSEProvider() {
146             super( "JXTAPSE", 2.3, "JXTA PSE Alias Provider" );
147             
148             // Aliases for the oids we use in pkcs5_Encrypt_pbePrivateKey and pkcs5_Decrypt_pbePrivateKey
149             put( "Alg.Alias.AlgorithmParameters.OID.1.2.840.113549.1.5.1", "PBEWithMD2AndDES" );
150             put( "Alg.Alias.AlgorithmParameters.OID.1.2.840.113549.1.5.3", "PBEWithMD5AndDES" );
151             put( "Alg.Alias.AlgorithmParameters.1.2.840.113549.1.5.3", "PBEWithMD5AndDES" );
152             put( "Alg.Alias.AlgorithmParameters.OID.1.2.840.113549.1.5.10", "PBEWithSHA1AndDES" );
153             put( "Alg.Alias.AlgorithmParameters.OID.1.2.840.113549.1.5.12", "PBKDF2" );
154             put( "Alg.Alias.AlgorithmParameters.OID.1.2.840.113549.1.5.13", "PBES2" );
155             put( "Alg.Alias.AlgorithmParameters.OID.1.2.840.113549.1.5.14", "PBMAC1" );
156             
157             Provider [] providers = Security.getProviders();
158             Iterator eachProvider = Arrays.asList( providers ).iterator();
159             List providerNames = new ArrayList();
160             while( eachProvider.hasNext() ) {
161                 Provider aProvider = (Provider) eachProvider.next();
162                 
163                 providerNames.add( aProvider.getName() );
164             }
165             
166             boolean addedPBE = false;
167             
168             String overrideProp = System.getProperty( "net.jxta.impl.membership.pse.PSEUtils.PBEParamsClass" );
169             
170             if( null != overrideProp ) {
171                  put( "AlgorithmParameters.1.2.840.113549.1.5.3", overrideProp );
172                  addedPBE = true;
173             }
174 
175             if( (!addedPBE) && providerNames.contains( "SunJCE" ) ) {
176                  put( "AlgorithmParameters.1.2.840.113549.1.5.3", "com.sun.crypto.provider.PBEParameters" );
177                  addedPBE = true;
178             }
179             
180             if( (!addedPBE) && providerNames.contains( "IBMJCE" ) ) {
181                  put( "AlgorithmParameters.1.2.840.113549.1.5.3", "com.ibm.crypto.provider.PBEParameters" );
182                  addedPBE = true;
183             }
184             
185             // FIXME 20040823 bondolo Add solutions for other VMs here.
186             
187             if( !addedPBE ) {
188                 // we'return desperate, add it anyway.
189                 put( "AlgorithmParameters.1.2.840.113549.1.5.3", "com.sun.crypto.provider.PBEParameters" );
190                 LOG.warn( "Could not find supported provider. Trying " + "com.sun.crypto.provider.PBEParameters" + " as last hope." );
191             }
192             
193             //
194             // Compatibility KeyStore
195             //
196             put("KeyStore.PSE", "net.jxta.impl.membership.pse.PSEStore");
197         }
198         
199         public String getProperty( String key ) {
200             String result = super.getProperty(key);
201             
202             // LOG.debug( "PSE Provider returning : " + key + " --> " + result );
203             
204             return result;
205         }
206         
207         public String getProperty( String key, String defaultValue ) {
208             String result = super.getProperty(key, defaultValue );
209             
210             // LOG.debug( "PSE Provider returning : " + key + " --> " + result );
211             
212             return result;
213         }
214     }
215     
216     /**
217      *  Singleton utility class
218      **/
219     private PSEUtils() {
220         
221         try {
222             ClassLoader sysloader = ClassLoader.getSystemClassLoader();
223             
224             Class loaded = sysloader.loadClass( BouncyCastleProvider.class.getName() );
225             
226             Provider provider = (Provider) loaded.newInstance();
227             
228             Security.addProvider( provider );
229             
230             loaded = sysloader.loadClass( PSEProvider.class.getName() );
231             
232             provider = (Provider) loaded.newInstance();
233             
234             Security.addProvider( provider );
235             
236             if ( LOG.isEnabledFor(Level.INFO) ) {
237                 LOG.info( "Loaded Security Providers into system class loader" );
238             }
239         } catch ( Exception disallowed ) {
240             
241             if ( LOG.isEnabledFor(Level.ERROR) ) {
242                 LOG.error( "Can't load Security Providers into System Class Loader, using local class loader (this may not work)", disallowed );
243             }
244             
245             // Add the providers we use.
246             Security.addProvider(new BouncyCastleProvider());
247             
248             Security.addProvider(new PSEProvider());
249             
250             if ( LOG.isEnabledFor(Level.INFO) ) {
251                 LOG.info( "Loaded Security Providers into local class loader" );
252             }
253         }
254         
255 //        Provider [] providers = Security.getProviders();
256 //        Iterator eachProvider = Arrays.asList( providers ).iterator();
257 //        
258 //        while ( eachProvider.hasNext() ) {
259 //            Provider aProvider = (Provider) eachProvider.next();
260 //            
261 //            System.out.println( "\n\n" + aProvider.getName() + " - " + aProvider.getVersion() + " - " + aProvider.getInfo() );
262 //            
263 //            Iterator allMappings = aProvider.entrySet().iterator();
264 //            
265 //            while ( allMappings.hasNext() ) {
266 //                Map.Entry aMapping = (Map.Entry) allMappings.next();
267 //                
268 //                Object key = aMapping.getKey();
269 //                System.out.println( key + " (" + key.getClass().getName() + ") --> " + aMapping.getValue() + " (" + key.getClass().getName() + ")" );
270 //            }
271 //        }
272     }
273     
274     
275     /**
276      *  Issuer Information
277      **/
278     public static class IssuerInfo {
279         public X509Certificate cert;        // subject Cert
280         public PrivateKey  subjectPkey; // subject private key
281         public X509Certificate issuer;      // issuer Cert
282         public PrivateKey  issuerPkey;  // issuer private key
283     }
284     
285     /**
286      *  Generate a Cert
287      *
288      *  @param cn subject cn for the certificate
289      *  @param issuerinfo the cert issuer or null if self-signed root cert.
290      *  @return the details of the generated cert.
291      *  @throws SecurityException if the cert could not be generated.
292      **/
293     public static IssuerInfo genCert( String cn, IssuerInfo issuerinfo ) throws SecurityException {
294         try {
295             String useCN;
296             if( null == issuerinfo ) {
297                 if ( LOG.isEnabledFor(Level.DEBUG) ) {
298                     LOG.debug( "Generating Self Signed Cert ...");
299                 }
300                 
301                 if( !cn.endsWith( "-CA" ) ) {
302                     useCN = cn + "-CA";
303                 } else {
304                     useCN = cn;
305                 }
306             } else {
307                 if ( LOG.isEnabledFor(Level.DEBUG) ) {
308                     LOG.debug( "Generating Client Cert ...");
309                 }
310                 
311                 useCN = cn;
312             }
313             
314             // set name attribute
315             Hashtable attrs = new Hashtable();
316             attrs.put( X509Principal.CN, useCN );
317             attrs.put( X509Principal.O, "www.jxta.org" );
318             
319             // XXX bondolo 20040405 wouldn't SN or UID be a better choice?
320             // set ou to 20 random digits
321             byte[] ou = new byte[10];
322             UTILS.srng.nextBytes(ou);
323             String ouStr = toHexDigits(ou);
324             attrs.put( X509Principal.OU, ouStr );
325             
326             X509Principal subject = new X509Principal(attrs);
327             X500Principal samesubject = new X500Principal( subject.getEncoded() );
328             KeyPairGenerator g = KeyPairGenerator.getInstance( "RSA" );
329             g.initialize( 1024, UTILS.srng );
330             
331             KeyPair keypair = g.generateKeyPair();
332             
333             return genCert( samesubject, keypair, issuerinfo );
334         } catch (NoSuchAlgorithmException e) {
335             if (LOG.isEnabledFor(Level.ERROR)) {
336                 LOG.debug( "Could not generate certificate", e );
337             }
338             SecurityException failure = new SecurityException("Could not generate certificate" );
339             failure.initCause( e );
340             throw failure;
341         }
342     }
343     
344     /**
345      *  Generate a Cert given a keypair
346      *
347      *  @param subject subjectDN for the certificate
348      *  @param keypair the keypair to use.
349      *  @param issuerinfo the cert issuer or null if self-signed root cert.
350      *  @return the details of the generated cert.
351      *  @throws SecurityException if the cert could not be generated.
352      **/
353     public static IssuerInfo genCert( X500Principal subject, KeyPair keypair, IssuerInfo issuerinfo ) throws SecurityException {
354         try {
355             // set up issuer
356             PrivateKey signer;
357             X509Principal issuer;
358             
359             if( null == issuerinfo ) { // self-signed root cert
360                 signer = keypair.getPrivate();
361                 issuer = new X509Principal( subject.getEncoded() );
362             } else {      // issuer signed service sert
363                 signer = issuerinfo.subjectPkey;
364                 X500Principal issuer_subject = issuerinfo.cert.getSubjectX500Principal();
365                 
366                 issuer = new X509Principal( issuer_subject.getEncoded() );
367             }
368             
369             // set validity 10 years from today
370             Date today = new Date();
371             Calendar cal = Calendar.getInstance();
372             cal.setTime(today);
373             cal.add(Calendar.YEAR, 10);
374             Date until = cal.getTime();
375             
376             // generate cert
377             X509V3CertificateGenerator certGen = new X509V3CertificateGenerator();
378             
379             certGen.setIssuerDN(issuer);
380             certGen.setSubjectDN( new X509Principal( subject.getEncoded() ) );
381             certGen.setNotBefore(today);
382             certGen.setNotAfter(until);
383             certGen.setPublicKey( keypair.getPublic() );
384             //certGen.setSignatureAlgorithm("SHA1withDSA");
385             certGen.setSignatureAlgorithm("SHA1withRSA");
386             // FIXME bondolo 20040317 needs fixing.
387             certGen.setSerialNumber( BigInteger.valueOf(1) );
388             
389             // return issuer info for generating service cert
390             IssuerInfo info = new IssuerInfo();
391             
392             // the cert
393             info.cert = certGen.generateX509Certificate( signer, UTILS.srng );
394             
395             // For saving service cert private key
396             info.subjectPkey = keypair.getPrivate();
397             
398             // for signing service cert
399             info.issuer = (null == issuerinfo) ? info.cert : issuerinfo.cert;
400             
401             // for signing service cert
402             info.issuerPkey = signer;
403             
404             // dump the certificate?
405             if ( LOG.isEnabledFor(Level.DEBUG) ) {
406                 if( null == issuer ) {
407                     LOG.debug( "Root Cert : \n" + info.cert.toString() );
408                 } else {
409                     LOG.debug( "Client Cert : \n" + info.cert.toString() );
410                 }
411             }
412             
413             return info;
414         } catch (SignatureException e) {
415             if (LOG.isEnabledFor(Level.ERROR)) {
416                 LOG.debug( "Could not generate certificate", e );
417             }
418             
419             SecurityException failure = new SecurityException("Could not generate certificate" );
420             failure.initCause( e );
421             throw failure;
422         } catch ( InvalidKeyException e ) {
423             if (LOG.isEnabledFor(Level.ERROR)) {
424                 LOG.debug( "Could not generate certificate", e );
425             }
426             
427             SecurityException failure = new SecurityException("Could not generate certificate" );
428             failure.initCause( e );
429             throw failure;
430         } catch ( IOException e ) {
431             if (LOG.isEnabledFor(Level.ERROR)) {
432                 LOG.debug( "Could not generate certificate", e );
433             }
434             
435             SecurityException failure = new SecurityException("Could not generate certificate" );
436             failure.initCause( e );
437             throw failure;
438         }
439     }
440     
441     /**
442      *  return the CN token from the provided cert's subjectDN
443      *
444      *  @param cert the certificate to examine
445      *  @return the CN name or null if none could be found.
446      **/
447     public static String getCertSubjectCName( X509Certificate cert ) {
448         
449         // get the subject dname
450         X500Principal subject = cert.getSubjectX500Principal();
451         
452         X509NameTokenizer tokens = new X509NameTokenizer( subject.getName() );
453         
454         // iterate over the attributes of the dname
455         while( tokens.hasMoreTokens() ) {
456             String aToken = tokens.nextToken();
457             
458             if( aToken.length() < 3 ) {
459                 continue;
460             }
461             
462             String attribute = aToken.substring( 0, 3 );
463             
464             if( "CN=".equalsIgnoreCase( attribute ) ) {
465                 return aToken.substring( 3 );
466             }
467         }
468         
469         return null;
470     }
471     
472     /**
473      * Compute the signature of a stream.
474      *
475      * @param key the private key used to sign the stream
476      * @param stream the stream to sign.
477      * @return byte[] the signature
478      **/
479     public static byte[] computeSignature( String algorithm, PrivateKey key, InputStream stream )
480     throws InvalidKeyException, SignatureException, IOException {
481         Signature sign;
482         try {
483             sign = Signature.getInstance( algorithm );
484         } catch( NoSuchAlgorithmException badsigner ) {
485             throw new IOException( "Could not initialize signer with algorithm " + algorithm );
486         }
487         sign.initSign(key, UTILS.srng );
488         
489         byte [] buffer = new byte [1024];
490         
491         while( true ) {
492             int read = stream.read( buffer );
493             
494             if ( read < 0 ) {
495                 break;
496             }
497             
498             sign.update( buffer, 0, read );
499         }
500         
501         return sign.sign();
502     }
503     
504     /**
505      *  Verify a signature of a stream.
506      *
507      *  @param cert The certificate containing the public key which will be used
508      *  to verify the signature.
509      *  @param signature    The signature to verify.
510      *  @param stream   The stream to verify.
511      *  @return boolean true if the signature was valid otherwise false.
512      **/
513     public static boolean verifySignature( String algorithm, Certificate cert, byte [] signature, InputStream stream )
514     throws   InvalidKeyException, SignatureException, IOException {
515         Signature sign;
516         try {
517             sign = Signature.getInstance( algorithm );
518         } catch( NoSuchAlgorithmException badsigner ) {
519             throw new IOException( "Could not initialize signer with algorithm " + algorithm );
520         }
521         
522         sign.initVerify(cert);
523         
524         byte [] buffer = new byte [1024];
525         
526         while( true ) {
527             int read = stream.read( buffer );
528             
529             if ( read < 0 ) {
530                 break;
531             }
532             
533             sign.update( buffer, 0, read );
534         }
535         
536         return sign.verify( signature );
537     }
538     
539     /**
540      * returns a hash SHA-1 of the given byte array
541      *
542      * @param data the data to be hashed
543      * @return byte[] the hash of the data
544      */
545     public static byte[] hash( String algorithm, byte[] data) {
546         try {
547             MessageDigest digest = MessageDigest.getInstance( algorithm );
548             return  digest.digest(data);
549         } catch (NoSuchAlgorithmException e) {
550             return null;
551         }
552     }
553     
554     /**
555      *  We are trying to use : PBEWithMD5AndDES
556      **/
557     static final String PKCS5_PBSE1_ALGO = "PBEWithMD5AndDES";
558     
559     /**
560      *  Given a private key and a password, encrypt the private key using the
561      *  PBESE1 algorithm.
562      *
563      *  @param password The password which will be used.
564      *  @param privkey  The private key to be encrypted.
565      *  @param iterations   Number of iterations.
566      *  @return An encrypted private key info or null if the key could not be
567      *  encrypted.
568      **/
569     public static EncryptedPrivateKeyInfo pkcs5_Encrypt_pbePrivateKey( char [] password, PrivateKey privkey, int iterations ) {
570         if ( LOG.isEnabledFor(Level.DEBUG) ) {
571             LOG.debug( "Encrypting " + privkey + " with '" + new String(password) + "'" );
572         }
573         
574         PBEKeySpec pbeKeySpec = new PBEKeySpec(password);
575         byte [] salt = new byte [8];
576         UTILS.srng.nextBytes( salt );
577         
578         try {
579             PBEParameterSpec pbeParamSpec = new PBEParameterSpec( salt, iterations );
580             
581             // convert password into a SecretKey object, using a PBE key factory.
582             SecretKeyFactory keyFac = SecretKeyFactory.getInstance( PKCS5_PBSE1_ALGO );
583             SecretKey pbeKey = keyFac.generateSecret(pbeKeySpec);
584             
585             // Create PBE Cipher
586             Cipher pbeCipher = Cipher.getInstance( PKCS5_PBSE1_ALGO );
587             
588             // Initialize PBE Cipher with key and parameters
589             pbeCipher.init(Cipher.ENCRYPT_MODE, pbeKey, pbeParamSpec);
590             
591             byte [] encryptedPrivKey = pbeCipher.doFinal( privkey.getEncoded() );
592             
593             AlgorithmParameters algo = AlgorithmParameters.getInstance( PKCS5_PBSE1_ALGO );
594             algo.init(pbeParamSpec);
595             
596             EncryptedPrivateKeyInfo result = new EncryptedPrivateKeyInfo( algo, encryptedPrivKey );
597             
598             return result;
599         } catch ( Exception failed ) {
600             if ( LOG.isEnabledFor(Level.WARN) ) {
601                 LOG.warn( "Encrypt failed", failed );
602             }
603             return null;
604         }
605     }
606     
607     /**
608      *  Given an encrypted private key and a password, decrypt the private key
609      *  using the PBESE1 algorithm.
610      *
611      *  @param password The password which will be used.
612      *  @param encryptedPrivKey  The private key to be encrypted.
613      *  @return The decrypted private key or null if the key could not be decrpyted.
614      **/
615     public static PrivateKey pkcs5_Decrypt_pbePrivateKey( char [] password, String algorithm, EncryptedPrivateKeyInfo encryptedPrivKey ) {
616         if ( LOG.isEnabledFor(Level.DEBUG) ) {
617             LOG.debug( "Decrypting " + encryptedPrivKey + "/" + algorithm + " with '" + new String(password) + "'" );
618         }
619         
620         PBEKeySpec pbeKeySpec = new PBEKeySpec(password);
621         
622         try {
623             AlgorithmParameters algo = encryptedPrivKey.getAlgParameters();
624             
625             if ( null == algo ) {
626                 if ( LOG.isEnabledFor(Level.WARN) ) {
627                     LOG.warn( "Could not get algo parameters from " + encryptedPrivKey );
628                 }
629                 
630                 throw new IllegalStateException( "Could not get algo parameters from " + encryptedPrivKey );
631             }
632             
633             PBEParameterSpec pbeParamSpec = (PBEParameterSpec) algo.getParameterSpec( PBEParameterSpec.class );
634             
635             // convert password into a SecretKey object, using a PBE key factory.
636             SecretKeyFactory keyFac = SecretKeyFactory.getInstance( PKCS5_PBSE1_ALGO );
637             SecretKey pbeKey = keyFac.generateSecret(pbeKeySpec);
638             
639             // Create PBE Cipher
640             Cipher pbeCipher = Cipher.getInstance( PKCS5_PBSE1_ALGO );
641             
642             // Initialize PBE Cipher with key and parameters
643             pbeCipher.init(Cipher.DECRYPT_MODE, pbeKey, pbeParamSpec);
644             
645             KeySpec key_spec;
646             try {
647                 key_spec = encryptedPrivKey.getKeySpec( pbeCipher );
648             } catch ( InvalidKeySpecException failed ) {
649                 if ( LOG.isEnabledFor(Level.WARN) ) {
650                     LOG.warn( "Incorrect key for " + encryptedPrivKey + " : " + failed );
651                 }
652                 return null;
653             }
654             
655             KeyFactory kf = KeyFactory.getInstance( algorithm );
656             
657             return kf.generatePrivate( key_spec );
658         } catch ( Exception failed ) {
659             if ( LOG.isEnabledFor(Level.WARN) ) {
660                 LOG.warn( "Decrypt failed", failed );
661             }
662             return null;
663         }
664     }
665     
666     // Load a wrapped object in base64 format:
667     //   The following three methods were modified
668     //   from similar pureTLS methods.
669     /**
670      * WrappedObject.java
671      *
672      * Copyright (C) 1999, Claymore Systems, Inc.
673      * All Rights Reserved.
674      *
675      * ekr@rtfm.com  Fri Jun  4 09:11:27 1999
676      *
677      * This package is a SSLv3/TLS implementation written by Eric Rescorla
678      * <ekr@rtfm.com> and licensed by Claymore Systems, Inc.
679      *
680      * Redistribution and use in source and binary forms, with or without
681      * modification, are permitted provided that the following conditions
682      * are met:
683      * 1. Redistributions of source code must retain the above copyright
684      * notice, this list of conditions and the following disclaimer.
685      * 2. Redistributions in binary form must reproduce the above copyright
686      * notice, this list of conditions and the following disclaimer in the
687      * documentation and/or other materials provided with the distribution.
688      * 3. All advertising materials mentioning features or use of this software
689      * must display the following acknowledgement:
690      * This product includes software developed by Claymore Systems, Inc.
691      * 4. Neither the name of Claymore Systems, Inc. nor the name of Eric
692      * Rescorla may be used to endorse or promote products derived from this
693      * software without specific prior written permission.
694      *
695      * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
696      * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
697      * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
698      * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
699      * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
700      * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
701      * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
702      * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
703      * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
704      * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
705      * SUCH DAMAGE.
706      *
707      */
708     
709     public static String loadBase64Object( BufferedReader rdr, String type ) throws IOException {
710         if( null != findObject( rdr, type ) ) {
711             return readBase64Object( rdr, type );
712         } else {
713             return null;
714         }
715     }
716     
717     public static byte [] loadObject( BufferedReader rdr, String type ) throws IOException {
718         if( null != findObject( rdr, type ) ) {
719             return readObject( rdr, type );
720         } else {
721             return null;
722         }
723     }
724     
725     public static String findObject( BufferedReader br, String type ) throws IOException {
726         String prefix = "-----BEGIN ";
727         String suffix = (type == null) ? "-----" : type + "-----";
728         
729         while( true ) {
730             br.mark( 1024 );
731             
732             String line = br.readLine();
733             
734             if( null == line ) {
735                 return null;
736             }
737             
738             if(!line.startsWith(prefix)) {
739                 continue;
740             }
741             
742             if(!line.endsWith(suffix)) {
743                 continue;
744             }
745             
746             br.reset();
747             
748             return line.substring( prefix.length(), line.length() - 5);
749         }
750     }
751     
752     /**
753      * We read a block of n-lines (\n terminated) and return a String of n-lines
754      * concatenated together. This keeps the format consistent with the pureTLS
755      * requirements.
756      **/
757     public static String readBase64Object( BufferedReader br, String type ) throws IOException {
758         String line = br.readLine();
759         
760         String prefix = "-----BEGIN ";
761         String suffix = (type == null) ? "-----" : type + "-----";
762         
763         if ( !line.startsWith(prefix) || !line.endsWith(suffix) ) {
764             throw new IOException( "Not at begining of object" );
765         }
766         
767         StringBuffer block = new StringBuffer();
768         
769         while (true) {
770             line = br.readLine();
771             
772             if (null == line ) {
773                 break;
774             }
775             
776             if (line.startsWith("-----END ")) {
777                 break;
778             }
779             
780             block.append( line );
781             block.append('\n');
782         }
783         
784         return block.toString();
785     }
786     
787     /**
788      *  Read an object
789      **/
790     public static byte[] readObject( BufferedReader br, String type ) throws IOException {
791         String base64 = readBase64Object( br, type );
792         return base64Decode( new StringReader(base64) );
793     }
794     
795     /**
796      *
797      **/
798     
799     /**
800      *  Write an ovject that is already base64 encoded.
801      **/
802     public static void writeBase64Object( BufferedWriter bw, String type, String object ) throws IOException {
803         
804         bw.write( "-----BEGIN " );
805         bw.write( type );
806         bw.write( "-----" );
807         bw.newLine();
808         
809         bw.write( object );
810         
811         char lastChar = object.charAt( object.length() - 1 );
812         if( ('\n' != lastChar) && ('\r' != lastChar) ) {
813             bw.newLine();
814         }
815         
816         bw.write( "-----END " );
817         bw.write( type );
818         bw.write( "-----" );
819         bw.newLine();
820         
821         bw.flush();
822     }
823     
824     public static void writeObject( BufferedWriter out, String type, byte [] object ) throws IOException {
825         String base64 = base64Encode( object );
826         writeBase64Object( out, type, base64 );
827     }
828     
829     /**
830      *  Convert a byte array into a BASE64 encoded String.
831      *
832      *  @param in the bytes to be converted
833      *  @return the BASE64 encoded String.
834      **/
835     public static String base64Encode( byte[] in ) throws IOException {
836         StringWriter base64 = new StringWriter();
837         
838         BASE64OutputStream b64os = new BASE64OutputStream( base64, 72 );
839         b64os.write(in);
840         b64os.close();
841         
842         String encoded = base64.toString();
843         
844         if ( LOG.isEnabledFor(Level.DEBUG) ) {
845             LOG.debug( "Encoded " + in.length + " bytes -> " + encoded.length() + " characters." );
846         }
847         
848         return encoded;
849     }
850     
851     /**
852      * Convert a BASE64 Encoded String into byte array.
853      *
854      *  @param the BASE64 encoded String
855      *  @return the decoded bytes.
856      **/
857     public static byte[] base64Decode( Reader in ) throws IOException {
858         BASE64InputStream b64is = new BASE64InputStream( in );
859         ByteArrayOutputStream bos = new ByteArrayOutputStream();
860         
861         
862         do {
863             int c = b64is.read( );
864             
865             if ( c < 0 ) {
866                 break;
867             }
868             
869             bos.write( c );
870         } while( true );
871         
872         byte[] result = bos.toByteArray();
873         
874         if ( LOG.isEnabledFor(Level.DEBUG) ) {
875             LOG.debug( "Decoded " + result.length + " bytes." );
876         }
877         
878         return result;
879     }
880     
881     /**
882      *  Private replacement for toHexString since we need the leading 0 digits.
883      *  Returns a String containing byte value encoded as 2 hex characters.
884      *
885      *  @param  theByte a byte containing the value to be encoded.
886      *  @return  String containing byte value encoded as 2 hex characters.
887      **/
888     private static String toHexDigits( byte theByte ) {
889         final char [] HEXDIGITS = { '0', '1', '2', '3', '4', '5', '6', '7',
890         '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
891         StringBuffer result = new StringBuffer(2);
892         
893         result.append( HEXDIGITS[(theByte >>> 4) & 15] );
894         result.append( HEXDIGITS[theByte & 15] );
895         
896         return result.toString();
897     }
898     
899     private static String toHexDigits( byte[] bytes ) {
900         StringBuffer    encoded = new StringBuffer(bytes.length * 2);
901         
902         // build the string.
903         for( int eachByte = 0; eachByte < bytes.length; eachByte++ ) {
904             encoded.append( toHexDigits(bytes[eachByte]).toUpperCase() );
905         }
906         
907         return encoded.toString();
908     }
909 }