File Source: CipherUtils.java

         /* 
    P/P   *  Method: com.dmdirc.util.CipherUtils__static_init
          */
     1  /*
     2   * Copyright (c) 2006-2009 Chris Smith, Shane Mc Cormack, Gregory Holmes
     3   *
     4   * Permission is hereby granted, free of charge, to any person obtaining a copy
     5   * of this software and associated documentation files (the "Software"), to deal
     6   * in the Software without restriction, including without limitation the rights
     7   * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     8   * copies of the Software, and to permit persons to whom the Software is
     9   * furnished to do so, subject to the following conditions:
    10   *
    11   * The above copyright notice and this permission notice shall be included in
    12   * all copies or substantial portions of the Software.
    13   *
    14   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    15   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    16   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    17   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    18   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    19   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
    20   * SOFTWARE.
    21   */
    22  
    23  package com.dmdirc.util;
    24  
    25  import com.dmdirc.Main;
    26  import com.dmdirc.config.IdentityManager;
    27  import com.dmdirc.logger.ErrorLevel;
    28  import com.dmdirc.logger.Logger;
    29  
    30  import java.io.IOException;
    31  import java.io.UnsupportedEncodingException;
    32  import java.nio.charset.Charset;
    33  import java.security.InvalidAlgorithmParameterException;
    34  import java.security.InvalidKeyException;
    35  import java.security.MessageDigest;
    36  import java.security.NoSuchAlgorithmException;
    37  import java.security.spec.AlgorithmParameterSpec;
    38  import java.security.spec.InvalidKeySpecException;
    39  import java.security.spec.KeySpec;
    40  
    41  import javax.crypto.BadPaddingException;
    42  import javax.crypto.Cipher;
    43  import javax.crypto.IllegalBlockSizeException;
    44  import javax.crypto.NoSuchPaddingException;
    45  import javax.crypto.SecretKey;
    46  import javax.crypto.SecretKeyFactory;
    47  import javax.crypto.spec.PBEKeySpec;
    48  import javax.crypto.spec.PBEParameterSpec;
    49  import net.miginfocom.Base64;
    50  
    51  /**
    52   * Helper class to encrypt and decrypt strings, requests passwords if needed.
    53   */
    54  public class CipherUtils {
    55      
    56      /** Singleton instance. */
    57      private static CipherUtils me;
    58      
    59      /** Encryption cipher. */
    60      private Cipher ecipher;
    61      
    62      /** Decryption cipher. */
    63      private Cipher dcipher;
    64      
    65      /** Salt. */
    66      private final byte[] SALT = {
    67          (byte) 0xA9, (byte) 0x9B, (byte) 0xC8, (byte) 0x32,
    68          (byte) 0x56, (byte) 0x35, (byte) 0xE3, (byte) 0x03,
    69      };
    70      
    71      /** Iteration count. */
    72      private static final int ITERATIONS = 19;
    73      
    74      /** Number of auth attemps before failing the attempt. */
    75      private static final int AUTH_TRIES = 4;
    76      
    77      /** User password. */
    78      private String password;
    79      
    80      /**
    81       * Prevents creation of a new instance of Encipher.
    82       */
             /* 
    P/P       *  Method: void com.dmdirc.util.CipherUtils()
              * 
              *  Postconditions:
              *    this.SALT == &new byte[](CipherUtils#1)
              *    new byte[](CipherUtils#1) num objects == 1
              *    this.SALT.length == 8
              *    this.SALT[0] == -87
              *    this.SALT[1] == -101
              *    this.SALT[2] == -56
              *    this.SALT[3] == 50
              *    this.SALT[4] == 86
              *    this.SALT[5] == 53
              *    this.SALT[6] == -29
              *    ...
              */
    83      protected CipherUtils() {
    84          // Do nothing
    85      }
    86      
    87      /**
    88       * Retrieves a singleton instance of CipherUtils.
    89       * 
    90       * @return A singleton cipher utils instance.
    91       */
    92      public static CipherUtils getCipherUtils() {
                 /* 
    P/P           *  Method: CipherUtils getCipherUtils()
                  * 
                  *  Preconditions:
                  *    init'ed(me)
                  * 
                  *  Postconditions:
                  *    me == One-of{old me, &new CipherUtils(getCipherUtils#1)}
                  *    me != null
                  *    return_value == One-of{old me, &new CipherUtils(getCipherUtils#1)}
                  *    return_value != null
                  *    new CipherUtils(getCipherUtils#1) num objects <= 1
                  *    new CipherUtils(getCipherUtils#1).SALT == &amp;new byte[](CipherUtils#1)
                  *    new byte[](CipherUtils#1) num objects <= 1
                  *    new byte[](CipherUtils#1).length == 8
                  *    new byte[](CipherUtils#1)[0] == -87
                  *    new byte[](CipherUtils#1)[1] == -101
                  *    ...
                  * 
                  *  Test Vectors:
                  *    me: Inverse{null}, Addr_Set{null}
                  */
    93          synchronized(CipherUtils.class) {
    94              if (me == null) {
    95                  me = new CipherUtils();
    96              }
    97              
    98              return me;
    99          }
   100      }
   101      
   102      /**
   103       * Encrypts a string using the stored settings. Will return null if the
   104       * automatic user authentication fails - use checkauth and auth.
   105       * @param str String to encrypt
   106       * @return Encrypted string
   107       */
   108      public String encrypt(final String str) {
                 /* 
    P/P           *  Method: String encrypt(String)
                  * 
                  *  Preconditions:
                  *    init'ed(this.dcipher)
                  *    (soft) init'ed(this.ecipher)
                  *    (soft) init'ed(this.password)
                  *    (soft) str != null
                  * 
                  *  Presumptions:
                  *    init'ed(com.dmdirc.logger.ErrorLevel.LOW)
                  *    javax.crypto.Cipher:doFinal(...).length@117 in {0, 3..169_538_366}
                  * 
                  *  Postconditions:
                  *    return_value in Addr_Set{null,&amp;new String(encodeToString#1)}
                  *    init'ed(this.dcipher)
                  *    init'ed(this.ecipher)
                  *    init'ed(this.password)
                  *    new String(encodeToString#1) num objects <= 1
                  * 
                  *  Test Vectors:
                  *    !(this.dcipher == null) &amp; !(this.ecipher == null): {1}, {0}
                  */
   109          if (!checkAuthed()) {
   110              if (auth()) {
   111                  createCiphers();
   112              } else {
   113                  return null;
   114              }
   115          }
   116          try {
   117              return Base64.encodeToString(ecipher.doFinal(str.getBytes("UTF8")), false);
   118          } catch (BadPaddingException e) {
   119              Logger.userError(ErrorLevel.LOW, "Unable to decrypt string: " + e.getMessage());
   120          } catch (IllegalBlockSizeException e) {
   121              Logger.userError(ErrorLevel.LOW, "Unable to decrypt string: " + e.getMessage());
   122          } catch (UnsupportedEncodingException e) {
   123              Logger.userError(ErrorLevel.LOW, "Unable to decrypt string: " + e.getMessage());
   124          }
   125  
   126          return null;
   127      }
   128      
   129      /**
   130       * Encrypts a string using the stored settings. Will return null if the
   131       * automatic user authentication fails - use checkauth and auth.
   132       * @param str String to decrypt
   133       * @return Decrypted string
   134       */
   135      public String decrypt(final String str) {
                 /* 
    P/P           *  Method: String decrypt(String)
                  * 
                  *  Preconditions:
                  *    init'ed(this.dcipher)
                  *    (soft) init'ed(net.miginfocom.Base64__static_init.new int[](Base64__static_init#1)[...])
                  *    (soft) init'ed(this.ecipher)
                  *    (soft) init'ed(this.password)
                  * 
                  *  Presumptions:
                  *    init'ed(com.dmdirc.logger.ErrorLevel.LOW)
                  * 
                  *  Postconditions:
                  *    return_value in Addr_Set{null,&amp;new String(decrypt#1)}
                  *    init'ed(this.dcipher)
                  *    init'ed(this.ecipher)
                  *    init'ed(this.password)
                  *    new String(decrypt#1) num objects <= 1
                  * 
                  *  Test Vectors:
                  *    !(this.dcipher == null) &amp; !(this.ecipher == null): {1}, {0}
                  */
   136          if (!checkAuthed()) {
   137              if (auth()) {
   138                  createCiphers();
   139              } else {
   140                  return null;
   141              }
   142          }
   143          try {
   144              return new String(dcipher.doFinal(Base64.decode(str)));
   145          } catch (BadPaddingException e) {
   146              Logger.userError(ErrorLevel.LOW, "Unable to decrypt string: " + e.getMessage());
   147          } catch (IllegalBlockSizeException e) {
   148              Logger.userError(ErrorLevel.LOW, "Unable to decrypt string: " + e.getMessage());
   149          }
   150          return null;
   151      }
   152      
   153      /**
   154       * Performs a SHA-512 hash.
   155       * @param data String to hashed
   156       * @return hashed string
   157       */
   158      public String hash(final String data) {
   159          try {
                     /* 
    P/P               *  Method: String hash(String)
                      * 
                      *  Preconditions:
                      *    (soft) data != null
                      * 
                      *  Presumptions:
                      *    init'ed(com.dmdirc.logger.ErrorLevel.LOW)
                      *    java.security.MessageDigest:getInstance(...)@160 != null
                      * 
                      *  Postconditions:
                      *    return_value in Addr_Set{null,&amp;new String(hash#1)}
                      *    new String(hash#1) num objects <= 1
                      */
   160              return new String(MessageDigest.getInstance("SHA-512")
   161              .digest(data.getBytes("UTF8")), Charset.forName("UTF-8"));
   162          } catch (NoSuchAlgorithmException e) {
   163              Logger.userError(ErrorLevel.LOW, "Unable to hash string");
   164          } catch (IOException e) {
   165              Logger.userError(ErrorLevel.LOW, "Unable to hash string");
   166          }
   167          return null;
   168      }
   169      
   170      /**
   171       * Checks if a user is authed.
   172       *
   173       * @return true if authed, false otherwise
   174       */
   175      public boolean checkAuthed() {
                 /* 
    P/P           *  Method: bool checkAuthed()
                  * 
                  *  Preconditions:
                  *    init'ed(this.dcipher)
                  *    (soft) init'ed(this.ecipher)
                  * 
                  *  Postconditions:
                  *    init'ed(return_value)
                  * 
                  *  Test Vectors:
                  *    this.dcipher: Addr_Set{null}, Inverse{null}
                  *    this.ecipher: Addr_Set{null}, Inverse{null}
                  */
   176          if (dcipher != null && ecipher != null) {
   177              return true;
   178          }
   179          return false;
   180      }
   181      
   182      /**
   183       * creates ciphers.
   184       */
   185      protected void createCiphers() {
   186          try {
                     /* 
    P/P               *  Method: void createCiphers()
                      * 
                      *  Preconditions:
                      *    (soft) this.password != null
                      * 
                      *  Presumptions:
                      *    init'ed(com.dmdirc.logger.ErrorLevel.LOW)
                      *    javax.crypto.Cipher:getInstance(...)@191 != null
                      *    javax.crypto.Cipher:getInstance(...)@192 != null
                      *    javax.crypto.SecretKeyFactory:generateSecret(...)@189 != null
                      *    javax.crypto.SecretKeyFactory:getInstance(...)@189 != null
                      * 
                      *  Postconditions:
                      *    init'ed(this.dcipher)
                      *    init'ed(this.ecipher)
                      */
   187              final KeySpec keySpec = new PBEKeySpec(
   188                      password.toCharArray(), SALT, ITERATIONS);
   189              final SecretKey key = SecretKeyFactory.
   190                      getInstance("PBEWithMD5AndDES").generateSecret(keySpec);
   191              ecipher = Cipher.getInstance(key.getAlgorithm());
   192              dcipher = Cipher.getInstance(key.getAlgorithm());
   193              final AlgorithmParameterSpec paramSpec =
   194                      new PBEParameterSpec(SALT, ITERATIONS);
   195              ecipher.init(Cipher.ENCRYPT_MODE, key, paramSpec);
   196              dcipher.init(Cipher.DECRYPT_MODE, key, paramSpec);
   197          } catch (InvalidAlgorithmParameterException e) {
   198              Logger.userError(ErrorLevel.LOW, "Unable to create ciphers");
   199              ecipher = null;
   200              dcipher = null;
   201          } catch (InvalidKeySpecException e) {
   202              Logger.userError(ErrorLevel.LOW, "Unable to create ciphers");
   203              ecipher = null;
   204              dcipher = null;
   205          } catch (NoSuchPaddingException e) {
   206              Logger.userError(ErrorLevel.LOW, "Unable to create ciphers");
   207              ecipher = null;
   208              dcipher = null;
   209          } catch (NoSuchAlgorithmException e) {
   210              Logger.userError(ErrorLevel.LOW, "Unable to create ciphers");
   211              ecipher = null;
   212              dcipher = null;
   213          } catch (InvalidKeyException e) {
   214              Logger.userError(ErrorLevel.LOW, "Unable to create ciphers");
   215              ecipher = null;
   216              dcipher = null;
   217          }
   218      }
   219      
   220      /**
   221       * Auths a user and sets the password.
   222       *
   223       * @return true if auth was successful, false otherwise.
   224       */
   225      public boolean auth() {
                 /* 
    P/P           *  Method: bool auth()
                  * 
                  *  Preconditions:
                  *    (soft) init'ed(this.password)
                  * 
                  *  Presumptions:
                  *    com.dmdirc.config.IdentityManager:getConfigIdentity(...)@242 != null
                  *    com.dmdirc.config.IdentityManager:getGlobalConfig(...)@229 != null
                  *    com.dmdirc.config.IdentityManager:getGlobalConfig(...)@230 != null
                  *    com.dmdirc.config.IdentityManager:getGlobalConfig(...)@232 != null
                  *    com.dmdirc.config.IdentityManager:getGlobalConfig(...)@234 != null
                  *    ...
                  * 
                  *  Postconditions:
                  *    init'ed(return_value)
                  *    init'ed(this.password)
                  * 
                  *  Test Vectors:
                  *    com.dmdirc.config.ConfigManager:hasOptionString(...)@229: {0}, {1}
                  *    com.dmdirc.config.ConfigManager:hasOptionString(...)@232: {0}, {1}
                  *    java.lang.String:equals(...)@245: {1}, {0}
                  *    java.lang.String:isEmpty(...)@238: {0}, {1}
                  */
   226          String passwordHash = null;
   227          String prompt = "Please enter your password";
   228          int tries = 1;
   229          if (IdentityManager.getGlobalConfig().hasOptionString("encryption", "password")) {
   230              password = IdentityManager.getGlobalConfig().getOption("encryption", "password");
   231          } else {
   232              if (IdentityManager.getGlobalConfig().hasOptionString("encryption",
   233                      "passwordHash")) {
   234                  passwordHash = IdentityManager.getGlobalConfig().getOption("encryption",
   235                          "passwordHash");
   236              }
   237  
   238              while ((password == null || password.isEmpty()) && tries < AUTH_TRIES) {
   239                  password = getPassword(prompt);
   240                  if (passwordHash == null) {
   241                      passwordHash = hash(password);
   242                      IdentityManager.getConfigIdentity().setOption("encryption", 
   243                              "passwordHash", passwordHash);
   244                  }
   245                  if (!hash(password).equals(passwordHash)) {
   246                      prompt = "<html>Password mis-match<br>Please re-enter "
   247                              + "your password</html>";
   248                      tries++;
   249                      password = null;
   250                  }
   251              }
   252          }
   253          if (tries == AUTH_TRIES) {
   254              return false;
   255          }
   256          return true;
   257      }
   258      
   259      /**
   260       * Requests the encryption password from the user.
   261       * 
   262       * @param prompt The prompt to show
   263       * @return The user-specified password
   264       */
   265      protected String getPassword(final String prompt) {
                 /* 
    P/P           *  Method: String getPassword(String)
                  * 
                  *  Presumptions:
                  *    com.dmdirc.Main:getUI(...)@266 != null
                  * 
                  *  Postconditions:
                  *    init'ed(return_value)
                  */
   266          return Main.getUI().getUserInput(prompt);
   267      }
   268  }








SofCheck Inspector Build Version : 2.17854
CipherUtils.java 2009-Jun-25 01:54:24
CipherUtils.class 2009-Sep-02 17:04:17