Save This Page
Home » openjdk-7 » com.sun.crypto » provider » [javadoc | source]
    1   /*
    2    * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
    3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    4    *
    5    * This code is free software; you can redistribute it and/or modify it
    6    * under the terms of the GNU General Public License version 2 only, as
    7    * published by the Free Software Foundation.  Oracle designates this
    8    * particular file as subject to the "Classpath" exception as provided
    9    * by Oracle in the LICENSE file that accompanied this code.
   10    *
   11    * This code is distributed in the hope that it will be useful, but WITHOUT
   12    * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   13    * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   14    * version 2 for more details (a copy is included in the LICENSE file that
   15    * accompanied this code).
   16    *
   17    * You should have received a copy of the GNU General Public License version
   18    * 2 along with this work; if not, write to the Free Software Foundation,
   19    * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   20    *
   21    * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
   22    * or visit www.oracle.com if you need additional information or have any
   23    * questions.
   24    */
   25   
   26   package com.sun.crypto.provider;
   27   
   28   import java.io.ObjectStreamException;
   29   import java.nio.ByteBuffer;
   30   import java.nio.CharBuffer;
   31   import java.nio.charset.Charset;
   32   import java.util.Arrays;
   33   import java.security.KeyRep;
   34   import java.security.GeneralSecurityException;
   35   import java.security.NoSuchAlgorithmException;
   36   import java.security.NoSuchProviderException;
   37   import java.security.spec.InvalidKeySpecException;
   38   import javax.crypto.Mac;
   39   import javax.crypto.SecretKey;
   40   import javax.crypto.spec.PBEKeySpec;
   41   
   42   /**
   43    * This class represents a PBE key derived using PBKDF2 defined
   44    * in PKCS#5 v2.0. meaning that
   45    * 1) the password must consist of characters which will be converted
   46    *    to bytes using UTF-8 character encoding.
   47    * 2) salt, iteration count, and to be derived key length are supplied
   48    *
   49    * @author Valerie Peng
   50    *
   51    */
   52   final class PBKDF2KeyImpl implements javax.crypto.interfaces.PBEKey {
   53   
   54       static final long serialVersionUID = -2234868909660948157L;
   55   
   56       private char[] passwd;
   57       private byte[] salt;
   58       private int iterCount;
   59       private byte[] key;
   60   
   61       private Mac prf;
   62   
   63       private static byte[] getPasswordBytes(char[] passwd) {
   64           Charset utf8 = Charset.forName("UTF-8");
   65           CharBuffer cb = CharBuffer.wrap(passwd);
   66           ByteBuffer bb = utf8.encode(cb);
   67   
   68           int len = bb.limit();
   69           byte[] passwdBytes = new byte[len];
   70           bb.get(passwdBytes, 0, len);
   71   
   72           return passwdBytes;
   73       }
   74   
   75       /**
   76        * Creates a PBE key from a given PBE key specification.
   77        *
   78        * @param key the given PBE key specification
   79        */
   80       PBKDF2KeyImpl(PBEKeySpec keySpec, String prfAlgo)
   81           throws InvalidKeySpecException {
   82           char[] passwd = keySpec.getPassword();
   83           if (passwd == null) {
   84               // Should allow an empty password.
   85               this.passwd = new char[0];
   86           } else {
   87               this.passwd = passwd.clone();
   88           }
   89           // Convert the password from char[] to byte[]
   90           byte[] passwdBytes = getPasswordBytes(this.passwd);
   91   
   92           this.salt = keySpec.getSalt();
   93           if (salt == null) {
   94               throw new InvalidKeySpecException("Salt not found");
   95           }
   96           this.iterCount = keySpec.getIterationCount();
   97           if (iterCount == 0) {
   98               throw new InvalidKeySpecException("Iteration count not found");
   99           } else if (iterCount < 0) {
  100               throw new InvalidKeySpecException("Iteration count is negative");
  101           }
  102           int keyLength = keySpec.getKeyLength();
  103           if (keyLength == 0) {
  104               throw new InvalidKeySpecException("Key length not found");
  105           } else if (keyLength == 0) {
  106               throw new InvalidKeySpecException("Key length is negative");
  107           }
  108           try {
  109               this.prf = Mac.getInstance(prfAlgo, "SunJCE");
  110           } catch (NoSuchAlgorithmException nsae) {
  111               // not gonna happen; re-throw just in case
  112               InvalidKeySpecException ike = new InvalidKeySpecException();
  113               ike.initCause(nsae);
  114               throw ike;
  115           } catch (NoSuchProviderException nspe) {
  116               // Again, not gonna happen; re-throw just in case
  117               InvalidKeySpecException ike = new InvalidKeySpecException();
  118               ike.initCause(nspe);
  119               throw ike;
  120           }
  121           this.key = deriveKey(prf, passwdBytes, salt, iterCount, keyLength);
  122       }
  123   
  124       private static byte[] deriveKey(final Mac prf, final byte[] password,
  125               byte[] salt, int iterCount, int keyLengthInBit) {
  126           int keyLength = keyLengthInBit/8;
  127           byte[] key = new byte[keyLength];
  128           try {
  129               int hlen = prf.getMacLength();
  130               int intL = (keyLength + hlen - 1)/hlen; // ceiling
  131               int intR = keyLength - (intL - 1)*hlen; // residue
  132               byte[] ui = new byte[hlen];
  133               byte[] ti = new byte[hlen];
  134               // SecretKeySpec cannot be used, since password can be empty here.
  135               SecretKey macKey = new SecretKey() {
  136                   @Override
  137                   public String getAlgorithm() {
  138                       return prf.getAlgorithm();
  139                   }
  140                   @Override
  141                   public String getFormat() {
  142                       return "RAW";
  143                   }
  144                   @Override
  145                   public byte[] getEncoded() {
  146                       return password;
  147                   }
  148                   @Override
  149                   public int hashCode() {
  150                       return Arrays.hashCode(password) * 41 +
  151                               prf.getAlgorithm().toLowerCase().hashCode();
  152                   }
  153                   @Override
  154                   public boolean equals(Object obj) {
  155                       if (this == obj) return true;
  156                       if (this.getClass() != obj.getClass()) return false;
  157                       SecretKey sk = (SecretKey)obj;
  158                       return prf.getAlgorithm().equalsIgnoreCase(
  159                           sk.getAlgorithm()) &&
  160                           Arrays.equals(password, sk.getEncoded());
  161                   }
  162               };
  163               prf.init(macKey);
  164   
  165               byte[] ibytes = new byte[4];
  166               for (int i = 1; i <= intL; i++) {
  167                   prf.update(salt);
  168                   ibytes[3] = (byte) i;
  169                   ibytes[2] = (byte) ((i >> 8) & 0xff);
  170                   ibytes[1] = (byte) ((i >> 16) & 0xff);
  171                   ibytes[0] = (byte) ((i >> 24) & 0xff);
  172                   prf.update(ibytes);
  173                   prf.doFinal(ui, 0);
  174                   System.arraycopy(ui, 0, ti, 0, ui.length);
  175   
  176                   for (int j = 2; j <= iterCount; j++) {
  177                       prf.update(ui);
  178                       prf.doFinal(ui, 0);
  179                       // XOR the intermediate Ui's together.
  180                       for (int k = 0; k < ui.length; k++) {
  181                           ti[k] ^= ui[k];
  182                       }
  183                   }
  184                   if (i == intL) {
  185                       System.arraycopy(ti, 0, key, (i-1)*hlen, intR);
  186                   } else {
  187                       System.arraycopy(ti, 0, key, (i-1)*hlen, hlen);
  188                   }
  189               }
  190           } catch (GeneralSecurityException gse) {
  191               throw new RuntimeException("Error deriving PBKDF2 keys");
  192           }
  193           return key;
  194       }
  195   
  196       public byte[] getEncoded() {
  197           return (byte[]) key.clone();
  198       }
  199   
  200       public String getAlgorithm() {
  201           return "PBKDF2With" + prf.getAlgorithm();
  202       }
  203   
  204       public int getIterationCount() {
  205           return iterCount;
  206       }
  207   
  208       public char[] getPassword() {
  209           return (char[]) passwd.clone();
  210       }
  211   
  212       public byte[] getSalt() {
  213           return salt.clone();
  214       }
  215   
  216       public String getFormat() {
  217           return "RAW";
  218       }
  219   
  220       /**
  221        * Calculates a hash code value for the object.
  222        * Objects that are equal will also have the same hashcode.
  223        */
  224       public int hashCode() {
  225           int retval = 0;
  226           for (int i = 1; i < this.key.length; i++) {
  227               retval += this.key[i] * i;
  228           }
  229           return(retval ^= getAlgorithm().toLowerCase().hashCode());
  230       }
  231   
  232       public boolean equals(Object obj) {
  233           if (obj == this)
  234               return true;
  235   
  236           if (!(obj instanceof SecretKey))
  237               return false;
  238   
  239           SecretKey that = (SecretKey) obj;
  240   
  241           if (!(that.getAlgorithm().equalsIgnoreCase(getAlgorithm())))
  242               return false;
  243           if (!(that.getFormat().equalsIgnoreCase("RAW")))
  244               return false;
  245           byte[] thatEncoded = that.getEncoded();
  246           boolean ret = Arrays.equals(key, that.getEncoded());
  247           java.util.Arrays.fill(thatEncoded, (byte)0x00);
  248           return ret;
  249       }
  250   
  251       /**
  252        * Replace the PBE key to be serialized.
  253        *
  254        * @return the standard KeyRep object to be serialized
  255        *
  256        * @throws ObjectStreamException if a new object representing
  257        * this PBE key could not be created
  258        */
  259       private Object writeReplace() throws ObjectStreamException {
  260               return new KeyRep(KeyRep.Type.SECRET, getAlgorithm(),
  261                                 getFormat(), getEncoded());
  262       }
  263   
  264       /**
  265        * Ensures that the password bytes of this key are
  266        * erased when there are no more references to it.
  267        */
  268       protected void finalize() throws Throwable {
  269           try {
  270               if (this.passwd != null) {
  271                   java.util.Arrays.fill(this.passwd, (char) '0');
  272                   this.passwd = null;
  273               }
  274               if (this.key != null) {
  275                   java.util.Arrays.fill(this.key, (byte)0x00);
  276                   this.key = null;
  277               }
  278           } finally {
  279               super.finalize();
  280           }
  281       }
  282   }

Save This Page
Home » openjdk-7 » com.sun.crypto » provider » [javadoc | source]