001 /* 002 * $Id: KeyChain.java 3100 2008-10-14 22:33:10Z rah003 $ 003 * 004 * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, 005 * Santa Clara, California 95054, U.S.A. All rights reserved. 006 * 007 * This library is free software; you can redistribute it and/or 008 * modify it under the terms of the GNU Lesser General Public 009 * License as published by the Free Software Foundation; either 010 * version 2.1 of the License, or (at your option) any later version. 011 * 012 * This library is distributed in the hope that it will be useful, 013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 015 * Lesser General Public License for more details. 016 * 017 * You should have received a copy of the GNU Lesser General Public 018 * License along with this library; if not, write to the Free Software 019 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 020 */ 021 package org.jdesktop.swingx.auth; 022 023 import java.io.EOFException; 024 import java.io.File; 025 import java.io.FileInputStream; 026 import java.io.FileOutputStream; 027 import java.io.IOException; 028 import java.io.InputStream; 029 import java.io.OutputStream; 030 import java.security.KeyStore; 031 import java.security.KeyStoreException; 032 import java.security.NoSuchAlgorithmException; 033 import java.security.UnrecoverableEntryException; 034 import java.security.cert.CertificateException; 035 import java.util.logging.Level; 036 import java.util.logging.Logger; 037 038 import javax.crypto.spec.SecretKeySpec; 039 040 /** 041 * <b>KeyChain</b> is a class that implements the "KeyChain" concept. 042 * Fundamentally, it allows you to store multiple keys/credentials 043 * in a central password store. Access to this central store is 044 * controlled through a master password. This mechanism is used in 045 * many popular client applications where you need to store credentials 046 * for multiple servers/accounts. The actual store for the KeyStore 047 * can be any OutputStream and it can work in the webstart sandbox 048 * using Muffins. 049 * </p> 050 * <p> 051 * To contstruct a <b>KeyChain</b>, you need to pass in an InputStream to the 052 * store and it will initialize the KeyStore from the InputStream. 053 * You can add and remove entries any time once you have an instance of 054 * KeyChain. To persist the KeyChain and reflect any changes, you need to 055 * call <b>store</b> method with an OutputStream. 056 * </p> 057 * 058 * @author Bino George 059 */ 060 public class KeyChain { 061 private static final Logger LOG = Logger 062 .getLogger(KeyChain.class.getName()); 063 064 private KeyStore store; 065 066 private char[] masterPassword; 067 068 /** 069 * Creates an instance of KeyChain and initializes the store 070 * from the InputStream. 071 * 072 * @param masterPassword 073 * @param inputStream 074 * @throws IOException 075 */ 076 public KeyChain(char[] masterPassword, InputStream inputStream) 077 throws IOException { 078 this.masterPassword = masterPassword; 079 080 try { 081 store = KeyStore.getInstance("JCEKS"); 082 store.load(inputStream, masterPassword); 083 084 } catch (KeyStoreException ex) { 085 LOG.log(Level.WARNING, "", ex); 086 } catch (CertificateException ex) { 087 LOG.log(Level.WARNING, "", ex); 088 } catch (NoSuchAlgorithmException ex) { 089 LOG.log(Level.WARNING, "", ex); 090 } catch (EOFException ex) { 091 LOG.log(Level.WARNING, "", ex); 092 } 093 094 } 095 096 /** 097 * Fetches the password for a given account/user and server. 098 * @param user 099 * @param server 100 * @return <code>null</code> if no password could be obtained, the password 101 * otherwise 102 */ 103 public String getPassword(String user, String server) { 104 105 try { 106 107 KeyStore.SecretKeyEntry entry2 = (KeyStore.SecretKeyEntry) store 108 .getEntry(user + "@" + server, 109 new KeyStore.PasswordProtection(masterPassword)); 110 return new String(entry2.getSecretKey().getEncoded()); 111 } catch (KeyStoreException ex) { 112 LOG.log(Level.WARNING, "", ex); 113 } catch (UnrecoverableEntryException ex) { 114 LOG.log(Level.WARNING, "", ex); 115 } catch (NoSuchAlgorithmException ex) { 116 LOG.log(Level.WARNING, "", ex); 117 } 118 119 return null; 120 } 121 122 /** 123 * Adds a password to the KeyChain for a given account/user and server. 124 * 125 * @param user 126 * @param server 127 * @param password 128 */ 129 public void addPassword(String user, String server, char[] password) 130 { 131 String pass = new String(password); 132 SecretKeySpec passwordKey = new SecretKeySpec(pass.getBytes(), "JCEKS"); 133 KeyStore.SecretKeyEntry entry = new KeyStore.SecretKeyEntry(passwordKey); 134 try { 135 store.setEntry(user + "@" + server, entry, 136 new KeyStore.PasswordProtection(masterPassword)); 137 } catch (KeyStoreException e) { 138 LOG.log(Level.WARNING, "", e); 139 } 140 } 141 142 /** 143 * Removes a password for a given account/user and server. 144 * 145 * @param user 146 * @param server 147 */ 148 public void removePassword(String user, String server) { 149 try { 150 store.deleteEntry(user + "@" + server); 151 } catch (KeyStoreException e) { 152 LOG.log(Level.WARNING, "", e); 153 } 154 } 155 156 /** 157 * Persists the KeyChain to an OutputStream 158 * 159 * @param ostream 160 * @throws IOException 161 */ 162 163 public void store(OutputStream ostream) throws IOException { 164 try { 165 store.store(ostream, masterPassword); 166 } catch (KeyStoreException ex) { 167 LOG.log(Level.WARNING, "", ex); 168 } catch (CertificateException ex) { 169 LOG.log(Level.WARNING, "", ex); 170 } catch (NoSuchAlgorithmException ex) { 171 LOG.log(Level.WARNING, "", ex); 172 } 173 } 174 175 176 public static void main(String[] args) { 177 try { 178 File file = new File("c:\\test.txt"); 179 FileInputStream fis; 180 if (!file.exists()) { 181 file.createNewFile(); 182 fis = null; 183 } else { 184 fis = new FileInputStream(file); 185 } 186 KeyChain kc = new KeyChain("test".toCharArray(), fis); 187 kc.addPassword("bino", "sun-ds.sfbay", "test123".toCharArray()); 188 LOG.fine("pass = " 189 + kc.getPassword("bino", "sun-ds.sfbay")); 190 191 LOG.fine("More testing :"); 192 for (int i = 0; i < 100; i++) { 193 kc.addPassword("" + i, "sun-ds.sfbay", ("" + i).toCharArray()); 194 } 195 for (int i = 0; i < 100; i++) { 196 LOG.fine("key =" + i + " pass =" 197 + kc.getPassword("" + i, "sun-ds.sfbay")); 198 } 199 kc.store(new FileOutputStream(file)); 200 } catch (Exception e) { 201 LOG.log(Level.WARNING, "", e); 202 } 203 } 204 205 }