1 /*** 2 * 3 */ 4 package de.cohesion.bssh.impl; 5 6 import java.io.File; 7 import java.io.FileInputStream; 8 import java.io.FileOutputStream; 9 import java.io.IOException; 10 import java.io.ObjectInputStream; 11 import java.io.ObjectOutputStream; 12 import java.security.Provider; 13 import java.security.Security; 14 import java.util.Iterator; 15 16 import javax.crypto.Cipher; 17 import javax.crypto.SealedObject; 18 import javax.crypto.SecretKey; 19 import javax.crypto.SecretKeyFactory; 20 import javax.crypto.spec.PBEKeySpec; 21 import javax.crypto.spec.PBEParameterSpec; 22 23 import de.cohesion.bssh.Member; 24 import de.cohesion.bssh.PasswordStore; 25 26 /*** 27 * @author schulzs 28 */ 29 public class PBEPasswordStore implements PasswordStore { 30 31 private enum Ciphers { 32 ENCRYPT(Cipher.ENCRYPT_MODE), DECRYPT(Cipher.DECRYPT_MODE); 33 34 private static final String CIPHER_ALGORITHM_NAME = "PBEWithMD5AndDES"; 35 36 private static final byte[] SALT = { (byte) 0xc7, (byte) 0x73, 37 (byte) 0x21, (byte) 0x8c, (byte) 0x7e, (byte) 0xc8, 38 (byte) 0xee, (byte) 0x99 }; 39 40 private static final int ITERATIONS = 20; 41 42 private static final PBEParameterSpec PARAM_SPEC = new PBEParameterSpec( 43 SALT, ITERATIONS); 44 45 private int opMode; 46 47 Ciphers(int opMode) { 48 this.opMode = opMode; 49 } 50 51 public Cipher get(final char[] passphrase) throws Exception { 52 Cipher c = Cipher.getInstance(CIPHER_ALGORITHM_NAME, "SunJCE"); 53 c.init(opMode, this.getKey(passphrase), PARAM_SPEC); 54 return c; 55 } 56 57 private SecretKey getKey(final char[] passphrase) throws Exception { 58 SecretKeyFactory factory = SecretKeyFactory 59 .getInstance(CIPHER_ALGORITHM_NAME); 60 return factory.generateSecret(new PBEKeySpec(passphrase)); 61 } 62 63 } 64 65 private static void dumpProviders() { 66 try { 67 Provider[] providers = Security.getProviders(); 68 for (Provider p : providers) { 69 System.out.println("Provider: " + p.getName() + ", " 70 + p.getInfo()); 71 for (Iterator itr = p.keySet().iterator(); itr.hasNext();) { 72 String key = (String) itr.next(); 73 String value = (String) p.get(key); 74 System.out.println("\t" + key + " = " + value); 75 } 76 } 77 } catch (Exception e) { 78 e.printStackTrace(); 79 } 80 } 81 82 private final File root; 83 84 private final char[] passphrase; 85 86 public PBEPasswordStore(final File root, final char[] passphrase) 87 throws IOException { 88 this.root = root; 89 this.passphrase = passphrase; 90 } 91 92 private File getPasswordFile(final Member member) throws IOException { 93 File f = new File(root.getAbsolutePath() + File.separator 94 + member.getUserName() + File.separator 95 + member.getHost().toString().replace('/', '-') + "-" 96 + member.getPort()); 97 if (!f.exists()) { 98 boolean success = f.mkdirs(); 99 if (!success) { 100 throw new IOException("could not create directory structure"); 101 } 102 } 103 return f; 104 } 105 106 public char[] getPassword(final Member member) throws IOException { 107 ObjectInputStream ois = new ObjectInputStream(new FileInputStream(this 108 .getPasswordFile(member))); 109 String result = null; 110 try { 111 SealedObject so = (SealedObject) ois.readObject(); 112 result = (String) so.getObject(Ciphers.DECRYPT.get(passphrase)); 113 } catch (Exception e) { 114 IOException ioe = new IOException( 115 "opening sealed password file failed"); 116 ioe.initCause(e); 117 throw ioe; 118 } finally { 119 ois.close(); 120 } 121 122 return result.toCharArray(); 123 } 124 125 public void store(final Member member, char[] password) throws IOException { 126 File f = this.getPasswordFile(member); 127 if (f.exists()) { 128 boolean success = f.delete(); 129 if (!success) { 130 throw new IOException( 131 "file already exists and could not be deleted"); 132 } 133 } 134 ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(f)); 135 try { 136 SealedObject so = new SealedObject(new String(password), 137 Ciphers.ENCRYPT.get(passphrase)); 138 oos.writeObject(so); 139 } catch (Exception e) { 140 IOException ioe = new IOException( 141 "storing sealed password file failed"); 142 ioe.initCause(e); 143 throw ioe; 144 } finally { 145 oos.close(); 146 } 147 } 148 149 }