Source for gnu.javax.crypto.jce.keyring.GnuKeyring

   1: /* GnuKeyring.java -- KeyStore adapter for a pair of private and public Keyrings
   2:    Copyright (C) 2003, 2006  Free Software Foundation, Inc.
   3: 
   4: This file is a part of GNU Classpath.
   5: 
   6: GNU Classpath is free software; you can redistribute it and/or modify
   7: it under the terms of the GNU General Public License as published by
   8: the Free Software Foundation; either version 2 of the License, or (at
   9: your option) any later version.
  10: 
  11: GNU Classpath is distributed in the hope that it will be useful, but
  12: WITHOUT ANY WARRANTY; without even the implied warranty of
  13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  14: General Public License for more details.
  15: 
  16: You should have received a copy of the GNU General Public License
  17: along with GNU Classpath; if not, write to the Free Software
  18: Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
  19: USA
  20: 
  21: Linking this library statically or dynamically with other modules is
  22: making a combined work based on this library.  Thus, the terms and
  23: conditions of the GNU General Public License cover the whole
  24: combination.
  25: 
  26: As a special exception, the copyright holders of this library give you
  27: permission to link this library with independent modules to produce an
  28: executable, regardless of the license terms of these independent
  29: modules, and to copy and distribute the resulting executable under
  30: terms of your choice, provided that you also meet, for each linked
  31: independent module, the terms and conditions of the license of that
  32: module.  An independent module is a module which is not derived from
  33: or based on this library.  If you modify this library, you may extend
  34: this exception to your version of the library, but you are not
  35: obligated to do so.  If you do not wish to do so, delete this
  36: exception statement from your version.  */
  37: 
  38: 
  39: package gnu.javax.crypto.jce.keyring;
  40: 
  41: import gnu.java.security.Configuration;
  42: import gnu.java.security.Registry;
  43: import gnu.javax.crypto.keyring.GnuPrivateKeyring;
  44: import gnu.javax.crypto.keyring.GnuPublicKeyring;
  45: import gnu.javax.crypto.keyring.IKeyring;
  46: import gnu.javax.crypto.keyring.IPrivateKeyring;
  47: import gnu.javax.crypto.keyring.IPublicKeyring;
  48: import gnu.javax.crypto.keyring.MalformedKeyringException;
  49: import gnu.javax.crypto.keyring.PrimitiveEntry;
  50: 
  51: import java.io.BufferedInputStream;
  52: import java.io.IOException;
  53: import java.io.InputStream;
  54: import java.io.OutputStream;
  55: import java.security.Key;
  56: import java.security.KeyStoreException;
  57: import java.security.KeyStoreSpi;
  58: import java.security.PrivateKey;
  59: import java.security.PublicKey;
  60: import java.security.UnrecoverableKeyException;
  61: import java.security.cert.Certificate;
  62: import java.util.Collections;
  63: import java.util.Date;
  64: import java.util.Enumeration;
  65: import java.util.HashMap;
  66: import java.util.HashSet;
  67: import java.util.Iterator;
  68: import java.util.Set;
  69: import java.util.logging.Logger;
  70: 
  71: import javax.crypto.SecretKey;
  72: 
  73: /**
  74:  * An <i>Adapter</i> over a pair of one private, and one public keyrings to
  75:  * emulate the keystore operations.
  76:  */
  77: public class GnuKeyring
  78:     extends KeyStoreSpi
  79: {
  80:   private static final Logger log = Logger.getLogger(GnuKeyring.class.getName());
  81:   private static final String NOT_LOADED = "not loaded";
  82: 
  83:   /** TRUE if the keystore is loaded; FALSE otherwise. */
  84:   private boolean loaded;
  85:   /** our underlying private keyring. */
  86:   private IPrivateKeyring privateKR;
  87:   /** our underlying public keyring. */
  88:   private IPublicKeyring publicKR;
  89: 
  90:   // default 0-arguments constructor
  91: 
  92:   public Enumeration engineAliases()
  93:   {
  94:     if (Configuration.DEBUG)
  95:       log.entering(this.getClass().getName(), "engineAliases");
  96:     ensureLoaded();
  97:     Enumeration result;
  98:     if (privateKR == null)
  99:       result = Collections.enumeration(Collections.EMPTY_SET);
 100:     else
 101:       {
 102:         Set aliases = new HashSet();
 103:         for (Enumeration e = privateKR.aliases(); e.hasMoreElements();)
 104:           {
 105:             String alias = (String) e.nextElement();
 106:             if (alias != null)
 107:               {
 108:                 alias = alias.trim();
 109:                 if (alias.length() > 0)
 110:                   {
 111:                     if (Configuration.DEBUG)
 112:                       log.fine("Adding alias (from private keyring): " + alias);
 113:                     aliases.add(alias);
 114:                   }
 115:               }
 116:           }
 117:         for (Enumeration e = publicKR.aliases(); e.hasMoreElements();)
 118:           {
 119:             String alias = (String) e.nextElement();
 120:             if (alias != null)
 121:               {
 122:                 alias = alias.trim();
 123:                 if (alias.length() > 0)
 124:                   {
 125:                     if (Configuration.DEBUG)
 126:                       log.fine("Adding alias (from public keyring): " + alias);
 127:                     aliases.add(alias);
 128:                   }
 129:               }
 130:           }
 131:         if (Configuration.DEBUG)
 132:           log.fine("Will enumerate: " + aliases);
 133:         result = Collections.enumeration(aliases);
 134:       }
 135:     if (Configuration.DEBUG)
 136:       log.exiting(this.getClass().getName(), "engineAliases");
 137:     return result;
 138:   }
 139: 
 140:   public boolean engineContainsAlias(String alias)
 141:   {
 142:     if (Configuration.DEBUG)
 143:       log.entering(this.getClass().getName(), "engineContainsAlias", alias);
 144:     ensureLoaded();
 145:     boolean inPrivateKR = privateKR.containsAlias(alias);
 146:     if (Configuration.DEBUG)
 147:       log.fine("inPrivateKR=" + inPrivateKR);
 148:     boolean inPublicKR = publicKR.containsAlias(alias);
 149:     if (Configuration.DEBUG)
 150:       log.fine("inPublicKR=" + inPublicKR);
 151:     boolean result = inPrivateKR || inPublicKR;
 152:     if (Configuration.DEBUG)
 153:       log.exiting(this.getClass().getName(), "engineContainsAlias",
 154:                   Boolean.valueOf(result));
 155:     return result;
 156:   }
 157: 
 158:   public void engineDeleteEntry(String alias)
 159:   {
 160:     if (Configuration.DEBUG)
 161:       log.entering(this.getClass().getName(), "engineDeleteEntry", alias);
 162:     ensureLoaded();
 163:     if (privateKR.containsAlias(alias))
 164:       privateKR.remove(alias);
 165:     else if (publicKR.containsAlias(alias))
 166:       publicKR.remove(alias);
 167:     else if (Configuration.DEBUG)
 168:       log.fine("Unknwon alias: " + alias);
 169:     if (Configuration.DEBUG)
 170:       log.exiting(this.getClass().getName(), "engineDeleteEntry");
 171:   }
 172: 
 173:   public Certificate engineGetCertificate(String alias)
 174:   {
 175:     if (Configuration.DEBUG)
 176:       log.entering(this.getClass().getName(), "engineGetCertificate", alias);
 177:     ensureLoaded();
 178:     Certificate result = publicKR.getCertificate(alias);
 179:     if (Configuration.DEBUG)
 180:       log.exiting(this.getClass().getName(), "engineGetCertificate", result);
 181:     return result;
 182:   }
 183: 
 184:   public String engineGetCertificateAlias(Certificate cert)
 185:   {
 186:     if (Configuration.DEBUG)
 187:       log.entering(this.getClass().getName(), "engineGetCertificateAlias", cert);
 188:     ensureLoaded();
 189:     String result = null;
 190:     for (Enumeration aliases = publicKR.aliases(); aliases.hasMoreElements();)
 191:       {
 192:         String alias = (String) aliases.nextElement();
 193:         Certificate cert2 = publicKR.getCertificate(alias);
 194:         if (cert.equals(cert2))
 195:           {
 196:             result = alias;
 197:             break;
 198:           }
 199:       }
 200:     if (Configuration.DEBUG)
 201:       log.exiting(this.getClass().getName(), "engineGetCertificateAlias", result);
 202:     return result;
 203:   }
 204: 
 205:   public void engineSetCertificateEntry(String alias, Certificate cert)
 206:       throws KeyStoreException
 207:   {
 208:     if (Configuration.DEBUG)
 209:       log.entering(this.getClass().getName(), "engineSetCertificateEntry",
 210:                    new Object[] { alias, cert });
 211:     ensureLoaded();
 212:     if (privateKR.containsAlias(alias))
 213:       throw new KeyStoreException("Alias [" + alias
 214:                                   + "] already exists and DOES NOT identify a "
 215:                                   + "Trusted Certificate Entry");
 216:     if (publicKR.containsCertificate(alias))
 217:       {
 218:         if (Configuration.DEBUG)
 219:           log.fine("Public keyring already contains Alias [" + alias
 220:                    + "]. Will remove it");
 221:         publicKR.remove(alias);
 222:       }
 223:     publicKR.putCertificate(alias, cert);
 224:     if (Configuration.DEBUG)
 225:       log.exiting(this.getClass().getName(), "engineSetCertificateEntry");
 226:   }
 227: 
 228:   public Certificate[] engineGetCertificateChain(String alias)
 229:   {
 230:     if (Configuration.DEBUG)
 231:       log.entering(this.getClass().getName(), "engineGetCertificateChain", alias);
 232:     ensureLoaded();
 233:     Certificate[] result = privateKR.getCertPath(alias);
 234:     if (Configuration.DEBUG)
 235:       log.exiting(this.getClass().getName(), "engineGetCertificateChain", result);
 236:     return result;
 237:   }
 238: 
 239:   public Date engineGetCreationDate(String alias)
 240:   {
 241:     if (Configuration.DEBUG)
 242:       log.entering(this.getClass().getName(), "engineGetCreationDate", alias);
 243:     ensureLoaded();
 244:     Date result = getCreationDate(alias, privateKR);
 245:     if (result == null)
 246:       result = getCreationDate(alias, publicKR);
 247: 
 248:     if (Configuration.DEBUG)
 249:       log.exiting(this.getClass().getName(), "engineGetCreationDate", result);
 250:     return result;
 251:   }
 252: 
 253:   public Key engineGetKey(String alias, char[] password)
 254:       throws UnrecoverableKeyException
 255:   {
 256:     if (Configuration.DEBUG)
 257:       log.entering(this.getClass().getName(), "engineGetKey", alias);
 258:     ensureLoaded();
 259:     Key result = null;
 260:     if (password == null)
 261:       {
 262:         if (privateKR.containsPublicKey(alias))
 263:           result = privateKR.getPublicKey(alias);
 264:       }
 265:     else if (privateKR.containsPrivateKey(alias))
 266:       result = privateKR.getPrivateKey(alias, password); 
 267: 
 268:     if (Configuration.DEBUG)
 269:       log.exiting(this.getClass().getName(), "engineGetKey",
 270:                   result == null ? "null" : result.getClass().getName());
 271:     return result;
 272:   }
 273: 
 274:   public void engineSetKeyEntry(String alias, Key key, char[] password,
 275:                                 Certificate[] chain)
 276:       throws KeyStoreException
 277:   {
 278:     if (Configuration.DEBUG)
 279:       log.entering(this.getClass().getName(), "engineSetKeyEntry",
 280:                    new Object[] { alias, key.getClass().getName(), chain });
 281:     ensureLoaded();
 282:     if (publicKR.containsAlias(alias))
 283:       throw new KeyStoreException("Alias [" + alias
 284:                                   + "] already exists and DOES NOT identify a "
 285:                                   + "Key Entry");
 286:     if (key instanceof PublicKey)
 287:       {
 288:         privateKR.remove(alias);
 289:         PublicKey pk = (PublicKey) key;
 290:         privateKR.putPublicKey(alias, pk);
 291:       }
 292:     else
 293:       {
 294:         if (! (key instanceof PrivateKey) && ! (key instanceof SecretKey))
 295:           throw new KeyStoreException("cannot store keys of type "
 296:                                       + key.getClass().getName());
 297:         privateKR.remove(alias);
 298:         privateKR.putCertPath(alias, chain);
 299:         if (Configuration.DEBUG)
 300:           log.fine("About to put private key in keyring...");
 301:         privateKR.putPrivateKey(alias, key, password);
 302:       }
 303:     if (Configuration.DEBUG)
 304:       log.exiting(this.getClass().getName(), "engineSetKeyEntry");
 305:   }
 306: 
 307:   public void engineSetKeyEntry(String alias, byte[] key, Certificate[] chain)
 308:       throws KeyStoreException
 309:   {
 310:     KeyStoreException x = new KeyStoreException("method not supported");
 311:     if (Configuration.DEBUG)
 312:       log.throwing(this.getClass().getName(), "engineSetKeyEntry(3)", x);
 313:     throw x;
 314:   }
 315: 
 316:   public boolean engineIsCertificateEntry(String alias)
 317:   {
 318:     if (Configuration.DEBUG)
 319:       log.entering(this.getClass().getName(), "engineIsCertificateEntry", alias);
 320:     ensureLoaded();
 321:     boolean result = publicKR.containsCertificate(alias);
 322:     if (Configuration.DEBUG)
 323:       log.exiting(this.getClass().getName(), "engineIsCertificateEntry",
 324:                   Boolean.valueOf(result));
 325:     return result;
 326:   }
 327: 
 328:   public boolean engineIsKeyEntry(String alias)
 329:   {
 330:     if (Configuration.DEBUG)
 331:       log.entering(this.getClass().getName(), "engineIsKeyEntry", alias);
 332:     ensureLoaded();
 333:     boolean result = privateKR.containsPublicKey(alias)
 334:                   || privateKR.containsPrivateKey(alias);
 335:     if (Configuration.DEBUG)
 336:       log.exiting(this.getClass().getName(), "engineIsKeyEntry",
 337:                   Boolean.valueOf(result));
 338:     return result;
 339:   }
 340: 
 341:   public void engineLoad(InputStream in, char[] password) throws IOException
 342:   {
 343:     if (Configuration.DEBUG)
 344:       log.entering(this.getClass().getName(), "engineLoad");
 345:     if (in != null)
 346:       {
 347:         if (! in.markSupported())
 348:           in = new BufferedInputStream(in);
 349: 
 350:         loadPrivateKeyring(in, password);
 351:         loadPublicKeyring(in, password);
 352:       }
 353:     else
 354:       createNewKeyrings();
 355: 
 356:     loaded = true;
 357:     if (Configuration.DEBUG)
 358:       log.exiting(this.getClass().getName(), "engineLoad");
 359:   }
 360: 
 361:   public void engineStore(OutputStream out, char[] password) throws IOException
 362:   {
 363:     if (Configuration.DEBUG)
 364:       log.entering(this.getClass().getName(), "engineStore");
 365:     ensureLoaded();
 366:     HashMap attr = new HashMap();
 367:     attr.put(IKeyring.KEYRING_DATA_OUT, out);
 368:     attr.put(IKeyring.KEYRING_PASSWORD, password);
 369: 
 370:     privateKR.store(attr);
 371:     publicKR.store(attr);
 372:     if (Configuration.DEBUG)
 373:       log.exiting(this.getClass().getName(), "engineStore");
 374:   }
 375: 
 376:   public int engineSize()
 377:   {
 378:     if (Configuration.DEBUG)
 379:       log.entering(this.getClass().getName(), "engineSize");
 380:     int result = 0;
 381:     for (Enumeration e = engineAliases(); e.hasMoreElements(); result++)
 382:       e.nextElement();
 383: 
 384:     if (Configuration.DEBUG)
 385:       log.exiting(this.getClass().getName(), "engineSize", Integer.valueOf(result));
 386:     return result;
 387:   }
 388: 
 389:   /**
 390:    * Ensure that the underlying keyring pair is loaded. Throw an exception if it
 391:    * isn't; otherwise returns silently.
 392:    *
 393:    * @throws IllegalStateException if the keyring is not loaded.
 394:    */
 395:   private void ensureLoaded()
 396:   {
 397:     if (! loaded)
 398:       throw new IllegalStateException(NOT_LOADED);
 399:   }
 400: 
 401:   /**
 402:    * Load the private keyring from the designated input stream.
 403:    * 
 404:    * @param in the input stream to process.
 405:    * @param password the password protecting the keyring.
 406:    * @throws MalformedKeyringException if the keyring is not a private one.
 407:    * @throws IOException if an I/O related exception occurs during the process.
 408:    */
 409:   private void loadPrivateKeyring(InputStream in, char[] password)
 410:       throws MalformedKeyringException, IOException
 411:   {
 412:     if (Configuration.DEBUG)
 413:       log.entering(this.getClass().getName(), "loadPrivateKeyring");
 414:     in.mark(5);
 415:     for (int i = 0; i < 4; i++)
 416:       if (in.read() != Registry.GKR_MAGIC[i])
 417:         throw new MalformedKeyringException("incorrect magic");
 418: 
 419:     int usage = in.read();
 420:     in.reset();
 421:     if (usage != GnuPrivateKeyring.USAGE)
 422:       throw new MalformedKeyringException(
 423:           "Was expecting a private keyring but got a wrong USAGE: "
 424:           + Integer.toBinaryString(usage));
 425:     HashMap attr = new HashMap();
 426:     attr.put(IKeyring.KEYRING_DATA_IN, in);
 427:     attr.put(IKeyring.KEYRING_PASSWORD, password);
 428:     privateKR = new GnuPrivateKeyring();
 429:     privateKR.load(attr);
 430:     if (Configuration.DEBUG)
 431:       log.exiting(this.getClass().getName(), "loadPrivateKeyring");
 432:   }
 433: 
 434:   /**
 435:    * Load the public keyring from the designated input stream.
 436:    * 
 437:    * @param in the input stream to process.
 438:    * @param password the password protecting the keyring.
 439:    * @throws MalformedKeyringException if the keyring is not a public one.
 440:    * @throws IOException if an I/O related exception occurs during the process.
 441:    */
 442:   private void loadPublicKeyring(InputStream in, char[] password)
 443:       throws MalformedKeyringException, IOException
 444:   {
 445:     if (Configuration.DEBUG)
 446:       log.entering(this.getClass().getName(), "loadPublicKeyring");
 447:     in.mark(5);
 448:     for (int i = 0; i < 4; i++)
 449:       if (in.read() != Registry.GKR_MAGIC[i])
 450:         throw new MalformedKeyringException("incorrect magic");
 451: 
 452:     int usage = in.read();
 453:     in.reset();
 454:     if (usage != GnuPublicKeyring.USAGE)
 455:       throw new MalformedKeyringException(
 456:           "Was expecting a public keyring but got a wrong USAGE: "
 457:           + Integer.toBinaryString(usage));
 458:     HashMap attr = new HashMap();
 459:     attr.put(IKeyring.KEYRING_DATA_IN, in);
 460:     attr.put(IKeyring.KEYRING_PASSWORD, password);
 461:     publicKR = new GnuPublicKeyring();
 462:     publicKR.load(attr);
 463:     if (Configuration.DEBUG)
 464:       log.exiting(this.getClass().getName(), "loadPublicKeyring");
 465:   }
 466: 
 467:   /**
 468:    * Return the creation date of a named alias in a designated keyring.
 469:    * 
 470:    * @param alias the alias to look for.
 471:    * @param keyring the keyring to search.
 472:    * @return the creattion date of the entry named <code>alias</code>. Return
 473:    *         <code>null</code> if <code>alias</code> was not found in
 474:    *         <code>keyring</code>.
 475:    */
 476:   private Date getCreationDate(String alias, IKeyring keyring)
 477:   {
 478:     if (Configuration.DEBUG)
 479:       log.entering(this.getClass().getName(), "getCreationDate",
 480:                    new Object[] { alias, keyring });
 481:     Date result = null;
 482:     if (keyring != null)
 483:       for (Iterator it = keyring.get(alias).iterator(); it.hasNext();)
 484:         {
 485:           Object o = it.next();
 486:           if (o instanceof PrimitiveEntry)
 487:             {
 488:               result = ((PrimitiveEntry) o).getCreationDate();
 489:               break;
 490:             }
 491:         }
 492:     if (Configuration.DEBUG)
 493:       log.exiting(this.getClass().getName(), "getCreationDate", result);
 494:     return result;
 495:   }
 496: 
 497:   /** Create empty keyrings. */
 498:   private void createNewKeyrings()
 499:   {
 500:     if (Configuration.DEBUG)
 501:       log.entering(this.getClass().getName(), "createNewKeyrings");
 502:     privateKR = new GnuPrivateKeyring("HMAC-SHA-1", 20, "AES", "OFB", 16);
 503:     publicKR = new GnuPublicKeyring("HMAC-SHA-1", 20);
 504:     if (Configuration.DEBUG)
 505:       log.exiting(this.getClass().getName(), "createNewKeyrings");
 506:   }
 507: }