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, 2010  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 = Configuration.DEBUG ?
  81:                         Logger.getLogger(GnuKeyring.class.getName()) : null;
  82:   private static final String NOT_LOADED = "not loaded";
  83: 
  84:   /** TRUE if the keystore is loaded; FALSE otherwise. */
  85:   private boolean loaded;
  86:   /** our underlying private keyring. */
  87:   private IPrivateKeyring privateKR;
  88:   /** our underlying public keyring. */
  89:   private IPublicKeyring publicKR;
  90: 
  91:   // default 0-arguments constructor
  92: 
  93:   public Enumeration engineAliases()
  94:   {
  95:     if (Configuration.DEBUG)
  96:       log.entering(this.getClass().getName(), "engineAliases");
  97:     ensureLoaded();
  98:     Enumeration result;
  99:     if (privateKR == null)
 100:       result = Collections.enumeration(Collections.EMPTY_SET);
 101:     else
 102:       {
 103:         Set aliases = new HashSet();
 104:         for (Enumeration e = privateKR.aliases(); e.hasMoreElements();)
 105:           {
 106:             String alias = (String) e.nextElement();
 107:             if (alias != null)
 108:               {
 109:                 alias = alias.trim();
 110:                 if (alias.length() > 0)
 111:                   {
 112:                     if (Configuration.DEBUG)
 113:                       log.fine("Adding alias (from private keyring): " + alias);
 114:                     aliases.add(alias);
 115:                   }
 116:               }
 117:           }
 118:         for (Enumeration e = publicKR.aliases(); e.hasMoreElements();)
 119:           {
 120:             String alias = (String) e.nextElement();
 121:             if (alias != null)
 122:               {
 123:                 alias = alias.trim();
 124:                 if (alias.length() > 0)
 125:                   {
 126:                     if (Configuration.DEBUG)
 127:                       log.fine("Adding alias (from public keyring): " + alias);
 128:                     aliases.add(alias);
 129:                   }
 130:               }
 131:           }
 132:         if (Configuration.DEBUG)
 133:           log.fine("Will enumerate: " + aliases);
 134:         result = Collections.enumeration(aliases);
 135:       }
 136:     if (Configuration.DEBUG)
 137:       log.exiting(this.getClass().getName(), "engineAliases");
 138:     return result;
 139:   }
 140: 
 141:   public boolean engineContainsAlias(String alias)
 142:   {
 143:     if (Configuration.DEBUG)
 144:       log.entering(this.getClass().getName(), "engineContainsAlias", alias);
 145:     ensureLoaded();
 146:     boolean inPrivateKR = privateKR.containsAlias(alias);
 147:     if (Configuration.DEBUG)
 148:       log.fine("inPrivateKR=" + inPrivateKR);
 149:     boolean inPublicKR = publicKR.containsAlias(alias);
 150:     if (Configuration.DEBUG)
 151:       log.fine("inPublicKR=" + inPublicKR);
 152:     boolean result = inPrivateKR || inPublicKR;
 153:     if (Configuration.DEBUG)
 154:       log.exiting(this.getClass().getName(), "engineContainsAlias",
 155:                   Boolean.valueOf(result));
 156:     return result;
 157:   }
 158: 
 159:   public void engineDeleteEntry(String alias)
 160:   {
 161:     if (Configuration.DEBUG)
 162:       log.entering(this.getClass().getName(), "engineDeleteEntry", alias);
 163:     ensureLoaded();
 164:     if (privateKR.containsAlias(alias))
 165:       privateKR.remove(alias);
 166:     else if (publicKR.containsAlias(alias))
 167:       publicKR.remove(alias);
 168:     else if (Configuration.DEBUG)
 169:       log.fine("Unknwon alias: " + alias);
 170:     if (Configuration.DEBUG)
 171:       log.exiting(this.getClass().getName(), "engineDeleteEntry");
 172:   }
 173: 
 174:   public Certificate engineGetCertificate(String alias)
 175:   {
 176:     if (Configuration.DEBUG)
 177:       log.entering(this.getClass().getName(), "engineGetCertificate", alias);
 178:     ensureLoaded();
 179:     Certificate result = publicKR.getCertificate(alias);
 180:     if (Configuration.DEBUG)
 181:       log.exiting(this.getClass().getName(), "engineGetCertificate", result);
 182:     return result;
 183:   }
 184: 
 185:   public String engineGetCertificateAlias(Certificate cert)
 186:   {
 187:     if (Configuration.DEBUG)
 188:       log.entering(this.getClass().getName(), "engineGetCertificateAlias", cert);
 189:     ensureLoaded();
 190:     String result = null;
 191:     for (Enumeration aliases = publicKR.aliases(); aliases.hasMoreElements();)
 192:       {
 193:         String alias = (String) aliases.nextElement();
 194:         Certificate cert2 = publicKR.getCertificate(alias);
 195:         if (cert.equals(cert2))
 196:           {
 197:             result = alias;
 198:             break;
 199:           }
 200:       }
 201:     if (Configuration.DEBUG)
 202:       log.exiting(this.getClass().getName(), "engineGetCertificateAlias", result);
 203:     return result;
 204:   }
 205: 
 206:   public void engineSetCertificateEntry(String alias, Certificate cert)
 207:       throws KeyStoreException
 208:   {
 209:     if (Configuration.DEBUG)
 210:       log.entering(this.getClass().getName(), "engineSetCertificateEntry",
 211:                    new Object[] { alias, cert });
 212:     ensureLoaded();
 213:     if (privateKR.containsAlias(alias))
 214:       throw new KeyStoreException("Alias [" + alias
 215:                                   + "] already exists and DOES NOT identify a "
 216:                                   + "Trusted Certificate Entry");
 217:     if (publicKR.containsCertificate(alias))
 218:       {
 219:         if (Configuration.DEBUG)
 220:           log.fine("Public keyring already contains Alias [" + alias
 221:                    + "]. Will remove it");
 222:         publicKR.remove(alias);
 223:       }
 224:     publicKR.putCertificate(alias, cert);
 225:     if (Configuration.DEBUG)
 226:       log.exiting(this.getClass().getName(), "engineSetCertificateEntry");
 227:   }
 228: 
 229:   public Certificate[] engineGetCertificateChain(String alias)
 230:   {
 231:     if (Configuration.DEBUG)
 232:       log.entering(this.getClass().getName(), "engineGetCertificateChain", alias);
 233:     ensureLoaded();
 234:     Certificate[] result = privateKR.getCertPath(alias);
 235:     if (Configuration.DEBUG)
 236:       log.exiting(this.getClass().getName(), "engineGetCertificateChain", result);
 237:     return result;
 238:   }
 239: 
 240:   public Date engineGetCreationDate(String alias)
 241:   {
 242:     if (Configuration.DEBUG)
 243:       log.entering(this.getClass().getName(), "engineGetCreationDate", alias);
 244:     ensureLoaded();
 245:     Date result = getCreationDate(alias, privateKR);
 246:     if (result == null)
 247:       result = getCreationDate(alias, publicKR);
 248: 
 249:     if (Configuration.DEBUG)
 250:       log.exiting(this.getClass().getName(), "engineGetCreationDate", result);
 251:     return result;
 252:   }
 253: 
 254:   public Key engineGetKey(String alias, char[] password)
 255:       throws UnrecoverableKeyException
 256:   {
 257:     if (Configuration.DEBUG)
 258:       log.entering(this.getClass().getName(), "engineGetKey", alias);
 259:     ensureLoaded();
 260:     Key result = null;
 261:     if (password == null)
 262:       {
 263:         if (privateKR.containsPublicKey(alias))
 264:           result = privateKR.getPublicKey(alias);
 265:       }
 266:     else if (privateKR.containsPrivateKey(alias))
 267:       result = privateKR.getPrivateKey(alias, password);
 268: 
 269:     if (Configuration.DEBUG)
 270:       log.exiting(this.getClass().getName(), "engineGetKey",
 271:                   result == null ? "null" : result.getClass().getName());
 272:     return result;
 273:   }
 274: 
 275:   public void engineSetKeyEntry(String alias, Key key, char[] password,
 276:                                 Certificate[] chain)
 277:       throws KeyStoreException
 278:   {
 279:     if (Configuration.DEBUG)
 280:       log.entering(this.getClass().getName(), "engineSetKeyEntry",
 281:                    new Object[] { alias, key.getClass().getName(), chain });
 282:     ensureLoaded();
 283:     if (publicKR.containsAlias(alias))
 284:       throw new KeyStoreException("Alias [" + alias
 285:                                   + "] already exists and DOES NOT identify a "
 286:                                   + "Key Entry");
 287:     if (key instanceof PublicKey)
 288:       {
 289:         privateKR.remove(alias);
 290:         PublicKey pk = (PublicKey) key;
 291:         privateKR.putPublicKey(alias, pk);
 292:       }
 293:     else
 294:       {
 295:         if (! (key instanceof PrivateKey) && ! (key instanceof SecretKey))
 296:           throw new KeyStoreException("cannot store keys of type "
 297:                                       + key.getClass().getName());
 298:         privateKR.remove(alias);
 299:         privateKR.putCertPath(alias, chain);
 300:         if (Configuration.DEBUG)
 301:           log.fine("About to put private key in keyring...");
 302:         privateKR.putPrivateKey(alias, key, password);
 303:       }
 304:     if (Configuration.DEBUG)
 305:       log.exiting(this.getClass().getName(), "engineSetKeyEntry");
 306:   }
 307: 
 308:   public void engineSetKeyEntry(String alias, byte[] key, Certificate[] chain)
 309:       throws KeyStoreException
 310:   {
 311:     KeyStoreException x = new KeyStoreException("method not supported");
 312:     if (Configuration.DEBUG)
 313:       log.throwing(this.getClass().getName(), "engineSetKeyEntry(3)", x);
 314:     throw x;
 315:   }
 316: 
 317:   public boolean engineIsCertificateEntry(String alias)
 318:   {
 319:     if (Configuration.DEBUG)
 320:       log.entering(this.getClass().getName(), "engineIsCertificateEntry", alias);
 321:     ensureLoaded();
 322:     boolean result = publicKR.containsCertificate(alias);
 323:     if (Configuration.DEBUG)
 324:       log.exiting(this.getClass().getName(), "engineIsCertificateEntry",
 325:                   Boolean.valueOf(result));
 326:     return result;
 327:   }
 328: 
 329:   public boolean engineIsKeyEntry(String alias)
 330:   {
 331:     if (Configuration.DEBUG)
 332:       log.entering(this.getClass().getName(), "engineIsKeyEntry", alias);
 333:     ensureLoaded();
 334:     boolean result = privateKR.containsPublicKey(alias)
 335:                   || privateKR.containsPrivateKey(alias);
 336:     if (Configuration.DEBUG)
 337:       log.exiting(this.getClass().getName(), "engineIsKeyEntry",
 338:                   Boolean.valueOf(result));
 339:     return result;
 340:   }
 341: 
 342:   public void engineLoad(InputStream in, char[] password) throws IOException
 343:   {
 344:     if (Configuration.DEBUG)
 345:       log.entering(this.getClass().getName(), "engineLoad");
 346:     if (in != null)
 347:       {
 348:         if (! in.markSupported())
 349:           in = new BufferedInputStream(in);
 350: 
 351:         loadPrivateKeyring(in, password);
 352:         loadPublicKeyring(in, password);
 353:       }
 354:     else
 355:       createNewKeyrings();
 356: 
 357:     loaded = true;
 358:     if (Configuration.DEBUG)
 359:       log.exiting(this.getClass().getName(), "engineLoad");
 360:   }
 361: 
 362:   public void engineStore(OutputStream out, char[] password) throws IOException
 363:   {
 364:     if (Configuration.DEBUG)
 365:       log.entering(this.getClass().getName(), "engineStore");
 366:     ensureLoaded();
 367:     HashMap attr = new HashMap();
 368:     attr.put(IKeyring.KEYRING_DATA_OUT, out);
 369:     attr.put(IKeyring.KEYRING_PASSWORD, password);
 370: 
 371:     privateKR.store(attr);
 372:     publicKR.store(attr);
 373:     if (Configuration.DEBUG)
 374:       log.exiting(this.getClass().getName(), "engineStore");
 375:   }
 376: 
 377:   public int engineSize()
 378:   {
 379:     if (Configuration.DEBUG)
 380:       log.entering(this.getClass().getName(), "engineSize");
 381:     int result = 0;
 382:     for (Enumeration e = engineAliases(); e.hasMoreElements(); result++)
 383:       e.nextElement();
 384: 
 385:     if (Configuration.DEBUG)
 386:       log.exiting(this.getClass().getName(), "engineSize", Integer.valueOf(result));
 387:     return result;
 388:   }
 389: 
 390:   /**
 391:    * Ensure that the underlying keyring pair is loaded. Throw an exception if it
 392:    * isn't; otherwise returns silently.
 393:    *
 394:    * @throws IllegalStateException if the keyring is not loaded.
 395:    */
 396:   private void ensureLoaded()
 397:   {
 398:     if (! loaded)
 399:       throw new IllegalStateException(NOT_LOADED);
 400:   }
 401: 
 402:   /**
 403:    * Load the private keyring from the designated input stream.
 404:    *
 405:    * @param in the input stream to process.
 406:    * @param password the password protecting the keyring.
 407:    * @throws MalformedKeyringException if the keyring is not a private one.
 408:    * @throws IOException if an I/O related exception occurs during the process.
 409:    */
 410:   private void loadPrivateKeyring(InputStream in, char[] password)
 411:       throws MalformedKeyringException, IOException
 412:   {
 413:     if (Configuration.DEBUG)
 414:       log.entering(this.getClass().getName(), "loadPrivateKeyring");
 415:     in.mark(5);
 416:     for (int i = 0; i < 4; i++)
 417:       if (in.read() != Registry.GKR_MAGIC[i])
 418:         throw new MalformedKeyringException("incorrect magic");
 419: 
 420:     int usage = in.read();
 421:     in.reset();
 422:     if (usage != GnuPrivateKeyring.USAGE)
 423:       throw new MalformedKeyringException(
 424:           "Was expecting a private keyring but got a wrong USAGE: "
 425:           + Integer.toBinaryString(usage));
 426:     HashMap attr = new HashMap();
 427:     attr.put(IKeyring.KEYRING_DATA_IN, in);
 428:     attr.put(IKeyring.KEYRING_PASSWORD, password);
 429:     privateKR = new GnuPrivateKeyring();
 430:     privateKR.load(attr);
 431:     if (Configuration.DEBUG)
 432:       log.exiting(this.getClass().getName(), "loadPrivateKeyring");
 433:   }
 434: 
 435:   /**
 436:    * Load the public keyring from the designated input stream.
 437:    *
 438:    * @param in the input stream to process.
 439:    * @param password the password protecting the keyring.
 440:    * @throws MalformedKeyringException if the keyring is not a public one.
 441:    * @throws IOException if an I/O related exception occurs during the process.
 442:    */
 443:   private void loadPublicKeyring(InputStream in, char[] password)
 444:       throws MalformedKeyringException, IOException
 445:   {
 446:     if (Configuration.DEBUG)
 447:       log.entering(this.getClass().getName(), "loadPublicKeyring");
 448:     in.mark(5);
 449:     for (int i = 0; i < 4; i++)
 450:       if (in.read() != Registry.GKR_MAGIC[i])
 451:         throw new MalformedKeyringException("incorrect magic");
 452: 
 453:     int usage = in.read();
 454:     in.reset();
 455:     if (usage != GnuPublicKeyring.USAGE)
 456:       throw new MalformedKeyringException(
 457:           "Was expecting a public keyring but got a wrong USAGE: "
 458:           + Integer.toBinaryString(usage));
 459:     HashMap attr = new HashMap();
 460:     attr.put(IKeyring.KEYRING_DATA_IN, in);
 461:     attr.put(IKeyring.KEYRING_PASSWORD, password);
 462:     publicKR = new GnuPublicKeyring();
 463:     publicKR.load(attr);
 464:     if (Configuration.DEBUG)
 465:       log.exiting(this.getClass().getName(), "loadPublicKeyring");
 466:   }
 467: 
 468:   /**
 469:    * Return the creation date of a named alias in a designated keyring.
 470:    *
 471:    * @param alias the alias to look for.
 472:    * @param keyring the keyring to search.
 473:    * @return the creattion date of the entry named <code>alias</code>. Return
 474:    *         <code>null</code> if <code>alias</code> was not found in
 475:    *         <code>keyring</code>.
 476:    */
 477:   private Date getCreationDate(String alias, IKeyring keyring)
 478:   {
 479:     if (Configuration.DEBUG)
 480:       log.entering(this.getClass().getName(), "getCreationDate",
 481:                    new Object[] { alias, keyring });
 482:     Date result = null;
 483:     if (keyring != null)
 484:       for (Iterator it = keyring.get(alias).iterator(); it.hasNext();)
 485:         {
 486:           Object o = it.next();
 487:           if (o instanceof PrimitiveEntry)
 488:             {
 489:               result = ((PrimitiveEntry) o).getCreationDate();
 490:               break;
 491:             }
 492:         }
 493:     if (Configuration.DEBUG)
 494:       log.exiting(this.getClass().getName(), "getCreationDate", result);
 495:     return result;
 496:   }
 497: 
 498:   /** Create empty keyrings. */
 499:   private void createNewKeyrings()
 500:   {
 501:     if (Configuration.DEBUG)
 502:       log.entering(this.getClass().getName(), "createNewKeyrings");
 503:     privateKR = new GnuPrivateKeyring("HMAC-SHA-1", 20, "AES", "OFB", 16);
 504:     publicKR = new GnuPublicKeyring("HMAC-SHA-1", 20);
 505:     if (Configuration.DEBUG)
 506:       log.exiting(this.getClass().getName(), "createNewKeyrings");
 507:   }
 508: }