1 /*
   2  * Copyright (c) 1998, 2011, 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 javax.crypto.spec;
  27 
  28 import java.security.spec.KeySpec;
  29 import javax.crypto.SecretKey;
  30 
  31 /**
  32  * This class specifies a secret key in a provider-independent fashion.
  33  *
  34  * <p>It can be used to construct a <code>SecretKey</code> from a byte array,
  35  * without having to go through a (provider-based)
  36  * <code>SecretKeyFactory</code>.
  37  *
  38  * <p>This class is only useful for raw secret keys that can be represented as
  39  * a byte array and have no key parameters associated with them, e.g., DES or
  40  * Triple DES keys.
  41  *
  42  * @author Jan Luehe
  43  *
  44  * @see javax.crypto.SecretKey
  45  * @see javax.crypto.SecretKeyFactory
  46  * @since 1.4
  47  */
  48 public class SecretKeySpec implements KeySpec, SecretKey {
  49 
  50     private static final long serialVersionUID = 6577238317307289933L;
  51 
  52     /**
  53      * The secret key.
  54      *
  55      * @serial
  56      */
  57     private byte[] key;
  58 
  59     /**
  60      * The name of the algorithm associated with this key.
  61      *
  62      * @serial
  63      */
  64     private String algorithm;
  65 
  66     /**
  67      * Constructs a secret key from the given byte array.
  68      *
  69      * <p>This constructor does not check if the given bytes indeed specify a
  70      * secret key of the specified algorithm. For example, if the algorithm is
  71      * DES, this constructor does not check if <code>key</code> is 8 bytes
  72      * long, and also does not check for weak or semi-weak keys.
  73      * In order for those checks to be performed, an algorithm-specific
  74      * <i>key specification</i> class (in this case:
  75      * {@link DESKeySpec DESKeySpec})
  76      * should be used.
  77      *
  78      * @param key the key material of the secret key. The contents of
  79      * the array are copied to protect against subsequent modification.
  80      * @param algorithm the name of the secret-key algorithm to be associated
  81      * with the given key material.
  82      * See Appendix A in the <a href=
  83      *   "{@docRoot}/../technotes/guides/security/crypto/CryptoSpec.html#AppA">
  84      * Java Cryptography Architecture Reference Guide</a>
  85      * for information about standard algorithm names.
  86      * @exception IllegalArgumentException if <code>algorithm</code>
  87      * is null or <code>key</code> is null or empty.
  88      */
  89     public SecretKeySpec(byte[] key, String algorithm) {
  90         if (key == null || algorithm == null) {
  91             throw new IllegalArgumentException("Missing argument");
  92         }
  93         if (key.length == 0) {
  94             throw new IllegalArgumentException("Empty key");
  95         }
  96         this.key = key.clone();
  97         this.algorithm = algorithm;
  98     }
  99 
 100     /**
 101      * Constructs a secret key from the given byte array, using the first
 102      * <code>len</code> bytes of <code>key</code>, starting at
 103      * <code>offset</code> inclusive.
 104      *
 105      * <p> The bytes that constitute the secret key are
 106      * those between <code>key[offset]</code> and
 107      * <code>key[offset+len-1]</code> inclusive.
 108      *
 109      * <p>This constructor does not check if the given bytes indeed specify a
 110      * secret key of the specified algorithm. For example, if the algorithm is
 111      * DES, this constructor does not check if <code>key</code> is 8 bytes
 112      * long, and also does not check for weak or semi-weak keys.
 113      * In order for those checks to be performed, an algorithm-specific key
 114      * specification class (in this case:
 115      * {@link DESKeySpec DESKeySpec})
 116      * must be used.
 117      *
 118      * @param key the key material of the secret key. The first
 119      * <code>len</code> bytes of the array beginning at
 120      * <code>offset</code> inclusive are copied to protect
 121      * against subsequent modification.
 122      * @param offset the offset in <code>key</code> where the key material
 123      * starts.
 124      * @param len the length of the key material.
 125      * @param algorithm the name of the secret-key algorithm to be associated
 126      * with the given key material.
 127      * See Appendix A in the <a href=
 128      *   "{@docRoot}/../technotes/guides/security/crypto/CryptoSpec.html#AppA">
 129      * Java Cryptography Architecture Reference Guide</a>
 130      * for information about standard algorithm names.
 131      * @exception IllegalArgumentException if <code>algorithm</code>
 132      * is null or <code>key</code> is null, empty, or too short,
 133      * i.e. {@code key.length-offset<len}.
 134      * @exception ArrayIndexOutOfBoundsException is thrown if
 135      * <code>offset</code> or <code>len</code> index bytes outside the
 136      * <code>key</code>.
 137      */
 138     public SecretKeySpec(byte[] key, int offset, int len, String algorithm) {
 139         if (key == null || algorithm == null) {
 140             throw new IllegalArgumentException("Missing argument");
 141         }
 142         if (key.length == 0) {
 143             throw new IllegalArgumentException("Empty key");
 144         }
 145         if (key.length-offset < len) {
 146             throw new IllegalArgumentException
 147                 ("Invalid offset/length combination");
 148         }
 149         if (len < 0) {
 150             throw new ArrayIndexOutOfBoundsException("len is negative");
 151         }
 152         this.key = new byte[len];
 153         System.arraycopy(key, offset, this.key, 0, len);
 154         this.algorithm = algorithm;
 155     }
 156 
 157     /**
 158      * Returns the name of the algorithm associated with this secret key.
 159      *
 160      * @return the secret key algorithm.
 161      */
 162     public String getAlgorithm() {
 163         return this.algorithm;
 164     }
 165 
 166     /**
 167      * Returns the name of the encoding format for this secret key.
 168      *
 169      * @return the string "RAW".
 170      */
 171     public String getFormat() {
 172         return "RAW";
 173     }
 174 
 175     /**
 176      * Returns the key material of this secret key.
 177      *
 178      * @return the key material. Returns a new array
 179      * each time this method is called.
 180      */
 181     public byte[] getEncoded() {
 182         return this.key.clone();
 183     }
 184 
 185     /**
 186      * Calculates a hash code value for the object.
 187      * Objects that are equal will also have the same hashcode.
 188      */
 189     public int hashCode() {
 190         int retval = 0;
 191         for (int i = 1; i < this.key.length; i++) {
 192             retval += this.key[i] * i;
 193         }
 194         if (this.algorithm.equalsIgnoreCase("TripleDES"))
 195             return (retval ^= "desede".hashCode());
 196         else
 197             return (retval ^= this.algorithm.toLowerCase().hashCode());
 198     }
 199 
 200    /**
 201      * Tests for equality between the specified object and this
 202      * object. Two SecretKeySpec objects are considered equal if
 203      * they are both SecretKey instances which have the
 204      * same case-insensitive algorithm name and key encoding.
 205      *
 206      * @param obj the object to test for equality with this object.
 207      *
 208      * @return true if the objects are considered equal, false if
 209      * <code>obj</code> is null or otherwise.
 210      */
 211     public boolean equals(Object obj) {
 212         if (this == obj)
 213             return true;
 214 
 215         if (!(obj instanceof SecretKey))
 216             return false;
 217 
 218         String thatAlg = ((SecretKey)obj).getAlgorithm();
 219         if (!(thatAlg.equalsIgnoreCase(this.algorithm))) {
 220             if ((!(thatAlg.equalsIgnoreCase("DESede"))
 221                  || !(this.algorithm.equalsIgnoreCase("TripleDES")))
 222                 && (!(thatAlg.equalsIgnoreCase("TripleDES"))
 223                     || !(this.algorithm.equalsIgnoreCase("DESede"))))
 224             return false;
 225         }
 226 
 227         byte[] thatKey = ((SecretKey)obj).getEncoded();
 228 
 229         return java.util.Arrays.equals(this.key, thatKey);
 230     }
 231 }