KeyImpl.java 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320
  1. /*
  2. * Licensed to the Apache Software Foundation (ASF) under one or more
  3. * contributor license agreements. See the NOTICE file distributed with
  4. * this work for additional information regarding copyright ownership.
  5. * The ASF licenses this file to You under the Apache License, Version 2.0
  6. * (the "License"); you may not use this file except in compliance with
  7. * the License. You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. */
  17. package javax.security.auth.kerberos;
  18. import java.io.IOException;
  19. import java.io.ObjectInputStream;
  20. import java.io.ObjectOutputStream;
  21. import java.io.Serializable;
  22. import java.util.Arrays;
  23. import javax.crypto.Cipher;
  24. import javax.crypto.SecretKey;
  25. import javax.crypto.spec.IvParameterSpec;
  26. import javax.crypto.spec.SecretKeySpec;
  27. import javax.security.auth.DestroyFailedException;
  28. import javax.security.auth.Destroyable;
  29. import org.apache.harmony.auth.internal.kerberos.v5.EncryptionKey;
  30. import org.apache.harmony.auth.internal.nls.Messages;
  31. import org.apache.harmony.security.utils.Array;
  32. /**
  33. * This class encapsulates a Kerberos encryption key.
  34. *
  35. */
  36. class KeyImpl implements SecretKey, Destroyable, Serializable {
  37. private static final long serialVersionUID = -7889313790214321193L;
  38. private transient byte[] keyBytes;
  39. private transient int keyType;
  40. // indicates the ticket state
  41. private transient boolean destroyed;
  42. // Pre-calculated parity values
  43. // TODO the alternative for boolean table - any acceptable algorithm?
  44. private final static boolean[] PARITY = new boolean[] { false, true, true,
  45. false, true, false, false, true, true, false, false, true, false,
  46. true, true, false, true, false, false, true, false, true, true,
  47. false, false, true, true, false, true, false, false, true, true,
  48. false, false, true, false, true, true, false, false, true, true,
  49. false, true, false, false, true, false, true, true, false, true,
  50. false, false, true, true, false, false, true, false, true, true,
  51. false, true, false, false, true, false, true, true, false, false,
  52. true, true, false, true, false, false, true, false, true, true,
  53. false, true, false, false, true, true, false, false, true, false,
  54. true, true, false, false, true, true, false, true, false, false,
  55. true, true, false, false, true, false, true, true, false, true,
  56. false, false, true, false, true, true, false, false, true, true,
  57. false, true, false, false, true, true, false, false, true, false,
  58. true, true, false, false, true, true, false, true, false, false,
  59. true, false, true, true, false, true, false, false, true, true,
  60. false, false, true, false, true, true, false, false, true, true,
  61. false, true, false, false, true, true, false, false, true, false,
  62. true, true, false, true, false, false, true, false, true, true,
  63. false, false, true, true, false, true, false, false, true, false,
  64. true, true, false, true, false, false, true, true, false, false,
  65. true, false, true, true, false, true, false, false, true, false,
  66. true, true, false, false, true, true, false, true, false, false,
  67. true, true, false, false, true, false, true, true, false, false,
  68. true, true, false, true, false, false, true, false, true, true,
  69. false, true, false, false, true, true, false, false, true, false,
  70. true, true, false };
  71. // Pre-calculated reversed values
  72. // TODO any acceptable alternative algorithm instead of table?
  73. private static final byte[] REVERSE = new byte[] { 0, 64, 32, 96, 16, 80,
  74. 48, 112, 8, 72, 40, 104, 24, 88, 56, 120, 4, 68, 36, 100, 20, 84,
  75. 52, 116, 12, 76, 44, 108, 28, 92, 60, 124, 2, 66, 34, 98, 18, 82,
  76. 50, 114, 10, 74, 42, 106, 26, 90, 58, 122, 6, 70, 38, 102, 22, 86,
  77. 54, 118, 14, 78, 46, 110, 30, 94, 62, 126, 1, 65, 33, 97, 17, 81,
  78. 49, 113, 9, 73, 41, 105, 25, 89, 57, 121, 5, 69, 37, 101, 21, 85,
  79. 53, 117, 13, 77, 45, 109, 29, 93, 61, 125, 3, 67, 35, 99, 19, 83,
  80. 51, 115, 11, 75, 43, 107, 27, 91, 59, 123, 7, 71, 39, 103, 23, 87,
  81. 55, 119, 15, 79, 47, 111, 31, 95, 63, 127 };
  82. /**
  83. * creates a secret key from a given raw bytes
  84. *
  85. * @param keyBytes
  86. * @param keyType
  87. */
  88. public KeyImpl(byte[] keyBytes, int keyType) {
  89. this.keyBytes = new byte[keyBytes.length];
  90. System.arraycopy(keyBytes , 0, this.keyBytes, 0, this.keyBytes.length);
  91. this.keyType = keyType;
  92. }
  93. /**
  94. * creates a secret key from a given password
  95. *
  96. * @param principal
  97. * @param password
  98. * @param algorithm
  99. */
  100. public KeyImpl(KerberosPrincipal principal, char[] password, String algorithm) {
  101. //
  102. // See http://www.ietf.org/rfc/rfc3961.txt for algorithm description
  103. //
  104. if (principal == null || password == null) {
  105. throw new NullPointerException();
  106. }
  107. if (algorithm != null && "DES".compareTo(algorithm) != 0) { //$NON-NLS-1$
  108. throw new IllegalArgumentException(Messages.getString("auth.49")); //$NON-NLS-1$
  109. }
  110. keyType = 3; // DES algorithm
  111. keyBytes = new byte[8];
  112. String realm = principal.getRealm();
  113. String pname = principal.getName();
  114. StringBuilder buf = new StringBuilder();
  115. buf.append(password);
  116. buf.append(realm);
  117. buf.append(pname.substring(0, pname.length() - realm.length() - 1));
  118. byte[] tmp = org.apache.harmony.luni.util.Util.getUTF8Bytes(buf
  119. .toString());
  120. // pad with 0x00 to 8 byte boundary
  121. byte[] raw = new byte[tmp.length
  122. + ((tmp.length % 8) == 0 ? 0 : (8 - tmp.length % 8))];
  123. System.arraycopy(tmp, 0, raw, 0, tmp.length);
  124. long k1, k2 = 0;
  125. boolean isOdd = false;
  126. // for each 8-byte block in raw byte array
  127. for (int i = 0; i < raw.length; i = i + 8, isOdd = !isOdd) {
  128. k1 = 0;
  129. if (isOdd) {
  130. //reverse
  131. for (int j = 7; j > -1; j--) {
  132. k1 = (k1 << 7) + REVERSE[raw[i + j] & 0x7F];
  133. }
  134. } else {
  135. for (int j = 0; j < 8; j++) {
  136. k1 = (k1 << 7) + (raw[i + j] & 0x7F);
  137. }
  138. }
  139. k2 = k2 ^ k1;
  140. }
  141. // 56-bit long to byte array (8 bytes)
  142. for (int i = 7; i > -1; i--) {
  143. keyBytes[i] = (byte) k2;
  144. keyBytes[i] = (byte) (keyBytes[i] << 1);
  145. k2 = k2 >> 7;
  146. }
  147. keyCorrection(keyBytes);
  148. // calculate DES-CBC check sum
  149. try {
  150. Cipher cipher = Cipher.getInstance("DES/CBC/NoPadding"); //$NON-NLS-1$
  151. // use tmp key as IV
  152. IvParameterSpec IV = new IvParameterSpec(keyBytes);
  153. // do DES encryption
  154. SecretKey secretKey = new SecretKeySpec(keyBytes, "DES"); //$NON-NLS-1$
  155. cipher.init(Cipher.ENCRYPT_MODE, secretKey, IV);
  156. byte[] enc = cipher.doFinal(raw);
  157. // final last block is check sum
  158. System.arraycopy(enc, enc.length - 8, keyBytes, 0, 8);
  159. keyCorrection(keyBytes);
  160. } catch (Exception e) {
  161. throw new RuntimeException(
  162. Messages.getString("auth.4A"), e); //$NON-NLS-1$
  163. }
  164. }
  165. private void keyCorrection(byte[] key) {
  166. // fix parity
  167. for (int i = 0; i < 8; i++) {
  168. if (!PARITY[key[i] & 0xFF]) {
  169. if ((key[i] & 0x01) == 0) {
  170. key[i]++;
  171. } else {
  172. key[i]--;
  173. }
  174. }
  175. }
  176. // TODO if is week do XOR
  177. //if(DESKeySpec.isWeak(keyBytes,0)){
  178. //}
  179. }
  180. /**
  181. * Method is described in
  182. * <code>getAlgorithm</code> in interface <code>Key</code>
  183. */
  184. public final String getAlgorithm() {
  185. checkState();
  186. if (keyType == 0) {
  187. return "NULL"; //$NON-NLS-1$
  188. }
  189. return "DES"; //$NON-NLS-1$
  190. }
  191. /**
  192. * Method is described in
  193. * <code>getFormat</code> in interface <code>Key</code>
  194. */
  195. public final String getFormat() {
  196. checkState();
  197. return "RAW"; //$NON-NLS-1$
  198. }
  199. /**
  200. * Method is described in
  201. * <code>getEncoded</code> in interface <code>Key</code>
  202. */
  203. public final byte[] getEncoded() {
  204. checkState();
  205. byte[] tmp = new byte[keyBytes.length];
  206. System.arraycopy(keyBytes, 0, tmp, 0, tmp.length);
  207. return tmp;
  208. }
  209. /**
  210. * Returns the key type for this key
  211. */
  212. public final int getKeyType() {
  213. checkState();
  214. return keyType;
  215. }
  216. /**
  217. * Destroys this key
  218. */
  219. public void destroy() throws DestroyFailedException {
  220. if (!destroyed) {
  221. Arrays.fill(keyBytes, (byte) 0);
  222. destroyed = true;
  223. }
  224. }
  225. /**
  226. * Determines if this key has been destroyed
  227. */
  228. public boolean isDestroyed() {
  229. return destroyed;
  230. }
  231. /**
  232. * A string representation of this key
  233. */
  234. @Override
  235. public String toString() {
  236. String s_key = null;
  237. StringBuilder sb = new StringBuilder();
  238. if (keyBytes.length == 0) {
  239. s_key = "Empty Key"; //$NON-NLS-1$
  240. } else {
  241. s_key = Array.toString(keyBytes," "); //$NON-NLS-1$
  242. }
  243. sb.append("EncryptionKey: ").append("KeyType = ").append(keyType); //$NON-NLS-1$ //$NON-NLS-2$
  244. sb.append("KeyBytes (Hex dump) = ").append(s_key); //$NON-NLS-1$
  245. return sb.toString();
  246. }
  247. /**
  248. * if a key is destroyed then IllegalStateException should be thrown
  249. */
  250. private void checkState() {
  251. if (destroyed) {
  252. throw new IllegalStateException (Messages.getString("auth.48")); //$NON-NLS-1$
  253. }
  254. }
  255. private void readObject(ObjectInputStream s) throws IOException,
  256. ClassNotFoundException {
  257. s.defaultReadObject();
  258. EncryptionKey ekey = (EncryptionKey) EncryptionKey.ASN1
  259. .decode((byte[]) s.readObject());
  260. keyType = ekey.getType();
  261. keyBytes = ekey.getValue();
  262. }
  263. private void writeObject(ObjectOutputStream s) throws IOException {
  264. if (destroyed) {
  265. throw new IOException(Messages.getString("auth.48")); //$NON-NLS-1$
  266. }
  267. s.defaultWriteObject();
  268. byte[] enc = EncryptionKey.ASN1.encode(new EncryptionKey(keyType,
  269. keyBytes));
  270. s.writeObject(enc);
  271. }
  272. }