/* Copyright 2012 Jan Ove Saltvedt This file is part of KBot. KBot is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. KBot is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with KBot. If not, see <http://www.gnu.org/licenses/>. */ /* * Copyright � 2010 Jan Ove Saltvedt. * All rights reserved. */ package com.kbotpro.handlers; import org.apache.log4j.Logger; import sun.misc.BASE64Encoder; import sun.misc.BASE64Decoder; import javax.crypto.spec.DESKeySpec; import javax.crypto.*; import java.util.List; import java.util.ArrayList; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.security.spec.InvalidKeySpecException; import java.io.UnsupportedEncodingException; import java.io.IOException; import java.io.StringReader; import com.kbotpro.servercom.ServerCom; import com.kbotpro.various.StaticStorage; import org.jdom.Document; import org.jdom.Element; import org.jdom.JDOMException; import org.jdom.input.SAXBuilder; /** * Created by IntelliJ IDEA. * User: Jan Ove Saltvedt * Date: Nov 29, 2009 * Time: 11:22:44 AM * To change this template use File | Settings | File Templates. */ public class AccountsManager { private List<Account> accounts; public List<Account> getAccounts(){ if(accounts != null){ return accounts; } updateAccounts(); return accounts; } public void createAccount(Account account){ StaticStorage.serverCom.addAccount(account); } public void deleteAccount(Account account){ if(account.ID < 0){ return; } StaticStorage.serverCom.deleteAccount(account); } public void updateAccounts() { List<Account> accounts = new ArrayList<Account>(); String xml = StaticStorage.serverCom.getAccountsInternal(); try { Document document = new SAXBuilder().build(new StringReader(xml)); Element root = document.getRootElement(); if(root.getChildText("succeeded").equals("true")){ for(Element accountNode: (List<Element>)root.getChildren("account")){ int ID = Integer.parseInt(accountNode.getAttribute("id").getValue()); String encryptedUsername = accountNode.getChildText("enusername"); String encryptedPassword = accountNode.getChildText("enpasswd"); String usernameHash = accountNode.getChildText("usernamehash"); String passwordHash = accountNode.getChildText("passwdhash"); final Account account = new Account(ID, encryptedUsername, encryptedPassword, usernameHash, passwordHash); if(account.toString() != null){ accounts.add(account); } else{ StaticStorage.serverCom.deleteAccount(account); Logger.getRootLogger().warn("Deleting account because we can no longer decrypt it. Probably because user changed password on forums."); } } } } catch (JDOMException e) { Logger.getRootLogger().error("Exception: ", e); //To change body of catch statement use File | Settings | File Templates. } catch (IOException e) { Logger.getRootLogger().error("Exception: ", e); //To change body of catch statement use File | Settings | File Templates. } this.accounts = accounts; } public Account constructAccount(String username, String password) { return new Account(username, password, StaticStorage.userStorage.getPassword()); } public class Account { public int ID = -1; public String encryptedUsername; public String encryptedPassword; public String usernameHash; public String passwordHash; public boolean membersAccount = false; public String pin = ""; public boolean useLamp = false; public String lampIndex = "Runecraft"; public Account(int ID, String encryptedUsername, String encryptedPassword, String usernameHash, String passwordHash) { this.ID = ID; this.encryptedUsername = encryptedUsername; this.encryptedPassword = encryptedPassword; this.usernameHash = usernameHash; this.passwordHash = passwordHash; } public Account(String username, String password, String passPhrase) { try { DESKeySpec keySpec = new DESKeySpec(generateKey(passPhrase)); SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES"); SecretKey key = keyFactory.generateSecret(keySpec); sun.misc.BASE64Encoder base64encoder = new BASE64Encoder(); byte[] cleartext = username.getBytes("UTF8"); Cipher cipher = Cipher.getInstance("DES"); cipher.init(Cipher.ENCRYPT_MODE, key); encryptedUsername = base64encoder.encode(cipher.doFinal(cleartext)); cleartext = password.getBytes("UTF8"); encryptedPassword = base64encoder.encode(cipher.doFinal(cleartext)); usernameHash = ServerCom.MD5(username); passwordHash = ServerCom.MD5(password); } catch (InvalidKeyException e) { Logger.getRootLogger().error("Exception: ", e); //To change body of catch statement use File | Settings | File Templates. } catch (UnsupportedEncodingException e) { Logger.getRootLogger().error("Exception: ", e); //To change body of catch statement use File | Settings | File Templates. } catch (InvalidKeySpecException e) { Logger.getRootLogger().error("Exception: ", e); //To change body of catch statement use File | Settings | File Templates. } catch (NoSuchAlgorithmException e) { Logger.getRootLogger().error("Exception: ", e); //To change body of catch statement use File | Settings | File Templates. } catch (IllegalBlockSizeException e) { Logger.getRootLogger().error("Exception: ", e); //To change body of catch statement use File | Settings | File Templates. } catch (NoSuchPaddingException e) { Logger.getRootLogger().error("Exception: ", e); //To change body of catch statement use File | Settings | File Templates. } catch (BadPaddingException e) { Logger.getRootLogger().error("Exception: ", e); //To change body of catch statement use File | Settings | File Templates. } } private String username; public String getUsername(){ if(username != null){ return username; } String decrypted = decode(StaticStorage.userStorage.getPassword(), encryptedUsername); if(verify(decrypted, usernameHash)){ username = decrypted; return decrypted; } else{ return null; } } private String password; public String getPassword(){ if(password != null){ return password; } String decrypted = decode(StaticStorage.userStorage.getPassword(), encryptedPassword); if(verify(decrypted, passwordHash)){ password = decrypted; return decrypted; } else{ return null; } } private byte[] generateKey(String passPhrase){ if(passPhrase.length() < 8){ int add = 8-passPhrase.length(); while(add != 0){ passPhrase += 'g'; add--; } } try { return passPhrase.getBytes("UTF8"); } catch (UnsupportedEncodingException e) { Logger.getRootLogger().error("Exception: ", e); //To change body of catch statement use File | Settings | File Templates. return null; } } private String decode(String passPhrase, String encryptedString){ try { DESKeySpec keySpec = new DESKeySpec(generateKey(passPhrase)); SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES"); SecretKey key = keyFactory.generateSecret(keySpec); sun.misc.BASE64Decoder base64decoder = new BASE64Decoder(); byte[] encrypedPwdBytes = base64decoder.decodeBuffer(encryptedString); Cipher cipher = Cipher.getInstance("DES");// cipher is not thread safe cipher.init(Cipher.DECRYPT_MODE, key); byte[] plainTextPwdBytes = (cipher.doFinal(encrypedPwdBytes)); return new String(plainTextPwdBytes, "UTF8"); } catch (InvalidKeyException e) { Logger.getRootLogger().error("Exception: ", e); //To change body of catch statement use File | Settings | File Templates. } catch (UnsupportedEncodingException e) { Logger.getRootLogger().error("Exception: ", e); //To change body of catch statement use File | Settings | File Templates. } catch (NoSuchAlgorithmException e) { Logger.getRootLogger().error("Exception: ", e); //To change body of catch statement use File | Settings | File Templates. } catch (InvalidKeySpecException e) { Logger.getRootLogger().error("Exception: ", e); //To change body of catch statement use File | Settings | File Templates. } catch (IllegalBlockSizeException e) { Logger.getRootLogger().error("Exception: ", e); //To change body of catch statement use File | Settings | File Templates. } catch (NoSuchPaddingException e) { Logger.getRootLogger().error("Exception: ", e); //To change body of catch statement use File | Settings | File Templates. } catch (BadPaddingException e) { Logger.getRootLogger().error("Exception: ", e); //To change body of catch statement use File | Settings | File Templates. } catch (IOException e) { Logger.getRootLogger().error("Exception: ", e); //To change body of catch statement use File | Settings | File Templates. } return "<invalid>"; } private boolean verify(String decodedString, String verifyHash){ try { String hash = ServerCom.MD5(decodedString); return hash.equalsIgnoreCase(verifyHash); } catch (NoSuchAlgorithmException e) { Logger.getRootLogger().error("Exception: ", e); //To change body of catch statement use File | Settings | File Templates. } catch (UnsupportedEncodingException e) { Logger.getRootLogger().error("Exception: ", e); //To change body of catch statement use File | Settings | File Templates. } return false; } /** * Returns a string representation of the object. In general, the * <code>toString</code> method returns a string that * "textually represents" this object. The result should * be a concise but informative representation that is easy for a * person to read. * It is recommended that all subclasses override this method. * <p/> * The <code>toString</code> method for class <code>Object</code> * returns a string consisting of the name of the class of which the * object is an instance, the at-sign character `<code>@</code>', and * the unsigned hexadecimal representation of the hash code of the * object. In other words, this method returns a string equal to the * value of: * <blockquote> * <pre> * getClass().getName() + '@' + Integer.toHexString(hashCode()) * </pre></blockquote> * * @return a string representation of the object. */ @Override public String toString() { return getUsername(); } } }