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 }